2 This file is part of GNUnet
3 (C) 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file util/scheduler.c
23 * @brief schedule computations using continuation passing style
24 * @author Christian Grothoff
27 #include "gnunet_common.h"
28 #include "gnunet_scheduler_lib.h"
29 #include "gnunet_signal_lib.h"
30 #include "gnunet_time_lib.h"
32 #define DEBUG_TASKS GNUNET_NO
35 * Linked list of pending tasks.
40 * This is a linked list.
45 * Function to run when ready.
47 GNUNET_SCHEDULER_Task callback;
50 * Closure for the callback.
55 * Set of file descriptors this task is waiting
56 * for for reading. Once ready, this is updated
57 * to reflect the set of file descriptors ready
60 struct GNUNET_NETWORK_FDSet *read_set;
63 * Set of file descriptors this task is waiting
64 * for for writing. Once ready, this is updated
65 * to reflect the set of file descriptors ready
68 struct GNUNET_NETWORK_FDSet *write_set;
71 * Unique task identifier.
73 GNUNET_SCHEDULER_TaskIdentifier id;
76 * Identifier of a prerequisite task.
78 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
81 * Absolute timeout value for the task, or
82 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
84 struct GNUNET_TIME_Absolute timeout;
87 * Why is the task ready? Set after task is added to ready queue.
88 * Initially set to zero. All reasons that have already been
89 * satisfied (i.e. read or write ready) will be set over time.
91 enum GNUNET_SCHEDULER_Reason reason;
96 enum GNUNET_SCHEDULER_Priority priority;
102 * Handle for the scheduling service.
104 struct GNUNET_SCHEDULER_Handle
108 * List of tasks waiting for an event.
110 struct Task *pending;
113 * List of tasks ready to run right now,
114 * grouped by importance.
116 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
119 * Identity of the last task queued. Incremented for each task to
120 * generate a unique task ID (it is virtually impossible to start
121 * more than 2^64 tasks during the lifetime of a process).
123 GNUNET_SCHEDULER_TaskIdentifier last_id;
126 * Highest number so that all tasks with smaller identifiers
127 * have already completed. Also the lowest number of a task
128 * still waiting to be executed.
130 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
133 * Number of tasks on the ready list.
135 unsigned int ready_count;
138 * How many tasks have we run so far?
140 unsigned long long tasks_run;
143 * Priority of the task running right now. Only
144 * valid while a task is running.
146 enum GNUNET_SCHEDULER_Priority current_priority;
152 * Check that the given priority is legal (and return it).
154 * @param p priority value to check
155 * @return p on success, 0 on error
157 static enum GNUNET_SCHEDULER_Priority
158 check_priority (enum GNUNET_SCHEDULER_Priority p)
160 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
163 return 0; /* make compiler happy */
168 * Is a task with this identifier still pending? Also updates
169 * "lowest_pending_id" as a side-effect (for faster checks in the
170 * future), but only if the return value is "GNUNET_NO" (and
171 * the "lowest_pending_id" check failed).
173 * @param sched the scheduler
174 * @param id which task are we checking for
175 * @return GNUNET_YES if so, GNUNET_NO if not
178 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
179 GNUNET_SCHEDULER_TaskIdentifier id)
182 enum GNUNET_SCHEDULER_Priority p;
183 GNUNET_SCHEDULER_TaskIdentifier min;
185 if (id < sched->lowest_pending_id)
187 min = -1; /* maximum value */
188 pos = sched->pending;
197 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
199 pos = sched->ready[p];
209 sched->lowest_pending_id = min;
215 * Update all sets and timeout for select.
217 * @param sched the scheduler
218 * @param rs read-set, set to all FDs we would like to read (updated)
219 * @param ws write-set, set to all FDs we would like to write (updated)
220 * @param timeout next timeout (updated)
223 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
224 struct GNUNET_NETWORK_FDSet *rs,
225 struct GNUNET_NETWORK_FDSet *ws,
226 struct GNUNET_TIME_Relative *timeout)
230 pos = sched->pending;
233 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
234 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
240 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
242 struct GNUNET_TIME_Relative to;
244 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
245 if (timeout->value > to.value)
248 if (pos->read_set != NULL)
249 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
250 if (pos->write_set != NULL)
251 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
252 if (pos->reason != 0)
253 *timeout = GNUNET_TIME_UNIT_ZERO;
260 * Check if the ready set overlaps with the set we want to have ready.
261 * If so, update the want set (set all FDs that are ready). If not,
264 * @param ready set that is ready
265 * @param want set that we want to be ready
266 * @return GNUNET_YES if there was some overlap
269 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
270 struct GNUNET_NETWORK_FDSet *want)
274 if (GNUNET_NETWORK_fdset_overlap (ready, want))
276 /* copy all over (yes, there maybe unrelated bits,
277 but this should not hurt well-written clients) */
278 GNUNET_NETWORK_fdset_copy (want, ready);
286 * Check if the given task is eligible to run now.
287 * Also set the reason why it is eligible.
289 * @param sched the scheduler
290 * @param task task to check if it is ready
291 * @param now the current time
292 * @param rs set of FDs ready for reading
293 * @param ws set of FDs ready for writing
294 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
297 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
299 struct GNUNET_TIME_Absolute now,
300 const struct GNUNET_NETWORK_FDSet *rs,
301 const struct GNUNET_NETWORK_FDSet *ws)
303 if (now.value >= task->timeout.value)
304 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
305 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
306 (rs != NULL) && (set_overlaps (rs, task->read_set)))
307 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
308 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
309 (ws != NULL) && (set_overlaps (ws, task->write_set)))
310 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
311 if (task->reason == 0)
312 return GNUNET_NO; /* not ready */
313 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
315 if (GNUNET_YES == is_pending (sched, task->prereq_id))
316 return GNUNET_NO; /* prereq waiting */
317 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
324 * Put a task that is ready for execution into the ready queue.
326 * @param handle the scheduler
327 * @param task task ready for execution
330 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
332 task->next = handle->ready[check_priority (task->priority)];
333 handle->ready[check_priority (task->priority)] = task;
334 handle->ready_count++;
339 * Check which tasks are ready and move them
340 * to the respective ready queue.
342 * @param handle the scheduler
343 * @param rs FDs ready for reading
344 * @param ws FDs ready for writing
347 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
348 const struct GNUNET_NETWORK_FDSet *rs,
349 const struct GNUNET_NETWORK_FDSet *ws)
354 struct GNUNET_TIME_Absolute now;
356 now = GNUNET_TIME_absolute_get ();
358 pos = handle->pending;
362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363 "Checking readyness of task: %llu / %p\n",
364 pos->id, pos->callback_cls);
367 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
370 handle->pending = next;
373 queue_ready_task (handle, pos);
384 * Request the shutdown of a scheduler. Marks all currently
385 * pending tasks as ready because of shutdown. This will
386 * cause all tasks to run (as soon as possible, respecting
387 * priorities and prerequisite tasks). Note that tasks
388 * scheduled AFTER this call may still be delayed arbitrarily.
390 * @param sched the scheduler
393 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
397 pos = sched->pending;
400 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
401 /* we don't move the task into the ready queue yet; check_ready
402 will do that later, possibly adding additional
410 * Destroy a task (release associated resources)
412 * @param t task to destroy
415 destroy_task (struct Task *t)
417 if (NULL != t->read_set)
418 GNUNET_NETWORK_fdset_destroy (t->read_set);
419 if (NULL != t->write_set)
420 GNUNET_NETWORK_fdset_destroy (t->write_set);
426 * Run at least one task in the highest-priority queue that is not
427 * empty. Keep running tasks until we are either no longer running
428 * "URGENT" tasks or until we have at least one "pending" task (which
429 * may become ready, hence we should select on it). Naturally, if
430 * there are no more ready tasks, we also return.
432 * @param sched the scheduler
435 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
437 enum GNUNET_SCHEDULER_Priority p;
439 struct GNUNET_SCHEDULER_TaskContext tc;
443 if (sched->ready_count == 0)
445 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
446 /* yes, p>0 is correct, 0 is "KEEP" which should
447 always be an empty queue (see assertion)! */
448 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
450 pos = sched->ready[p];
454 GNUNET_assert (pos != NULL); /* ready_count wrong? */
455 sched->ready[p] = pos->next;
456 sched->ready_count--;
457 sched->current_priority = p;
458 GNUNET_assert (pos->priority == p);
460 tc.reason = pos->reason;
461 tc.read_ready = pos->read_set;
462 tc.write_ready = pos->write_set;
463 pos->callback (pos->callback_cls, &tc);
465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466 "Running task: %llu / %p\n", pos->id, pos->callback_cls);
471 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
476 * Pipe used to communicate shutdown via signal.
478 static struct GNUNET_DISK_PipeHandle *sigpipe;
482 * Signal handler called for signals that should cause us to shutdown.
485 sighandler_shutdown ()
489 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
490 (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
497 * Initialize and run scheduler. This function will return when all
498 * tasks have completed. On systems with signals, receiving a SIGTERM
499 * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
500 * to be run after the active task is complete. As a result, SIGTERM
501 * causes all active tasks to be scheduled with reason
502 * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
503 * afterwards will execute normally!). Note that any particular signal
504 * will only shut down one scheduler; applications should always only
505 * create a single scheduler.
507 * @param task task to run immediately
508 * @param task_cls closure of task
511 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
513 struct GNUNET_SCHEDULER_Handle sched;
514 struct GNUNET_NETWORK_FDSet *rs;
515 struct GNUNET_NETWORK_FDSet *ws;
516 struct GNUNET_TIME_Relative timeout;
518 struct GNUNET_SIGNAL_Context *shc_int;
519 struct GNUNET_SIGNAL_Context *shc_term;
520 struct GNUNET_SIGNAL_Context *shc_quit;
521 struct GNUNET_SIGNAL_Context *shc_hup;
522 unsigned long long last_tr;
523 unsigned int busy_wait_warning;
524 const struct GNUNET_DISK_FileHandle *pr;
527 rs = GNUNET_NETWORK_fdset_create ();
528 ws = GNUNET_NETWORK_fdset_create ();
530 GNUNET_assert (sigpipe == NULL);
531 sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
532 GNUNET_assert (sigpipe != NULL);
533 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
534 GNUNET_assert (pr != NULL);
535 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
536 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
537 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
538 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
540 memset (&sched, 0, sizeof (sched));
541 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
542 GNUNET_SCHEDULER_add_continuation (&sched,
545 GNUNET_SCHEDULER_REASON_STARTUP);
547 busy_wait_warning = 0;
548 while ((sched.pending != NULL) || (sched.ready_count > 0))
550 GNUNET_NETWORK_fdset_zero (rs);
551 GNUNET_NETWORK_fdset_zero (ws);
552 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
553 update_sets (&sched, rs, ws, &timeout);
554 GNUNET_NETWORK_fdset_handle_set (rs, pr);
555 if (sched.ready_count > 0)
557 /* no blocking, more work already ready! */
558 timeout = GNUNET_TIME_UNIT_ZERO;
560 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
561 if (ret == GNUNET_SYSERR)
565 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
569 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
571 /* consume the signal */
572 GNUNET_DISK_file_read (pr, &c, sizeof (c));
573 /* mark all active tasks as ready due to shutdown */
574 GNUNET_SCHEDULER_shutdown (&sched);
577 if (last_tr == sched.tasks_run)
583 last_tr = sched.tasks_run;
584 busy_wait_warning = 0;
586 if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
588 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
589 _("Looks like we're busy waiting...\n"));
590 sleep (1); /* mitigate */
592 check_ready (&sched, rs, ws);
596 GNUNET_SIGNAL_handler_uninstall (shc_int);
597 GNUNET_SIGNAL_handler_uninstall (shc_term);
598 GNUNET_SIGNAL_handler_uninstall (shc_quit);
599 GNUNET_SIGNAL_handler_uninstall (shc_hup);
600 GNUNET_DISK_pipe_close (sigpipe);
603 GNUNET_NETWORK_fdset_destroy (rs);
604 GNUNET_NETWORK_fdset_destroy (ws);
610 * Get information about the current load of this scheduler. Use this
611 * function to determine if an elective task should be added or simply
612 * dropped (if the decision should be made based on the number of
613 * tasks ready to run).
615 * @param sched scheduler to query
616 * @param p priority level to look at
617 * @return number of tasks pending right now
620 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
621 enum GNUNET_SCHEDULER_Priority p)
626 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
627 return sched->ready_count;
628 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
629 p = sched->current_priority;
631 pos = sched->ready[p];
642 * Cancel the task with the specified identifier.
643 * The task must not yet have run.
645 * @param sched scheduler to use
646 * @param task id of the task to cancel
647 * @return original closure of the task
650 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
651 GNUNET_SCHEDULER_TaskIdentifier task)
655 enum GNUNET_SCHEDULER_Priority p;
671 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
678 sched->ready_count--;
688 sched->pending = t->next;
690 sched->ready[p] = t->next;
693 prev->next = t->next;
694 ret = t->callback_cls;
696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697 "Canceling task: %llu / %p\n", task, t->callback_cls);
705 * Continue the current execution with the given function. This is
706 * similar to the other "add" functions except that there is no delay
707 * and the reason code can be specified.
709 * @param sched scheduler to use
710 * @param task main function of the task
711 * @param task_cls closure for 'main'
712 * @param reason reason for task invocation
715 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
716 GNUNET_SCHEDULER_Task task,
718 enum GNUNET_SCHEDULER_Reason reason)
722 t = GNUNET_malloc (sizeof (struct Task));
724 t->callback_cls = task_cls;
725 t->id = ++sched->last_id;
727 t->priority = sched->current_priority;
729 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730 "Adding continuation task: %llu / %p\n",
731 t->id, t->callback_cls);
733 queue_ready_task (sched, t);
739 * Schedule a new task to be run after the specified prerequisite task
740 * has completed. It will be run with the priority of the calling
743 * @param sched scheduler to use
744 * @param prerequisite_task run this task after the task with the given
745 * task identifier completes (and any of our other
746 * conditions, such as delay, read or write-readyness
747 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
748 * on completion of other tasks (this will cause the task to run as
750 * @param task main function of the task
751 * @param task_cls closure of task
752 * @return unique task identifier for the job
753 * only valid until "task" is started!
755 GNUNET_SCHEDULER_TaskIdentifier
756 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
757 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
758 GNUNET_SCHEDULER_Task task, void *task_cls)
760 return GNUNET_SCHEDULER_add_select (sched,
761 GNUNET_SCHEDULER_PRIORITY_KEEP,
763 GNUNET_TIME_UNIT_ZERO,
764 NULL, NULL, task, task_cls);
769 * Schedule a new task to be run with a specified priority.
771 * @param sched scheduler to use
772 * @param prio how important is the new task?
773 * @param task main function of the task
774 * @param task_cls closure of task
775 * @return unique task identifier for the job
776 * only valid until "task" is started!
778 GNUNET_SCHEDULER_TaskIdentifier
779 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
780 enum GNUNET_SCHEDULER_Priority prio,
781 GNUNET_SCHEDULER_Task task,
784 return GNUNET_SCHEDULER_add_select (sched,
786 GNUNET_SCHEDULER_NO_TASK,
787 GNUNET_TIME_UNIT_ZERO,
788 NULL, NULL, task, task_cls);
794 * Schedule a new task to be run with a specified delay. The task
795 * will be scheduled for execution once the delay has expired. It
796 * will be run with the priority of the calling task.
798 * @param sched scheduler to use
799 * @param delay when should this operation time out? Use
800 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
801 * @param task main function of the task
802 * @param task_cls closure of task
803 * @return unique task identifier for the job
804 * only valid until "task" is started!
806 GNUNET_SCHEDULER_TaskIdentifier
807 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
808 struct GNUNET_TIME_Relative delay,
809 GNUNET_SCHEDULER_Task task, void *task_cls)
811 return GNUNET_SCHEDULER_add_select (sched,
812 GNUNET_SCHEDULER_PRIORITY_KEEP,
813 GNUNET_SCHEDULER_NO_TASK, delay,
814 NULL, NULL, task, task_cls);
819 * Schedule a new task to be run with a specified delay or when the
820 * specified file descriptor is ready for reading. The delay can be
821 * used as a timeout on the socket being ready. The task will be
822 * scheduled for execution once either the delay has expired or the
823 * socket operation is ready. It will be run with the priority of
826 * @param sched scheduler to use
827 * @param delay when should this operation time out? Use
828 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
829 * @param rfd read file-descriptor
830 * @param task main function of the task
831 * @param task_cls closure of task
832 * @return unique task identifier for the job
833 * only valid until "task" is started!
835 GNUNET_SCHEDULER_TaskIdentifier
836 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
837 struct GNUNET_TIME_Relative delay,
838 struct GNUNET_NETWORK_Handle * rfd,
839 GNUNET_SCHEDULER_Task task, void *task_cls)
841 struct GNUNET_NETWORK_FDSet *rs;
842 GNUNET_SCHEDULER_TaskIdentifier ret;
844 GNUNET_assert (rfd != NULL);
845 rs = GNUNET_NETWORK_fdset_create ();
846 GNUNET_NETWORK_fdset_set (rs, rfd);
847 ret = GNUNET_SCHEDULER_add_select (sched,
848 GNUNET_SCHEDULER_PRIORITY_KEEP,
849 GNUNET_SCHEDULER_NO_TASK,
850 delay, rs, NULL, task, task_cls);
851 GNUNET_NETWORK_fdset_destroy (rs);
857 * Schedule a new task to be run with a specified delay or when the
858 * specified file descriptor is ready for writing. The delay can be
859 * used as a timeout on the socket being ready. The task will be
860 * scheduled for execution once either the delay has expired or the
861 * socket operation is ready. It will be run with the priority of
864 * @param sched scheduler to use
865 * @param delay when should this operation time out? Use
866 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
867 * @param wfd write file-descriptor
868 * @param task main function of the task
869 * @param task_cls closure of task
870 * @return unique task identifier for the job
871 * only valid until "task" is started!
873 GNUNET_SCHEDULER_TaskIdentifier
874 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
875 struct GNUNET_TIME_Relative delay,
876 struct GNUNET_NETWORK_Handle * wfd,
877 GNUNET_SCHEDULER_Task task, void *task_cls)
879 struct GNUNET_NETWORK_FDSet *ws;
880 GNUNET_SCHEDULER_TaskIdentifier ret;
882 GNUNET_assert (wfd != NULL);
883 ws = GNUNET_NETWORK_fdset_create ();
884 GNUNET_NETWORK_fdset_set (ws, wfd);
885 ret = GNUNET_SCHEDULER_add_select (sched,
886 GNUNET_SCHEDULER_PRIORITY_KEEP,
887 GNUNET_SCHEDULER_NO_TASK, delay,
888 NULL, ws, task, task_cls);
889 GNUNET_NETWORK_fdset_destroy (ws);
895 * Schedule a new task to be run with a specified delay or when the
896 * specified file descriptor is ready for reading. The delay can be
897 * used as a timeout on the socket being ready. The task will be
898 * scheduled for execution once either the delay has expired or the
899 * socket operation is ready. It will be run with the priority of
902 * @param sched scheduler to use
903 * @param delay when should this operation time out? Use
904 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
905 * @param rfd read file-descriptor
906 * @param task main function of the task
907 * @param task_cls closure of task
908 * @return unique task identifier for the job
909 * only valid until "task" is started!
911 GNUNET_SCHEDULER_TaskIdentifier
912 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
913 struct GNUNET_TIME_Relative delay,
914 const struct GNUNET_DISK_FileHandle * rfd,
915 GNUNET_SCHEDULER_Task task, void *task_cls)
917 struct GNUNET_NETWORK_FDSet *rs;
918 GNUNET_SCHEDULER_TaskIdentifier ret;
920 GNUNET_assert (rfd != NULL);
921 rs = GNUNET_NETWORK_fdset_create ();
922 GNUNET_NETWORK_fdset_handle_set (rs, rfd);
923 ret = GNUNET_SCHEDULER_add_select (sched,
924 GNUNET_SCHEDULER_PRIORITY_KEEP,
925 GNUNET_SCHEDULER_NO_TASK, delay,
926 rs, NULL, task, task_cls);
927 GNUNET_NETWORK_fdset_destroy (rs);
933 * Schedule a new task to be run with a specified delay or when the
934 * specified file descriptor is ready for writing. The delay can be
935 * used as a timeout on the socket being ready. The task will be
936 * scheduled for execution once either the delay has expired or the
937 * socket operation is ready. It will be run with the priority of
940 * @param sched scheduler to use
941 * @param delay when should this operation time out? Use
942 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
943 * @param wfd write file-descriptor
944 * @param task main function of the task
945 * @param task_cls closure of task
946 * @return unique task identifier for the job
947 * only valid until "task" is started!
949 GNUNET_SCHEDULER_TaskIdentifier
950 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
951 struct GNUNET_TIME_Relative delay,
952 const struct GNUNET_DISK_FileHandle * wfd,
953 GNUNET_SCHEDULER_Task task, void *task_cls)
955 struct GNUNET_NETWORK_FDSet *ws;
956 GNUNET_SCHEDULER_TaskIdentifier ret;
958 GNUNET_assert (wfd != NULL);
959 ws = GNUNET_NETWORK_fdset_create ();
960 GNUNET_NETWORK_fdset_handle_set (ws, wfd);
961 ret = GNUNET_SCHEDULER_add_select (sched,
962 GNUNET_SCHEDULER_PRIORITY_KEEP,
963 GNUNET_SCHEDULER_NO_TASK,
964 delay, NULL, ws, task, task_cls);
965 GNUNET_NETWORK_fdset_destroy (ws);
972 * Schedule a new task to be run with a specified delay or when any of
973 * the specified file descriptor sets is ready. The delay can be used
974 * as a timeout on the socket(s) being ready. The task will be
975 * scheduled for execution once either the delay has expired or any of
976 * the socket operations is ready. This is the most general
977 * function of the "add" family. Note that the "prerequisite_task"
978 * must be satisfied in addition to any of the other conditions. In
979 * other words, the task will be started when
985 * || (shutdown-active && run-on-shutdown) )
988 * @param sched scheduler to use
989 * @param prio how important is this task?
990 * @param prerequisite_task run this task after the task with the given
991 * task identifier completes (and any of our other
992 * conditions, such as delay, read or write-readyness
993 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
994 * on completion of other tasks.
995 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
996 * which means that the task will only be run after we receive SIGTERM
997 * @param rs set of file descriptors we want to read (can be NULL)
998 * @param ws set of file descriptors we want to write (can be NULL)
999 * @param task main function of the task
1000 * @param task_cls closure of task
1001 * @return unique task identifier for the job
1002 * only valid until "task" is started!
1004 GNUNET_SCHEDULER_TaskIdentifier
1005 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1006 enum GNUNET_SCHEDULER_Priority prio,
1007 GNUNET_SCHEDULER_TaskIdentifier
1009 struct GNUNET_TIME_Relative delay,
1010 const struct GNUNET_NETWORK_FDSet * rs,
1011 const struct GNUNET_NETWORK_FDSet * ws,
1012 GNUNET_SCHEDULER_Task task, void *task_cls)
1016 t = GNUNET_malloc (sizeof (struct Task));
1018 t->callback_cls = task_cls;
1021 t->read_set = GNUNET_NETWORK_fdset_create ();
1022 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1026 t->write_set = GNUNET_NETWORK_fdset_create ();
1027 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1029 t->id = ++sched->last_id;
1030 t->prereq_id = prerequisite_task;
1031 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1033 check_priority ((prio ==
1034 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1036 t->next = sched->pending;
1039 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1040 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1045 /* end of scheduler.c */