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 for for writing.
64 * Once ready, this is updated to reflect the set of file
65 * descriptors ready for operation.
67 struct GNUNET_NETWORK_FDSet *write_set;
70 * Unique task identifier.
72 GNUNET_SCHEDULER_TaskIdentifier id;
75 * Identifier of a prerequisite task.
77 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
80 * Absolute timeout value for the task, or
81 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
83 struct GNUNET_TIME_Absolute timeout;
86 * Why is the task ready? Set after task is added to ready queue.
87 * Initially set to zero. All reasons that have already been
88 * satisfied (i.e. read or write ready) will be set over time.
90 enum GNUNET_SCHEDULER_Reason reason;
95 enum GNUNET_SCHEDULER_Priority priority;
101 * Handle for the scheduling service.
103 struct GNUNET_SCHEDULER_Handle
107 * List of tasks waiting for an event.
109 struct Task *pending;
112 * ID of the task that is running right now.
114 struct Task *active_task;
117 * List of tasks ready to run right now,
118 * grouped by importance.
120 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
123 * Identity of the last task queued. Incremented for each task to
124 * generate a unique task ID (it is virtually impossible to start
125 * more than 2^64 tasks during the lifetime of a process).
127 GNUNET_SCHEDULER_TaskIdentifier last_id;
130 * Highest number so that all tasks with smaller identifiers
131 * have already completed. Also the lowest number of a task
132 * still waiting to be executed.
134 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
137 * Number of tasks on the ready list.
139 unsigned int ready_count;
142 * How many tasks have we run so far?
144 unsigned long long tasks_run;
147 * Priority of the task running right now. Only
148 * valid while a task is running.
150 enum GNUNET_SCHEDULER_Priority current_priority;
156 * Check that the given priority is legal (and return it).
158 * @param p priority value to check
159 * @return p on success, 0 on error
161 static enum GNUNET_SCHEDULER_Priority
162 check_priority (enum GNUNET_SCHEDULER_Priority p)
164 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
167 return 0; /* make compiler happy */
172 * Is a task with this identifier still pending? Also updates
173 * "lowest_pending_id" as a side-effect (for faster checks in the
174 * future), but only if the return value is "GNUNET_NO" (and
175 * the "lowest_pending_id" check failed).
177 * @param sched the scheduler
178 * @param id which task are we checking for
179 * @return GNUNET_YES if so, GNUNET_NO if not
182 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
183 GNUNET_SCHEDULER_TaskIdentifier id)
186 enum GNUNET_SCHEDULER_Priority p;
187 GNUNET_SCHEDULER_TaskIdentifier min;
189 if (id < sched->lowest_pending_id)
191 min = -1; /* maximum value */
192 pos = sched->pending;
201 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
203 pos = sched->ready[p];
213 sched->lowest_pending_id = min;
219 * Update all sets and timeout for select.
221 * @param sched the scheduler
222 * @param rs read-set, set to all FDs we would like to read (updated)
223 * @param ws write-set, set to all FDs we would like to write (updated)
224 * @param timeout next timeout (updated)
227 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
228 struct GNUNET_NETWORK_FDSet *rs,
229 struct GNUNET_NETWORK_FDSet *ws,
230 struct GNUNET_TIME_Relative *timeout)
234 pos = sched->pending;
237 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
238 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
244 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
246 struct GNUNET_TIME_Relative to;
248 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
249 if (timeout->value > to.value)
252 if (pos->read_set != NULL)
253 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
254 if (pos->write_set != NULL)
255 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
256 if (pos->reason != 0)
257 *timeout = GNUNET_TIME_UNIT_ZERO;
264 * Check if the ready set overlaps with the set we want to have ready.
265 * If so, update the want set (set all FDs that are ready). If not,
268 * @param ready set that is ready
269 * @param want set that we want to be ready
270 * @return GNUNET_YES if there was some overlap
273 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
274 struct GNUNET_NETWORK_FDSet *want)
278 if (GNUNET_NETWORK_fdset_overlap (ready, want))
280 /* copy all over (yes, there maybe unrelated bits,
281 but this should not hurt well-written clients) */
282 GNUNET_NETWORK_fdset_copy (want, ready);
290 * Check if the given task is eligible to run now.
291 * Also set the reason why it is eligible.
293 * @param sched the scheduler
294 * @param task task to check if it is ready
295 * @param now the current time
296 * @param rs set of FDs ready for reading
297 * @param ws set of FDs ready for writing
298 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
301 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
303 struct GNUNET_TIME_Absolute now,
304 const struct GNUNET_NETWORK_FDSet *rs,
305 const struct GNUNET_NETWORK_FDSet *ws)
307 if (now.value >= task->timeout.value)
308 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
309 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
310 (rs != NULL) && (set_overlaps (rs, task->read_set)))
311 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
312 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
313 (ws != NULL) && (set_overlaps (ws, task->write_set)))
314 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
315 if (task->reason == 0)
316 return GNUNET_NO; /* not ready */
317 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
319 if (GNUNET_YES == is_pending (sched, task->prereq_id))
320 return GNUNET_NO; /* prereq waiting */
321 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
328 * Put a task that is ready for execution into the ready queue.
330 * @param handle the scheduler
331 * @param task task ready for execution
334 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
336 enum GNUNET_SCHEDULER_Priority p = task->priority;
337 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
338 p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
339 task->next = handle->ready[check_priority (p)];
340 handle->ready[check_priority (p)] = task;
341 handle->ready_count++;
346 * Check which tasks are ready and move them
347 * to the respective ready queue.
349 * @param handle the scheduler
350 * @param rs FDs ready for reading
351 * @param ws FDs ready for writing
354 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
355 const struct GNUNET_NETWORK_FDSet *rs,
356 const struct GNUNET_NETWORK_FDSet *ws)
361 struct GNUNET_TIME_Absolute now;
363 now = GNUNET_TIME_absolute_get ();
365 pos = handle->pending;
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "Checking readiness of task: %llu / %p\n",
371 pos->id, pos->callback_cls);
374 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
377 handle->pending = next;
380 queue_ready_task (handle, pos);
391 * Request the shutdown of a scheduler. Marks all currently
392 * pending tasks as ready because of shutdown. This will
393 * cause all tasks to run (as soon as possible, respecting
394 * priorities and prerequisite tasks). Note that tasks
395 * scheduled AFTER this call may still be delayed arbitrarily.
397 * @param sched the scheduler
400 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
405 pos = sched->pending;
408 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
409 /* we don't move the task into the ready queue yet; check_ready
410 will do that later, possibly adding additional
414 for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
416 pos = sched->ready[i];
419 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
420 /* we don't move the task into the ready queue yet; check_ready
421 will do that later, possibly adding additional
430 * Destroy a task (release associated resources)
432 * @param t task to destroy
435 destroy_task (struct Task *t)
437 if (NULL != t->read_set)
438 GNUNET_NETWORK_fdset_destroy (t->read_set);
439 if (NULL != t->write_set)
440 GNUNET_NETWORK_fdset_destroy (t->write_set);
446 * Run at least one task in the highest-priority queue that is not
447 * empty. Keep running tasks until we are either no longer running
448 * "URGENT" tasks or until we have at least one "pending" task (which
449 * may become ready, hence we should select on it). Naturally, if
450 * there are no more ready tasks, we also return.
452 * @param sched the scheduler
455 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
457 enum GNUNET_SCHEDULER_Priority p;
459 struct GNUNET_SCHEDULER_TaskContext tc;
463 if (sched->ready_count == 0)
465 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
466 /* yes, p>0 is correct, 0 is "KEEP" which should
467 always be an empty queue (see assertion)! */
468 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
470 pos = sched->ready[p];
474 GNUNET_assert (pos != NULL); /* ready_count wrong? */
475 sched->ready[p] = pos->next;
476 sched->ready_count--;
477 sched->current_priority = pos->priority;
478 sched->active_task = pos;
480 tc.reason = pos->reason;
481 tc.read_ready = pos->read_set;
482 tc.write_ready = pos->write_set;
483 pos->callback (pos->callback_cls, &tc);
485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
486 "Running task: %llu / %p\n", pos->id, pos->callback_cls);
488 sched->active_task = NULL;
492 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
496 * Pipe used to communicate shutdown via signal.
498 static struct GNUNET_DISK_PipeHandle *sigpipe;
502 * Signal handler called for signals that should cause us to shutdown.
505 sighandler_shutdown ()
509 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
510 (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
516 * Initialize and run scheduler. This function will return when all
517 * tasks have completed. On systems with signals, receiving a SIGTERM
518 * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
519 * to be run after the active task is complete. As a result, SIGTERM
520 * causes all active tasks to be scheduled with reason
521 * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
522 * afterwards will execute normally!). Note that any particular signal
523 * will only shut down one scheduler; applications should always only
524 * create a single scheduler.
526 * @param task task to run immediately
527 * @param task_cls closure of task
530 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
532 struct GNUNET_SCHEDULER_Handle sched;
533 struct GNUNET_NETWORK_FDSet *rs;
534 struct GNUNET_NETWORK_FDSet *ws;
535 struct GNUNET_TIME_Relative timeout;
537 struct GNUNET_SIGNAL_Context *shc_int;
538 struct GNUNET_SIGNAL_Context *shc_term;
539 struct GNUNET_SIGNAL_Context *shc_quit;
540 struct GNUNET_SIGNAL_Context *shc_hup;
541 unsigned long long last_tr;
542 unsigned int busy_wait_warning;
543 const struct GNUNET_DISK_FileHandle *pr;
546 rs = GNUNET_NETWORK_fdset_create ();
547 ws = GNUNET_NETWORK_fdset_create ();
548 GNUNET_assert (sigpipe == NULL);
549 sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
550 GNUNET_assert (sigpipe != NULL);
551 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
552 GNUNET_assert (pr != NULL);
553 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
554 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
556 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
557 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
559 memset (&sched, 0, sizeof (sched));
560 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
561 GNUNET_SCHEDULER_add_continuation (&sched,
564 GNUNET_SCHEDULER_REASON_STARTUP);
566 busy_wait_warning = 0;
567 while ((sched.pending != NULL) || (sched.ready_count > 0))
569 GNUNET_NETWORK_fdset_zero (rs);
570 GNUNET_NETWORK_fdset_zero (ws);
571 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
572 update_sets (&sched, rs, ws, &timeout);
573 GNUNET_NETWORK_fdset_handle_set (rs, pr);
574 if (sched.ready_count > 0)
576 /* no blocking, more work already ready! */
577 timeout = GNUNET_TIME_UNIT_ZERO;
579 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
580 if (ret == GNUNET_SYSERR)
584 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
587 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
589 /* consume the signal */
590 GNUNET_DISK_file_read (pr, &c, sizeof (c));
591 /* mark all active tasks as ready due to shutdown */
592 GNUNET_SCHEDULER_shutdown (&sched);
594 if (last_tr == sched.tasks_run)
600 last_tr = sched.tasks_run;
601 busy_wait_warning = 0;
603 if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
605 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
606 _("Looks like we're busy waiting...\n"));
607 sleep (1); /* mitigate */
609 check_ready (&sched, rs, ws);
612 GNUNET_SIGNAL_handler_uninstall (shc_int);
613 GNUNET_SIGNAL_handler_uninstall (shc_term);
615 GNUNET_SIGNAL_handler_uninstall (shc_quit);
616 GNUNET_SIGNAL_handler_uninstall (shc_hup);
618 GNUNET_DISK_pipe_close (sigpipe);
620 GNUNET_NETWORK_fdset_destroy (rs);
621 GNUNET_NETWORK_fdset_destroy (ws);
626 * Obtain the reason code for why the current task was
627 * started. Will return the same value as
628 * the GNUNET_SCHEDULER_TaskContext's reason field.
630 * @param sched scheduler to query
631 * @return reason(s) why the current task is run
633 enum GNUNET_SCHEDULER_Reason
634 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
636 return sched->active_task->reason;
641 * Get information about the current load of this scheduler. Use this
642 * function to determine if an elective task should be added or simply
643 * dropped (if the decision should be made based on the number of
644 * tasks ready to run).
646 * @param sched scheduler to query
647 * @param p priority level to look at
648 * @return number of tasks pending right now
651 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
652 enum GNUNET_SCHEDULER_Priority p)
657 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
658 return sched->ready_count;
659 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
660 p = sched->current_priority;
662 pos = sched->ready[p];
673 * Cancel the task with the specified identifier.
674 * The task must not yet have run.
676 * @param sched scheduler to use
677 * @param task id of the task to cancel
678 * @return original closure of the task
681 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
682 GNUNET_SCHEDULER_TaskIdentifier task)
686 enum GNUNET_SCHEDULER_Priority p;
702 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
709 sched->ready_count--;
719 sched->pending = t->next;
721 sched->ready[p] = t->next;
724 prev->next = t->next;
725 ret = t->callback_cls;
727 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
728 "Canceling task: %llu / %p\n", task, t->callback_cls);
736 * Continue the current execution with the given function. This is
737 * similar to the other "add" functions except that there is no delay
738 * and the reason code can be specified.
740 * @param sched scheduler to use
741 * @param task main function of the task
742 * @param task_cls closure for 'main'
743 * @param reason reason for task invocation
746 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
747 GNUNET_SCHEDULER_Task task,
749 enum GNUNET_SCHEDULER_Reason reason)
753 t = GNUNET_malloc (sizeof (struct Task));
755 t->callback_cls = task_cls;
756 t->id = ++sched->last_id;
758 t->priority = sched->current_priority;
760 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761 "Adding continuation task: %llu / %p\n",
762 t->id, t->callback_cls);
764 queue_ready_task (sched, t);
770 * Schedule a new task to be run after the specified prerequisite task
771 * has completed. It will be run with the priority of the calling
774 * @param sched scheduler to use
775 * @param prerequisite_task run this task after the task with the given
776 * task identifier completes (and any of our other
777 * conditions, such as delay, read or write-readiness
778 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
779 * on completion of other tasks (this will cause the task to run as
781 * @param task main function of the task
782 * @param task_cls closure of task
783 * @return unique task identifier for the job
784 * only valid until "task" is started!
786 GNUNET_SCHEDULER_TaskIdentifier
787 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
788 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
789 GNUNET_SCHEDULER_Task task, void *task_cls)
791 return GNUNET_SCHEDULER_add_select (sched,
792 GNUNET_SCHEDULER_PRIORITY_KEEP,
794 GNUNET_TIME_UNIT_ZERO,
795 NULL, NULL, task, task_cls);
800 * Schedule a new task to be run with a specified priority.
802 * @param sched scheduler to use
803 * @param prio how important is the new task?
804 * @param task main function of the task
805 * @param task_cls closure of task
806 * @return unique task identifier for the job
807 * only valid until "task" is started!
809 GNUNET_SCHEDULER_TaskIdentifier
810 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
811 enum GNUNET_SCHEDULER_Priority prio,
812 GNUNET_SCHEDULER_Task task,
815 return GNUNET_SCHEDULER_add_select (sched,
817 GNUNET_SCHEDULER_NO_TASK,
818 GNUNET_TIME_UNIT_ZERO,
819 NULL, NULL, task, task_cls);
825 * Schedule a new task to be run with a specified delay. The task
826 * will be scheduled for execution once the delay has expired. It
827 * will be run with the priority of the calling task.
829 * @param sched scheduler to use
830 * @param delay when should this operation time out? Use
831 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
832 * @param task main function of the task
833 * @param task_cls closure of task
834 * @return unique task identifier for the job
835 * only valid until "task" is started!
837 GNUNET_SCHEDULER_TaskIdentifier
838 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
839 struct GNUNET_TIME_Relative delay,
840 GNUNET_SCHEDULER_Task task, void *task_cls)
842 return GNUNET_SCHEDULER_add_select (sched,
843 GNUNET_SCHEDULER_PRIORITY_KEEP,
844 GNUNET_SCHEDULER_NO_TASK, delay,
845 NULL, NULL, task, task_cls);
851 * Schedule a new task to be run as soon as possible. The task
852 * will be run with the priority of the calling task.
854 * @param sched scheduler to use
855 * @param task main function of the task
856 * @param task_cls closure of task
857 * @return unique task identifier for the job
858 * only valid until "task" is started!
860 GNUNET_SCHEDULER_TaskIdentifier
861 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
862 GNUNET_SCHEDULER_Task task,
865 return GNUNET_SCHEDULER_add_select (sched,
866 GNUNET_SCHEDULER_PRIORITY_KEEP,
867 GNUNET_SCHEDULER_NO_TASK,
868 GNUNET_TIME_UNIT_ZERO,
869 NULL, NULL, task, task_cls);
875 * Schedule a new task to be run with a specified delay or when the
876 * specified file descriptor is ready for reading. The delay can be
877 * used as a timeout on the socket being ready. The task will be
878 * scheduled for execution once either the delay has expired or the
879 * socket operation is ready. It will be run with the priority of
882 * @param sched scheduler to use
883 * @param delay when should this operation time out? Use
884 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
885 * @param rfd read file-descriptor
886 * @param task main function of the task
887 * @param task_cls closure of task
888 * @return unique task identifier for the job
889 * only valid until "task" is started!
891 GNUNET_SCHEDULER_TaskIdentifier
892 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
893 struct GNUNET_TIME_Relative delay,
894 struct GNUNET_NETWORK_Handle * rfd,
895 GNUNET_SCHEDULER_Task task, void *task_cls)
897 struct GNUNET_NETWORK_FDSet *rs;
898 GNUNET_SCHEDULER_TaskIdentifier ret;
900 GNUNET_assert (rfd != NULL);
901 rs = GNUNET_NETWORK_fdset_create ();
902 GNUNET_NETWORK_fdset_set (rs, rfd);
903 ret = GNUNET_SCHEDULER_add_select (sched,
904 GNUNET_SCHEDULER_PRIORITY_KEEP,
905 GNUNET_SCHEDULER_NO_TASK,
906 delay, rs, NULL, task, task_cls);
907 GNUNET_NETWORK_fdset_destroy (rs);
913 * Schedule a new task to be run with a specified delay or when the
914 * specified file descriptor is ready for writing. The delay can be
915 * used as a timeout on the socket being ready. The task will be
916 * scheduled for execution once either the delay has expired or the
917 * socket operation is ready. It will be run with the priority of
920 * @param sched scheduler to use
921 * @param delay when should this operation time out? Use
922 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
923 * @param wfd write file-descriptor
924 * @param task main function of the task
925 * @param task_cls closure of task
926 * @return unique task identifier for the job
927 * only valid until "task" is started!
929 GNUNET_SCHEDULER_TaskIdentifier
930 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
931 struct GNUNET_TIME_Relative delay,
932 struct GNUNET_NETWORK_Handle * wfd,
933 GNUNET_SCHEDULER_Task task, void *task_cls)
935 struct GNUNET_NETWORK_FDSet *ws;
936 GNUNET_SCHEDULER_TaskIdentifier ret;
938 GNUNET_assert (wfd != NULL);
939 ws = GNUNET_NETWORK_fdset_create ();
940 GNUNET_NETWORK_fdset_set (ws, wfd);
941 ret = GNUNET_SCHEDULER_add_select (sched,
942 GNUNET_SCHEDULER_PRIORITY_KEEP,
943 GNUNET_SCHEDULER_NO_TASK, delay,
944 NULL, ws, task, task_cls);
945 GNUNET_NETWORK_fdset_destroy (ws);
951 * Schedule a new task to be run with a specified delay or when the
952 * specified file descriptor is ready for reading. The delay can be
953 * used as a timeout on the socket being ready. The task will be
954 * scheduled for execution once either the delay has expired or the
955 * socket operation is ready. It will be run with the priority of
958 * @param sched scheduler to use
959 * @param delay when should this operation time out? Use
960 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
961 * @param rfd read file-descriptor
962 * @param task main function of the task
963 * @param task_cls closure of task
964 * @return unique task identifier for the job
965 * only valid until "task" is started!
967 GNUNET_SCHEDULER_TaskIdentifier
968 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
969 struct GNUNET_TIME_Relative delay,
970 const struct GNUNET_DISK_FileHandle * rfd,
971 GNUNET_SCHEDULER_Task task, void *task_cls)
973 struct GNUNET_NETWORK_FDSet *rs;
974 GNUNET_SCHEDULER_TaskIdentifier ret;
976 GNUNET_assert (rfd != NULL);
977 rs = GNUNET_NETWORK_fdset_create ();
978 GNUNET_NETWORK_fdset_handle_set (rs, rfd);
979 ret = GNUNET_SCHEDULER_add_select (sched,
980 GNUNET_SCHEDULER_PRIORITY_KEEP,
981 GNUNET_SCHEDULER_NO_TASK, delay,
982 rs, NULL, task, task_cls);
983 GNUNET_NETWORK_fdset_destroy (rs);
989 * Schedule a new task to be run with a specified delay or when the
990 * specified file descriptor is ready for writing. The delay can be
991 * used as a timeout on the socket being ready. The task will be
992 * scheduled for execution once either the delay has expired or the
993 * socket operation is ready. It will be run with the priority of
996 * @param sched scheduler to use
997 * @param delay when should this operation time out? Use
998 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
999 * @param wfd write file-descriptor
1000 * @param task main function of the task
1001 * @param task_cls closure of task
1002 * @return unique task identifier for the job
1003 * only valid until "task" is started!
1005 GNUNET_SCHEDULER_TaskIdentifier
1006 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1007 struct GNUNET_TIME_Relative delay,
1008 const struct GNUNET_DISK_FileHandle * wfd,
1009 GNUNET_SCHEDULER_Task task, void *task_cls)
1011 struct GNUNET_NETWORK_FDSet *ws;
1012 GNUNET_SCHEDULER_TaskIdentifier ret;
1014 GNUNET_assert (wfd != NULL);
1015 ws = GNUNET_NETWORK_fdset_create ();
1016 GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1017 ret = GNUNET_SCHEDULER_add_select (sched,
1018 GNUNET_SCHEDULER_PRIORITY_KEEP,
1019 GNUNET_SCHEDULER_NO_TASK,
1020 delay, NULL, ws, task, task_cls);
1021 GNUNET_NETWORK_fdset_destroy (ws);
1028 * Schedule a new task to be run with a specified delay or when any of
1029 * the specified file descriptor sets is ready. The delay can be used
1030 * as a timeout on the socket(s) being ready. The task will be
1031 * scheduled for execution once either the delay has expired or any of
1032 * the socket operations is ready. This is the most general
1033 * function of the "add" family. Note that the "prerequisite_task"
1034 * must be satisfied in addition to any of the other conditions. In
1035 * other words, the task will be started when
1037 * (prerequisite-run)
1041 * || (shutdown-active && run-on-shutdown) )
1044 * @param sched scheduler to use
1045 * @param prio how important is this task?
1046 * @param prerequisite_task run this task after the task with the given
1047 * task identifier completes (and any of our other
1048 * conditions, such as delay, read or write-readiness
1049 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1050 * on completion of other tasks.
1051 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1052 * which means that the task will only be run after we receive SIGTERM
1053 * @param rs set of file descriptors we want to read (can be NULL)
1054 * @param ws set of file descriptors we want to write (can be NULL)
1055 * @param task main function of the task
1056 * @param task_cls closure of task
1057 * @return unique task identifier for the job
1058 * only valid until "task" is started!
1060 GNUNET_SCHEDULER_TaskIdentifier
1061 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1062 enum GNUNET_SCHEDULER_Priority prio,
1063 GNUNET_SCHEDULER_TaskIdentifier
1065 struct GNUNET_TIME_Relative delay,
1066 const struct GNUNET_NETWORK_FDSet * rs,
1067 const struct GNUNET_NETWORK_FDSet * ws,
1068 GNUNET_SCHEDULER_Task task, void *task_cls)
1072 t = GNUNET_malloc (sizeof (struct Task));
1074 t->callback_cls = task_cls;
1077 t->read_set = GNUNET_NETWORK_fdset_create ();
1078 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1082 t->write_set = GNUNET_NETWORK_fdset_create ();
1083 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1085 t->id = ++sched->last_id;
1086 t->prereq_id = prerequisite_task;
1087 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1089 check_priority ((prio ==
1090 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1092 t->next = sched->pending;
1095 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1101 /* end of scheduler.c */