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_os_lib.h"
29 #include "gnunet_scheduler_lib.h"
30 #include "gnunet_signal_lib.h"
31 #include "gnunet_time_lib.h"
35 * Obtain trace information for all scheduler calls that schedule tasks.
37 #define EXECINFO GNUNET_NO
40 * Depth of the traces collected via EXECINFO.
42 #define MAX_TRACE_DEPTH 50
45 #define DEBUG_TASKS GNUNET_NO
48 * Should we figure out which tasks are delayed for a while
49 * before they are run? (Consider using in combination with EXECINFO).
51 #define PROFILE_DELAYS GNUNET_NO
54 * Task that were in the queue for longer than this are reported if
55 * PROFILE_DELAYS is active.
57 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
60 * Linked list of pending tasks.
65 * This is a linked list.
70 * Function to run when ready.
72 GNUNET_SCHEDULER_Task callback;
75 * Closure for the callback.
80 * Set of file descriptors this task is waiting
81 * for for reading. Once ready, this is updated
82 * to reflect the set of file descriptors ready
85 struct GNUNET_NETWORK_FDSet *read_set;
88 * Set of file descriptors this task is waiting for for writing.
89 * Once ready, this is updated to reflect the set of file
90 * descriptors ready for operation.
92 struct GNUNET_NETWORK_FDSet *write_set;
95 * Unique task identifier.
97 GNUNET_SCHEDULER_TaskIdentifier id;
100 * Identifier of a prerequisite task.
102 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
105 * Absolute timeout value for the task, or
106 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
108 struct GNUNET_TIME_Absolute timeout;
112 * When was the task scheduled?
114 struct GNUNET_TIME_Absolute start_time;
118 * Why is the task ready? Set after task is added to ready queue.
119 * Initially set to zero. All reasons that have already been
120 * satisfied (i.e. read or write ready) will be set over time.
122 enum GNUNET_SCHEDULER_Reason reason;
127 enum GNUNET_SCHEDULER_Priority priority;
131 * Array of strings which make up a backtrace from the point when this
132 * task was scheduled (essentially, who scheduled the task?)
134 char **backtrace_strings;
137 * Size of the backtrace_strings array
139 int num_backtrace_strings;
146 * Handle for the scheduling service.
148 struct GNUNET_SCHEDULER_Handle
152 * List of tasks waiting for an event.
154 struct Task *pending;
157 * ID of the task that is running right now.
159 struct Task *active_task;
162 * List of tasks ready to run right now,
163 * grouped by importance.
165 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
168 * Identity of the last task queued. Incremented for each task to
169 * generate a unique task ID (it is virtually impossible to start
170 * more than 2^64 tasks during the lifetime of a process).
172 GNUNET_SCHEDULER_TaskIdentifier last_id;
175 * Highest number so that all tasks with smaller identifiers
176 * have already completed. Also the lowest number of a task
177 * still waiting to be executed.
179 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
182 * Number of tasks on the ready list.
184 unsigned int ready_count;
187 * How many tasks have we run so far?
189 unsigned long long tasks_run;
192 * Priority of the task running right now. Only
193 * valid while a task is running.
195 enum GNUNET_SCHEDULER_Priority current_priority;
198 * How 'nice' are we right now?
206 * Check that the given priority is legal (and return it).
208 * @param p priority value to check
209 * @return p on success, 0 on error
211 static enum GNUNET_SCHEDULER_Priority
212 check_priority (enum GNUNET_SCHEDULER_Priority p)
214 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
217 return 0; /* make compiler happy */
222 * Is a task with this identifier still pending? Also updates
223 * "lowest_pending_id" as a side-effect (for faster checks in the
224 * future), but only if the return value is "GNUNET_NO" (and
225 * the "lowest_pending_id" check failed).
227 * @param sched the scheduler
228 * @param id which task are we checking for
229 * @return GNUNET_YES if so, GNUNET_NO if not
232 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
233 GNUNET_SCHEDULER_TaskIdentifier id)
236 enum GNUNET_SCHEDULER_Priority p;
237 GNUNET_SCHEDULER_TaskIdentifier min;
239 if (id < sched->lowest_pending_id)
241 min = -1; /* maximum value */
242 pos = sched->pending;
251 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
253 pos = sched->ready[p];
263 sched->lowest_pending_id = min;
269 * Update all sets and timeout for select.
271 * @param sched the scheduler
272 * @param rs read-set, set to all FDs we would like to read (updated)
273 * @param ws write-set, set to all FDs we would like to write (updated)
274 * @param timeout next timeout (updated)
277 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
278 struct GNUNET_NETWORK_FDSet *rs,
279 struct GNUNET_NETWORK_FDSet *ws,
280 struct GNUNET_TIME_Relative *timeout)
284 pos = sched->pending;
287 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
288 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
294 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
296 struct GNUNET_TIME_Relative to;
298 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
299 if (timeout->value > to.value)
302 if (pos->read_set != NULL)
303 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
304 if (pos->write_set != NULL)
305 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
306 if (pos->reason != 0)
307 *timeout = GNUNET_TIME_UNIT_ZERO;
314 * Check if the ready set overlaps with the set we want to have ready.
315 * If so, update the want set (set all FDs that are ready). If not,
318 * @param ready set that is ready
319 * @param want set that we want to be ready
320 * @return GNUNET_YES if there was some overlap
323 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
324 struct GNUNET_NETWORK_FDSet *want)
328 if (GNUNET_NETWORK_fdset_overlap (ready, want))
330 /* copy all over (yes, there maybe unrelated bits,
331 but this should not hurt well-written clients) */
332 GNUNET_NETWORK_fdset_copy (want, ready);
340 * Check if the given task is eligible to run now.
341 * Also set the reason why it is eligible.
343 * @param sched the scheduler
344 * @param task task to check if it is ready
345 * @param now the current time
346 * @param rs set of FDs ready for reading
347 * @param ws set of FDs ready for writing
348 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
351 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
353 struct GNUNET_TIME_Absolute now,
354 const struct GNUNET_NETWORK_FDSet *rs,
355 const struct GNUNET_NETWORK_FDSet *ws)
357 if (now.value >= task->timeout.value)
358 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
359 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
360 (rs != NULL) && (set_overlaps (rs, task->read_set)))
361 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
362 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
363 (ws != NULL) && (set_overlaps (ws, task->write_set)))
364 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
365 if (task->reason == 0)
366 return GNUNET_NO; /* not ready */
367 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
369 if (GNUNET_YES == is_pending (sched, task->prereq_id))
370 return GNUNET_NO; /* prereq waiting */
371 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
378 * Put a task that is ready for execution into the ready queue.
380 * @param handle the scheduler
381 * @param task task ready for execution
384 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
386 enum GNUNET_SCHEDULER_Priority p = task->priority;
387 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
388 p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
389 task->next = handle->ready[check_priority (p)];
390 handle->ready[check_priority (p)] = task;
391 handle->ready_count++;
396 * Check which tasks are ready and move them
397 * to the respective ready queue.
399 * @param handle the scheduler
400 * @param rs FDs ready for reading
401 * @param ws FDs ready for writing
404 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
405 const struct GNUNET_NETWORK_FDSet *rs,
406 const struct GNUNET_NETWORK_FDSet *ws)
411 struct GNUNET_TIME_Absolute now;
413 now = GNUNET_TIME_absolute_get ();
415 pos = handle->pending;
419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420 "Checking readiness of task: %llu / %p\n",
421 pos->id, pos->callback_cls);
424 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
427 handle->pending = next;
430 queue_ready_task (handle, pos);
441 * Request the shutdown of a scheduler. Marks all currently
442 * pending tasks as ready because of shutdown. This will
443 * cause all tasks to run (as soon as possible, respecting
444 * priorities and prerequisite tasks). Note that tasks
445 * scheduled AFTER this call may still be delayed arbitrarily.
447 * @param sched the scheduler
450 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
455 pos = sched->pending;
458 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
459 /* we don't move the task into the ready queue yet; check_ready
460 will do that later, possibly adding additional
464 for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
466 pos = sched->ready[i];
469 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
470 /* we don't move the task into the ready queue yet; check_ready
471 will do that later, possibly adding additional
480 * Destroy a task (release associated resources)
482 * @param t task to destroy
485 destroy_task (struct Task *t)
487 if (NULL != t->read_set)
488 GNUNET_NETWORK_fdset_destroy (t->read_set);
489 if (NULL != t->write_set)
490 GNUNET_NETWORK_fdset_destroy (t->write_set);
492 GNUNET_free (t->backtrace_strings);
499 * Run at least one task in the highest-priority queue that is not
500 * empty. Keep running tasks until we are either no longer running
501 * "URGENT" tasks or until we have at least one "pending" task (which
502 * may become ready, hence we should select on it). Naturally, if
503 * there are no more ready tasks, we also return.
505 * @param sched the scheduler
508 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
510 enum GNUNET_SCHEDULER_Priority p;
512 struct GNUNET_SCHEDULER_TaskContext tc;
516 if (sched->ready_count == 0)
518 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
519 /* yes, p>0 is correct, 0 is "KEEP" which should
520 always be an empty queue (see assertion)! */
521 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
523 pos = sched->ready[p];
527 GNUNET_assert (pos != NULL); /* ready_count wrong? */
528 sched->ready[p] = pos->next;
529 sched->ready_count--;
530 if (sched->current_priority != pos->priority)
532 sched->current_priority = pos->priority;
533 (void) GNUNET_OS_set_process_priority (0, pos->priority);
535 sched->active_task = pos;
537 if (GNUNET_TIME_absolute_get_duration (pos->start_time).value >
538 DELAY_THRESHOLD.value)
540 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
541 "Task %u took %llums to be scheduled\n",
543 (unsigned long long) GNUNET_TIME_absolute_get_duration (pos->start_time).value);
546 for (i=0;i<pos->num_backtrace_strings;i++)
547 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
548 "Task %u trace %d: %s\n",
551 pos->backtrace_strings[i]);
556 tc.reason = pos->reason;
557 tc.read_ready = pos->read_set;
558 tc.write_ready = pos->write_set;
559 pos->callback (pos->callback_cls, &tc);
561 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562 "Running task: %llu / %p\n", pos->id, pos->callback_cls);
564 sched->active_task = NULL;
568 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
572 * Pipe used to communicate shutdown via signal.
574 static struct GNUNET_DISK_PipeHandle *sigpipe;
578 * Signal handler called for signals that should cause us to shutdown.
581 sighandler_shutdown ()
585 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
586 (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
592 * Initialize and run scheduler. This function will return when all
593 * tasks have completed. On systems with signals, receiving a SIGTERM
594 * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
595 * to be run after the active task is complete. As a result, SIGTERM
596 * causes all active tasks to be scheduled with reason
597 * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
598 * afterwards will execute normally!). Note that any particular signal
599 * will only shut down one scheduler; applications should always only
600 * create a single scheduler.
602 * @param task task to run immediately
603 * @param task_cls closure of task
606 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
608 struct GNUNET_SCHEDULER_Handle sched;
609 struct GNUNET_NETWORK_FDSet *rs;
610 struct GNUNET_NETWORK_FDSet *ws;
611 struct GNUNET_TIME_Relative timeout;
613 struct GNUNET_SIGNAL_Context *shc_int;
614 struct GNUNET_SIGNAL_Context *shc_term;
615 struct GNUNET_SIGNAL_Context *shc_quit;
616 struct GNUNET_SIGNAL_Context *shc_hup;
617 unsigned long long last_tr;
618 unsigned int busy_wait_warning;
619 const struct GNUNET_DISK_FileHandle *pr;
622 rs = GNUNET_NETWORK_fdset_create ();
623 ws = GNUNET_NETWORK_fdset_create ();
624 GNUNET_assert (sigpipe == NULL);
625 sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
626 GNUNET_assert (sigpipe != NULL);
627 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
628 GNUNET_assert (pr != NULL);
629 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
630 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
632 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
633 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
635 memset (&sched, 0, sizeof (sched));
636 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
637 GNUNET_SCHEDULER_add_continuation (&sched,
640 GNUNET_SCHEDULER_REASON_STARTUP);
642 busy_wait_warning = 0;
643 while ((sched.pending != NULL) || (sched.ready_count > 0))
645 GNUNET_NETWORK_fdset_zero (rs);
646 GNUNET_NETWORK_fdset_zero (ws);
647 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
648 update_sets (&sched, rs, ws, &timeout);
649 GNUNET_NETWORK_fdset_handle_set (rs, pr);
650 if (sched.ready_count > 0)
652 /* no blocking, more work already ready! */
653 timeout = GNUNET_TIME_UNIT_ZERO;
655 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
656 if (ret == GNUNET_SYSERR)
660 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
664 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
666 /* consume the signal */
667 GNUNET_DISK_file_read (pr, &c, sizeof (c));
668 /* mark all active tasks as ready due to shutdown */
669 GNUNET_SCHEDULER_shutdown (&sched);
671 if (last_tr == sched.tasks_run)
677 last_tr = sched.tasks_run;
678 busy_wait_warning = 0;
680 if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
682 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
683 _("Looks like we're busy waiting...\n"));
684 sleep (1); /* mitigate */
686 check_ready (&sched, rs, ws);
689 GNUNET_SIGNAL_handler_uninstall (shc_int);
690 GNUNET_SIGNAL_handler_uninstall (shc_term);
692 GNUNET_SIGNAL_handler_uninstall (shc_quit);
693 GNUNET_SIGNAL_handler_uninstall (shc_hup);
695 GNUNET_DISK_pipe_close (sigpipe);
697 GNUNET_NETWORK_fdset_destroy (rs);
698 GNUNET_NETWORK_fdset_destroy (ws);
703 * Obtain the reason code for why the current task was
704 * started. Will return the same value as
705 * the GNUNET_SCHEDULER_TaskContext's reason field.
707 * @param sched scheduler to query
708 * @return reason(s) why the current task is run
710 enum GNUNET_SCHEDULER_Reason
711 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
713 return sched->active_task->reason;
718 * Get information about the current load of this scheduler. Use this
719 * function to determine if an elective task should be added or simply
720 * dropped (if the decision should be made based on the number of
721 * tasks ready to run).
723 * @param sched scheduler to query
724 * @param p priority level to look at
725 * @return number of tasks pending right now
728 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
729 enum GNUNET_SCHEDULER_Priority p)
734 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
735 return sched->ready_count;
736 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
737 p = sched->current_priority;
739 pos = sched->ready[p];
750 * Cancel the task with the specified identifier.
751 * The task must not yet have run.
753 * @param sched scheduler to use
754 * @param task id of the task to cancel
755 * @return original closure of the task
758 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
759 GNUNET_SCHEDULER_TaskIdentifier task)
763 enum GNUNET_SCHEDULER_Priority p;
779 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
786 sched->ready_count--;
796 sched->pending = t->next;
798 sched->ready[p] = t->next;
801 prev->next = t->next;
802 ret = t->callback_cls;
804 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
805 "Canceling task: %llu / %p\n", task, t->callback_cls);
813 * Continue the current execution with the given function. This is
814 * similar to the other "add" functions except that there is no delay
815 * and the reason code can be specified.
817 * @param sched scheduler to use
818 * @param task main function of the task
819 * @param task_cls closure for 'main'
820 * @param reason reason for task invocation
823 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
824 GNUNET_SCHEDULER_Task task,
826 enum GNUNET_SCHEDULER_Reason reason)
830 void *backtrace_array[50];
832 t = GNUNET_malloc (sizeof (struct Task));
834 t->num_backtrace_strings = backtrace(backtrace_array, 50);
835 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
838 t->callback_cls = task_cls;
839 t->id = ++sched->last_id;
841 t->start_time = GNUNET_TIME_absolute_get ();
844 t->priority = sched->current_priority;
846 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
847 "Adding continuation task: %llu / %p\n",
848 t->id, t->callback_cls);
850 queue_ready_task (sched, t);
856 * Schedule a new task to be run after the specified prerequisite task
857 * has completed. It will be run with the priority of the calling
860 * @param sched scheduler to use
861 * @param prerequisite_task run this task after the task with the given
862 * task identifier completes (and any of our other
863 * conditions, such as delay, read or write-readiness
864 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
865 * on completion of other tasks (this will cause the task to run as
867 * @param task main function of the task
868 * @param task_cls closure of task
869 * @return unique task identifier for the job
870 * only valid until "task" is started!
872 GNUNET_SCHEDULER_TaskIdentifier
873 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
874 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
875 GNUNET_SCHEDULER_Task task, void *task_cls)
877 return GNUNET_SCHEDULER_add_select (sched,
878 GNUNET_SCHEDULER_PRIORITY_KEEP,
880 GNUNET_TIME_UNIT_ZERO,
881 NULL, NULL, task, task_cls);
886 * Schedule a new task to be run with a specified priority.
888 * @param sched scheduler to use
889 * @param prio how important is the new task?
890 * @param task main function of the task
891 * @param task_cls closure of task
892 * @return unique task identifier for the job
893 * only valid until "task" is started!
895 GNUNET_SCHEDULER_TaskIdentifier
896 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
897 enum GNUNET_SCHEDULER_Priority prio,
898 GNUNET_SCHEDULER_Task task,
901 return GNUNET_SCHEDULER_add_select (sched,
903 GNUNET_SCHEDULER_NO_TASK,
904 GNUNET_TIME_UNIT_ZERO,
905 NULL, NULL, task, task_cls);
911 * Schedule a new task to be run with a specified delay. The task
912 * will be scheduled for execution once the delay has expired. It
913 * will be run with the priority of the calling task.
915 * @param sched scheduler to use
916 * @param delay when should this operation time out? Use
917 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
918 * @param task main function of the task
919 * @param task_cls closure of task
920 * @return unique task identifier for the job
921 * only valid until "task" is started!
923 GNUNET_SCHEDULER_TaskIdentifier
924 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
925 struct GNUNET_TIME_Relative delay,
926 GNUNET_SCHEDULER_Task task, void *task_cls)
928 return GNUNET_SCHEDULER_add_select (sched,
929 GNUNET_SCHEDULER_PRIORITY_KEEP,
930 GNUNET_SCHEDULER_NO_TASK, delay,
931 NULL, NULL, task, task_cls);
937 * Schedule a new task to be run as soon as possible. The task
938 * will be run with the priority of the calling task.
940 * @param sched scheduler to use
941 * @param task main function of the task
942 * @param task_cls closure of task
943 * @return unique task identifier for the job
944 * only valid until "task" is started!
946 GNUNET_SCHEDULER_TaskIdentifier
947 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
948 GNUNET_SCHEDULER_Task task,
951 return GNUNET_SCHEDULER_add_select (sched,
952 GNUNET_SCHEDULER_PRIORITY_KEEP,
953 GNUNET_SCHEDULER_NO_TASK,
954 GNUNET_TIME_UNIT_ZERO,
955 NULL, NULL, task, task_cls);
961 * Schedule a new task to be run with a specified delay or when the
962 * specified file descriptor is ready for reading. The delay can be
963 * used as a timeout on the socket being ready. The task will be
964 * scheduled for execution once either the delay has expired or the
965 * socket operation is ready. It will be run with the priority of
968 * @param sched scheduler to use
969 * @param delay when should this operation time out? Use
970 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
971 * @param rfd read file-descriptor
972 * @param task main function of the task
973 * @param task_cls closure of task
974 * @return unique task identifier for the job
975 * only valid until "task" is started!
977 GNUNET_SCHEDULER_TaskIdentifier
978 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
979 struct GNUNET_TIME_Relative delay,
980 struct GNUNET_NETWORK_Handle * rfd,
981 GNUNET_SCHEDULER_Task task, void *task_cls)
983 struct GNUNET_NETWORK_FDSet *rs;
984 GNUNET_SCHEDULER_TaskIdentifier ret;
986 GNUNET_assert (rfd != NULL);
987 rs = GNUNET_NETWORK_fdset_create ();
988 GNUNET_NETWORK_fdset_set (rs, rfd);
989 ret = GNUNET_SCHEDULER_add_select (sched,
990 GNUNET_SCHEDULER_PRIORITY_KEEP,
991 GNUNET_SCHEDULER_NO_TASK,
992 delay, rs, NULL, task, task_cls);
993 GNUNET_NETWORK_fdset_destroy (rs);
999 * Schedule a new task to be run with a specified delay or when the
1000 * specified file descriptor is ready for writing. The delay can be
1001 * used as a timeout on the socket being ready. The task will be
1002 * scheduled for execution once either the delay has expired or the
1003 * socket operation is ready. It will be run with the priority of
1006 * @param sched scheduler to use
1007 * @param delay when should this operation time out? Use
1008 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1009 * @param wfd write file-descriptor
1010 * @param task main function of the task
1011 * @param task_cls closure of task
1012 * @return unique task identifier for the job
1013 * only valid until "task" is started!
1015 GNUNET_SCHEDULER_TaskIdentifier
1016 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
1017 struct GNUNET_TIME_Relative delay,
1018 struct GNUNET_NETWORK_Handle * wfd,
1019 GNUNET_SCHEDULER_Task task, void *task_cls)
1021 struct GNUNET_NETWORK_FDSet *ws;
1022 GNUNET_SCHEDULER_TaskIdentifier ret;
1024 GNUNET_assert (wfd != NULL);
1025 ws = GNUNET_NETWORK_fdset_create ();
1026 GNUNET_NETWORK_fdset_set (ws, wfd);
1027 ret = GNUNET_SCHEDULER_add_select (sched,
1028 GNUNET_SCHEDULER_PRIORITY_KEEP,
1029 GNUNET_SCHEDULER_NO_TASK, delay,
1030 NULL, ws, task, task_cls);
1031 GNUNET_NETWORK_fdset_destroy (ws);
1037 * Schedule a new task to be run with a specified delay or when the
1038 * specified file descriptor is ready for reading. The delay can be
1039 * used as a timeout on the socket being ready. The task will be
1040 * scheduled for execution once either the delay has expired or the
1041 * socket operation is ready. It will be run with the priority of
1044 * @param sched scheduler to use
1045 * @param delay when should this operation time out? Use
1046 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1047 * @param rfd read file-descriptor
1048 * @param task main function of the task
1049 * @param task_cls closure of task
1050 * @return unique task identifier for the job
1051 * only valid until "task" is started!
1053 GNUNET_SCHEDULER_TaskIdentifier
1054 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1055 struct GNUNET_TIME_Relative delay,
1056 const struct GNUNET_DISK_FileHandle * rfd,
1057 GNUNET_SCHEDULER_Task task, void *task_cls)
1059 struct GNUNET_NETWORK_FDSet *rs;
1060 GNUNET_SCHEDULER_TaskIdentifier ret;
1062 GNUNET_assert (rfd != NULL);
1063 rs = GNUNET_NETWORK_fdset_create ();
1064 GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1065 ret = GNUNET_SCHEDULER_add_select (sched,
1066 GNUNET_SCHEDULER_PRIORITY_KEEP,
1067 GNUNET_SCHEDULER_NO_TASK, delay,
1068 rs, NULL, task, task_cls);
1069 GNUNET_NETWORK_fdset_destroy (rs);
1075 * Schedule a new task to be run with a specified delay or when the
1076 * specified file descriptor is ready for writing. The delay can be
1077 * used as a timeout on the socket being ready. The task will be
1078 * scheduled for execution once either the delay has expired or the
1079 * socket operation is ready. It will be run with the priority of
1082 * @param sched scheduler to use
1083 * @param delay when should this operation time out? Use
1084 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1085 * @param wfd write file-descriptor
1086 * @param task main function of the task
1087 * @param task_cls closure of task
1088 * @return unique task identifier for the job
1089 * only valid until "task" is started!
1091 GNUNET_SCHEDULER_TaskIdentifier
1092 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1093 struct GNUNET_TIME_Relative delay,
1094 const struct GNUNET_DISK_FileHandle * wfd,
1095 GNUNET_SCHEDULER_Task task, void *task_cls)
1097 struct GNUNET_NETWORK_FDSet *ws;
1098 GNUNET_SCHEDULER_TaskIdentifier ret;
1100 GNUNET_assert (wfd != NULL);
1101 ws = GNUNET_NETWORK_fdset_create ();
1102 GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1103 ret = GNUNET_SCHEDULER_add_select (sched,
1104 GNUNET_SCHEDULER_PRIORITY_KEEP,
1105 GNUNET_SCHEDULER_NO_TASK,
1106 delay, NULL, ws, task, task_cls);
1107 GNUNET_NETWORK_fdset_destroy (ws);
1114 * Schedule a new task to be run with a specified delay or when any of
1115 * the specified file descriptor sets is ready. The delay can be used
1116 * as a timeout on the socket(s) being ready. The task will be
1117 * scheduled for execution once either the delay has expired or any of
1118 * the socket operations is ready. This is the most general
1119 * function of the "add" family. Note that the "prerequisite_task"
1120 * must be satisfied in addition to any of the other conditions. In
1121 * other words, the task will be started when
1123 * (prerequisite-run)
1127 * || (shutdown-active && run-on-shutdown) )
1130 * @param sched scheduler to use
1131 * @param prio how important is this task?
1132 * @param prerequisite_task run this task after the task with the given
1133 * task identifier completes (and any of our other
1134 * conditions, such as delay, read or write-readiness
1135 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1136 * on completion of other tasks.
1137 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1138 * which means that the task will only be run after we receive SIGTERM
1139 * @param rs set of file descriptors we want to read (can be NULL)
1140 * @param ws set of file descriptors we want to write (can be NULL)
1141 * @param task main function of the task
1142 * @param task_cls closure of task
1143 * @return unique task identifier for the job
1144 * only valid until "task" is started!
1146 GNUNET_SCHEDULER_TaskIdentifier
1147 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1148 enum GNUNET_SCHEDULER_Priority prio,
1149 GNUNET_SCHEDULER_TaskIdentifier
1151 struct GNUNET_TIME_Relative delay,
1152 const struct GNUNET_NETWORK_FDSet * rs,
1153 const struct GNUNET_NETWORK_FDSet * ws,
1154 GNUNET_SCHEDULER_Task task, void *task_cls)
1158 void *backtrace_array[MAX_TRACE_DEPTH];
1161 GNUNET_assert (NULL != task);
1162 t = GNUNET_malloc (sizeof (struct Task));
1164 t->callback_cls = task_cls;
1166 t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1167 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1171 t->read_set = GNUNET_NETWORK_fdset_create ();
1172 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1176 t->write_set = GNUNET_NETWORK_fdset_create ();
1177 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1179 t->id = ++sched->last_id;
1181 t->start_time = GNUNET_TIME_absolute_get ();
1183 t->prereq_id = prerequisite_task;
1184 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1186 check_priority ((prio ==
1187 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1189 t->next = sched->pending;
1192 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1193 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1198 /* end of scheduler.c */