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"
36 * Use lsof to generate file descriptor reports on select error?
37 * (turn off for stable releases).
39 #define USE_LSOF GNUNET_YES
42 * Obtain trace information for all scheduler calls that schedule tasks.
44 #define EXECINFO GNUNET_NO
47 * Depth of the traces collected via EXECINFO.
49 #define MAX_TRACE_DEPTH 50
52 #define DEBUG_TASKS GNUNET_NO
55 * Should we figure out which tasks are delayed for a while
56 * before they are run? (Consider using in combination with EXECINFO).
58 #define PROFILE_DELAYS GNUNET_NO
61 * Task that were in the queue for longer than this are reported if
62 * PROFILE_DELAYS is active.
64 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
67 * Linked list of pending tasks.
72 * This is a linked list.
77 * Function to run when ready.
79 GNUNET_SCHEDULER_Task callback;
82 * Closure for the callback.
87 * Set of file descriptors this task is waiting
88 * for for reading. Once ready, this is updated
89 * to reflect the set of file descriptors ready
92 struct GNUNET_NETWORK_FDSet *read_set;
95 * Set of file descriptors this task is waiting for for writing.
96 * Once ready, this is updated to reflect the set of file
97 * descriptors ready for operation.
99 struct GNUNET_NETWORK_FDSet *write_set;
102 * Unique task identifier.
104 GNUNET_SCHEDULER_TaskIdentifier id;
107 * Identifier of a prerequisite task.
109 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
112 * Absolute timeout value for the task, or
113 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
115 struct GNUNET_TIME_Absolute timeout;
119 * When was the task scheduled?
121 struct GNUNET_TIME_Absolute start_time;
125 * Why is the task ready? Set after task is added to ready queue.
126 * Initially set to zero. All reasons that have already been
127 * satisfied (i.e. read or write ready) will be set over time.
129 enum GNUNET_SCHEDULER_Reason reason;
134 enum GNUNET_SCHEDULER_Priority priority;
138 * Array of strings which make up a backtrace from the point when this
139 * task was scheduled (essentially, who scheduled the task?)
141 char **backtrace_strings;
144 * Size of the backtrace_strings array
146 int num_backtrace_strings;
153 * Handle for the scheduling service.
155 struct GNUNET_SCHEDULER_Handle
159 * List of tasks waiting for an event.
161 struct Task *pending;
164 * ID of the task that is running right now.
166 struct Task *active_task;
169 * List of tasks ready to run right now,
170 * grouped by importance.
172 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
175 * Identity of the last task queued. Incremented for each task to
176 * generate a unique task ID (it is virtually impossible to start
177 * more than 2^64 tasks during the lifetime of a process).
179 GNUNET_SCHEDULER_TaskIdentifier last_id;
182 * Highest number so that all tasks with smaller identifiers
183 * have already completed. Also the lowest number of a task
184 * still waiting to be executed.
186 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
189 * Number of tasks on the ready list.
191 unsigned int ready_count;
194 * How many tasks have we run so far?
196 unsigned long long tasks_run;
199 * Priority of the task running right now. Only
200 * valid while a task is running.
202 enum GNUNET_SCHEDULER_Priority current_priority;
205 * How 'nice' are we right now?
213 * Check that the given priority is legal (and return it).
215 * @param p priority value to check
216 * @return p on success, 0 on error
218 static enum GNUNET_SCHEDULER_Priority
219 check_priority (enum GNUNET_SCHEDULER_Priority p)
221 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
224 return 0; /* make compiler happy */
229 * Is a task with this identifier still pending? Also updates
230 * "lowest_pending_id" as a side-effect (for faster checks in the
231 * future), but only if the return value is "GNUNET_NO" (and
232 * the "lowest_pending_id" check failed).
234 * @param sched the scheduler
235 * @param id which task are we checking for
236 * @return GNUNET_YES if so, GNUNET_NO if not
239 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
240 GNUNET_SCHEDULER_TaskIdentifier id)
243 enum GNUNET_SCHEDULER_Priority p;
244 GNUNET_SCHEDULER_TaskIdentifier min;
246 if (id < sched->lowest_pending_id)
248 min = -1; /* maximum value */
249 pos = sched->pending;
258 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
260 pos = sched->ready[p];
270 sched->lowest_pending_id = min;
276 * Update all sets and timeout for select.
278 * @param sched the scheduler
279 * @param rs read-set, set to all FDs we would like to read (updated)
280 * @param ws write-set, set to all FDs we would like to write (updated)
281 * @param timeout next timeout (updated)
284 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
285 struct GNUNET_NETWORK_FDSet *rs,
286 struct GNUNET_NETWORK_FDSet *ws,
287 struct GNUNET_TIME_Relative *timeout)
291 pos = sched->pending;
294 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
295 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
301 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
303 struct GNUNET_TIME_Relative to;
305 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
306 if (timeout->value > to.value)
309 /* FIXME: this is a very expensive (9% of runtime for some
310 benchmarks!) way to merge the bit sets; specializing
311 the common case where we only have one bit in the pos's
312 set should improve performance dramatically! */
313 if (pos->read_set != NULL)
314 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
315 if (pos->write_set != NULL)
316 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
317 if (pos->reason != 0)
318 *timeout = GNUNET_TIME_UNIT_ZERO;
325 * Check if the ready set overlaps with the set we want to have ready.
326 * If so, update the want set (set all FDs that are ready). If not,
329 * @param ready set that is ready
330 * @param want set that we want to be ready
331 * @return GNUNET_YES if there was some overlap
334 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
335 struct GNUNET_NETWORK_FDSet *want)
339 /* FIXME: this is a very expensive (10% of runtime for some
340 benchmarks!) way to merge the bit sets; specializing
341 the common case where we only have one bit in the pos's
342 set should improve performance dramatically! */
343 if (GNUNET_NETWORK_fdset_overlap (ready, want))
345 /* copy all over (yes, there maybe unrelated bits,
346 but this should not hurt well-written clients) */
347 GNUNET_NETWORK_fdset_copy (want, ready);
355 * Check if the given task is eligible to run now.
356 * Also set the reason why it is eligible.
358 * @param sched the scheduler
359 * @param task task to check if it is ready
360 * @param now the current time
361 * @param rs set of FDs ready for reading
362 * @param ws set of FDs ready for writing
363 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
366 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
368 struct GNUNET_TIME_Absolute now,
369 const struct GNUNET_NETWORK_FDSet *rs,
370 const struct GNUNET_NETWORK_FDSet *ws)
372 if (now.value >= task->timeout.value)
373 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
374 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
375 (rs != NULL) && (set_overlaps (rs, task->read_set)))
376 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
377 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
378 (ws != NULL) && (set_overlaps (ws, task->write_set)))
379 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
380 if (task->reason == 0)
381 return GNUNET_NO; /* not ready */
382 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
384 if (GNUNET_YES == is_pending (sched, task->prereq_id))
385 return GNUNET_NO; /* prereq waiting */
386 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
393 * Put a task that is ready for execution into the ready queue.
395 * @param handle the scheduler
396 * @param task task ready for execution
399 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
401 enum GNUNET_SCHEDULER_Priority p = task->priority;
402 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
403 p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
404 task->next = handle->ready[check_priority (p)];
405 handle->ready[check_priority (p)] = task;
406 handle->ready_count++;
411 * Check which tasks are ready and move them
412 * to the respective ready queue.
414 * @param handle the scheduler
415 * @param rs FDs ready for reading
416 * @param ws FDs ready for writing
419 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
420 const struct GNUNET_NETWORK_FDSet *rs,
421 const struct GNUNET_NETWORK_FDSet *ws)
426 struct GNUNET_TIME_Absolute now;
428 now = GNUNET_TIME_absolute_get ();
430 pos = handle->pending;
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Checking readiness of task: %llu / %p\n",
436 pos->id, pos->callback_cls);
439 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
442 handle->pending = next;
445 queue_ready_task (handle, pos);
456 * Request the shutdown of a scheduler. Marks all currently
457 * pending tasks as ready because of shutdown. This will
458 * cause all tasks to run (as soon as possible, respecting
459 * priorities and prerequisite tasks). Note that tasks
460 * scheduled AFTER this call may still be delayed arbitrarily.
462 * @param sched the scheduler
465 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
470 pos = sched->pending;
473 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
474 /* we don't move the task into the ready queue yet; check_ready
475 will do that later, possibly adding additional
479 for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
481 pos = sched->ready[i];
484 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
485 /* we don't move the task into the ready queue yet; check_ready
486 will do that later, possibly adding additional
495 * Destroy a task (release associated resources)
497 * @param t task to destroy
500 destroy_task (struct Task *t)
502 if (NULL != t->read_set)
503 GNUNET_NETWORK_fdset_destroy (t->read_set);
504 if (NULL != t->write_set)
505 GNUNET_NETWORK_fdset_destroy (t->write_set);
507 GNUNET_free (t->backtrace_strings);
514 * Run at least one task in the highest-priority queue that is not
515 * empty. Keep running tasks until we are either no longer running
516 * "URGENT" tasks or until we have at least one "pending" task (which
517 * may become ready, hence we should select on it). Naturally, if
518 * there are no more ready tasks, we also return.
520 * @param sched the scheduler
523 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
525 enum GNUNET_SCHEDULER_Priority p;
527 struct GNUNET_SCHEDULER_TaskContext tc;
531 if (sched->ready_count == 0)
533 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
534 /* yes, p>0 is correct, 0 is "KEEP" which should
535 always be an empty queue (see assertion)! */
536 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
538 pos = sched->ready[p];
542 GNUNET_assert (pos != NULL); /* ready_count wrong? */
543 sched->ready[p] = pos->next;
544 sched->ready_count--;
545 if (sched->current_priority != pos->priority)
547 sched->current_priority = pos->priority;
548 (void) GNUNET_OS_set_process_priority (0, pos->priority);
550 sched->active_task = pos;
552 if (GNUNET_TIME_absolute_get_duration (pos->start_time).value >
553 DELAY_THRESHOLD.value)
555 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
556 "Task %u took %llums to be scheduled\n",
558 (unsigned long long) GNUNET_TIME_absolute_get_duration (pos->start_time).value);
562 tc.reason = pos->reason;
563 tc.read_ready = pos->read_set;
564 tc.write_ready = pos->write_set;
566 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
567 "Running task: %llu / %p\n", pos->id, pos->callback_cls);
569 pos->callback (pos->callback_cls, &tc);
572 for (i=0;i<pos->num_backtrace_strings;i++)
573 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
574 "Task %u trace %d: %s\n",
577 pos->backtrace_strings[i]);
579 sched->active_task = NULL;
583 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
587 * Pipe used to communicate shutdown via signal.
589 static struct GNUNET_DISK_PipeHandle *sigpipe;
593 * Signal handler called for signals that should cause us to shutdown.
596 sighandler_shutdown ()
600 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
601 (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
607 * Initialize and run scheduler. This function will return when all
608 * tasks have completed. On systems with signals, receiving a SIGTERM
609 * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
610 * to be run after the active task is complete. As a result, SIGTERM
611 * causes all active tasks to be scheduled with reason
612 * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
613 * afterwards will execute normally!). Note that any particular signal
614 * will only shut down one scheduler; applications should always only
615 * create a single scheduler.
617 * @param task task to run immediately
618 * @param task_cls closure of task
621 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
623 struct GNUNET_SCHEDULER_Handle sched;
624 struct GNUNET_NETWORK_FDSet *rs;
625 struct GNUNET_NETWORK_FDSet *ws;
626 struct GNUNET_TIME_Relative timeout;
628 struct GNUNET_SIGNAL_Context *shc_int;
629 struct GNUNET_SIGNAL_Context *shc_term;
630 struct GNUNET_SIGNAL_Context *shc_quit;
631 struct GNUNET_SIGNAL_Context *shc_hup;
632 unsigned long long last_tr;
633 unsigned int busy_wait_warning;
634 const struct GNUNET_DISK_FileHandle *pr;
637 rs = GNUNET_NETWORK_fdset_create ();
638 ws = GNUNET_NETWORK_fdset_create ();
639 GNUNET_assert (sigpipe == NULL);
640 sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
641 GNUNET_assert (sigpipe != NULL);
642 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
643 GNUNET_assert (pr != NULL);
644 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
645 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
647 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
648 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
650 memset (&sched, 0, sizeof (sched));
651 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
652 GNUNET_SCHEDULER_add_continuation (&sched,
655 GNUNET_SCHEDULER_REASON_STARTUP);
657 busy_wait_warning = 0;
658 while ((sched.pending != NULL) || (sched.ready_count > 0))
660 GNUNET_NETWORK_fdset_zero (rs);
661 GNUNET_NETWORK_fdset_zero (ws);
662 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
663 update_sets (&sched, rs, ws, &timeout);
664 GNUNET_NETWORK_fdset_handle_set (rs, pr);
665 if (sched.ready_count > 0)
667 /* no blocking, more work already ready! */
668 timeout = GNUNET_TIME_UNIT_ZERO;
670 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
671 if (ret == GNUNET_SYSERR)
676 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
680 snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
689 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
691 /* consume the signal */
692 GNUNET_DISK_file_read (pr, &c, sizeof (c));
693 /* mark all active tasks as ready due to shutdown */
694 GNUNET_SCHEDULER_shutdown (&sched);
696 if (last_tr == sched.tasks_run)
702 last_tr = sched.tasks_run;
703 busy_wait_warning = 0;
705 if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
707 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
708 _("Looks like we're busy waiting...\n"));
709 sleep (1); /* mitigate */
711 check_ready (&sched, rs, ws);
714 GNUNET_SIGNAL_handler_uninstall (shc_int);
715 GNUNET_SIGNAL_handler_uninstall (shc_term);
717 GNUNET_SIGNAL_handler_uninstall (shc_quit);
718 GNUNET_SIGNAL_handler_uninstall (shc_hup);
720 GNUNET_DISK_pipe_close (sigpipe);
722 GNUNET_NETWORK_fdset_destroy (rs);
723 GNUNET_NETWORK_fdset_destroy (ws);
728 * Obtain the reason code for why the current task was
729 * started. Will return the same value as
730 * the GNUNET_SCHEDULER_TaskContext's reason field.
732 * @param sched scheduler to query
733 * @return reason(s) why the current task is run
735 enum GNUNET_SCHEDULER_Reason
736 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
738 return sched->active_task->reason;
743 * Get information about the current load of this scheduler. Use this
744 * function to determine if an elective task should be added or simply
745 * dropped (if the decision should be made based on the number of
746 * tasks ready to run).
748 * @param sched scheduler to query
749 * @param p priority level to look at
750 * @return number of tasks pending right now
753 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
754 enum GNUNET_SCHEDULER_Priority p)
759 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
760 return sched->ready_count;
761 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
762 p = sched->current_priority;
764 pos = sched->ready[p];
775 * Cancel the task with the specified identifier.
776 * The task must not yet have run.
778 * @param sched scheduler to use
779 * @param task id of the task to cancel
780 * @return original closure of the task
783 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
784 GNUNET_SCHEDULER_TaskIdentifier task)
788 enum GNUNET_SCHEDULER_Priority p;
806 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
813 sched->ready_count--;
823 sched->pending = t->next;
825 sched->ready[p] = t->next;
828 prev->next = t->next;
829 ret = t->callback_cls;
831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832 "Canceling task: %llu / %p\n", task, t->callback_cls);
840 * Continue the current execution with the given function. This is
841 * similar to the other "add" functions except that there is no delay
842 * and the reason code can be specified.
844 * @param sched scheduler to use
845 * @param task main function of the task
846 * @param task_cls closure for 'main'
847 * @param reason reason for task invocation
850 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
851 GNUNET_SCHEDULER_Task task,
853 enum GNUNET_SCHEDULER_Reason reason)
857 void *backtrace_array[50];
859 t = GNUNET_malloc (sizeof (struct Task));
861 t->num_backtrace_strings = backtrace(backtrace_array, 50);
862 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
865 t->callback_cls = task_cls;
866 t->id = ++sched->last_id;
868 t->start_time = GNUNET_TIME_absolute_get ();
871 t->priority = sched->current_priority;
873 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
874 "Adding continuation task: %llu / %p\n",
875 t->id, t->callback_cls);
877 queue_ready_task (sched, t);
883 * Schedule a new task to be run after the specified prerequisite task
884 * has completed. It will be run with the priority of the calling
887 * @param sched scheduler to use
888 * @param prerequisite_task run this task after the task with the given
889 * task identifier completes (and any of our other
890 * conditions, such as delay, read or write-readiness
891 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
892 * on completion of other tasks (this will cause the task to run as
894 * @param task main function of the task
895 * @param task_cls closure of task
896 * @return unique task identifier for the job
897 * only valid until "task" is started!
899 GNUNET_SCHEDULER_TaskIdentifier
900 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
901 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
902 GNUNET_SCHEDULER_Task task, void *task_cls)
904 return GNUNET_SCHEDULER_add_select (sched,
905 GNUNET_SCHEDULER_PRIORITY_KEEP,
907 GNUNET_TIME_UNIT_ZERO,
908 NULL, NULL, task, task_cls);
913 * Schedule a new task to be run with a specified priority.
915 * @param sched scheduler to use
916 * @param prio how important is the new task?
917 * @param task main function of the task
918 * @param task_cls closure of task
919 * @return unique task identifier for the job
920 * only valid until "task" is started!
922 GNUNET_SCHEDULER_TaskIdentifier
923 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
924 enum GNUNET_SCHEDULER_Priority prio,
925 GNUNET_SCHEDULER_Task task,
928 return GNUNET_SCHEDULER_add_select (sched,
930 GNUNET_SCHEDULER_NO_TASK,
931 GNUNET_TIME_UNIT_ZERO,
932 NULL, NULL, task, task_cls);
938 * Schedule a new task to be run with a specified delay. The task
939 * will be scheduled for execution once the delay has expired. It
940 * will be run with the priority of the calling task.
942 * @param sched scheduler to use
943 * @param delay when should this operation time out? Use
944 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
945 * @param task main function of the task
946 * @param task_cls closure of task
947 * @return unique task identifier for the job
948 * only valid until "task" is started!
950 GNUNET_SCHEDULER_TaskIdentifier
951 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
952 struct GNUNET_TIME_Relative delay,
953 GNUNET_SCHEDULER_Task task, void *task_cls)
955 return GNUNET_SCHEDULER_add_select (sched,
956 GNUNET_SCHEDULER_PRIORITY_KEEP,
957 GNUNET_SCHEDULER_NO_TASK, delay,
958 NULL, NULL, task, task_cls);
964 * Schedule a new task to be run as soon as possible. The task
965 * will be run with the priority of the calling task.
967 * @param sched scheduler to use
968 * @param task main function of the task
969 * @param task_cls closure of task
970 * @return unique task identifier for the job
971 * only valid until "task" is started!
973 GNUNET_SCHEDULER_TaskIdentifier
974 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
975 GNUNET_SCHEDULER_Task task,
978 return GNUNET_SCHEDULER_add_select (sched,
979 GNUNET_SCHEDULER_PRIORITY_KEEP,
980 GNUNET_SCHEDULER_NO_TASK,
981 GNUNET_TIME_UNIT_ZERO,
982 NULL, NULL, task, task_cls);
988 * Schedule a new task to be run with a specified delay or when the
989 * specified file descriptor is ready for reading. The delay can be
990 * used as a timeout on the socket being ready. The task will be
991 * scheduled for execution once either the delay has expired or the
992 * socket operation is ready. It will be run with the priority of
995 * @param sched scheduler to use
996 * @param delay when should this operation time out? Use
997 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
998 * @param rfd read file-descriptor
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_read_net (struct GNUNET_SCHEDULER_Handle * sched,
1006 struct GNUNET_TIME_Relative delay,
1007 struct GNUNET_NETWORK_Handle * rfd,
1008 GNUNET_SCHEDULER_Task task, void *task_cls)
1010 struct GNUNET_NETWORK_FDSet *rs;
1011 GNUNET_SCHEDULER_TaskIdentifier ret;
1013 GNUNET_assert (rfd != NULL);
1014 rs = GNUNET_NETWORK_fdset_create ();
1015 GNUNET_NETWORK_fdset_set (rs, rfd);
1016 ret = GNUNET_SCHEDULER_add_select (sched,
1017 GNUNET_SCHEDULER_PRIORITY_KEEP,
1018 GNUNET_SCHEDULER_NO_TASK,
1019 delay, rs, NULL, task, task_cls);
1020 GNUNET_NETWORK_fdset_destroy (rs);
1026 * Schedule a new task to be run with a specified delay or when the
1027 * specified file descriptor is ready for writing. The delay can be
1028 * used as a timeout on the socket being ready. The task will be
1029 * scheduled for execution once either the delay has expired or the
1030 * socket operation is ready. It will be run with the priority of
1033 * @param sched scheduler to use
1034 * @param delay when should this operation time out? Use
1035 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1036 * @param wfd write file-descriptor
1037 * @param task main function of the task
1038 * @param task_cls closure of task
1039 * @return unique task identifier for the job
1040 * only valid until "task" is started!
1042 GNUNET_SCHEDULER_TaskIdentifier
1043 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
1044 struct GNUNET_TIME_Relative delay,
1045 struct GNUNET_NETWORK_Handle * wfd,
1046 GNUNET_SCHEDULER_Task task, void *task_cls)
1048 struct GNUNET_NETWORK_FDSet *ws;
1049 GNUNET_SCHEDULER_TaskIdentifier ret;
1051 GNUNET_assert (wfd != NULL);
1052 ws = GNUNET_NETWORK_fdset_create ();
1053 GNUNET_NETWORK_fdset_set (ws, wfd);
1054 ret = GNUNET_SCHEDULER_add_select (sched,
1055 GNUNET_SCHEDULER_PRIORITY_KEEP,
1056 GNUNET_SCHEDULER_NO_TASK, delay,
1057 NULL, ws, task, task_cls);
1058 GNUNET_NETWORK_fdset_destroy (ws);
1064 * Schedule a new task to be run with a specified delay or when the
1065 * specified file descriptor is ready for reading. The delay can be
1066 * used as a timeout on the socket being ready. The task will be
1067 * scheduled for execution once either the delay has expired or the
1068 * socket operation is ready. It will be run with the priority of
1071 * @param sched scheduler to use
1072 * @param delay when should this operation time out? Use
1073 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1074 * @param rfd read file-descriptor
1075 * @param task main function of the task
1076 * @param task_cls closure of task
1077 * @return unique task identifier for the job
1078 * only valid until "task" is started!
1080 GNUNET_SCHEDULER_TaskIdentifier
1081 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1082 struct GNUNET_TIME_Relative delay,
1083 const struct GNUNET_DISK_FileHandle * rfd,
1084 GNUNET_SCHEDULER_Task task, void *task_cls)
1086 struct GNUNET_NETWORK_FDSet *rs;
1087 GNUNET_SCHEDULER_TaskIdentifier ret;
1089 GNUNET_assert (rfd != NULL);
1090 rs = GNUNET_NETWORK_fdset_create ();
1091 GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1092 ret = GNUNET_SCHEDULER_add_select (sched,
1093 GNUNET_SCHEDULER_PRIORITY_KEEP,
1094 GNUNET_SCHEDULER_NO_TASK, delay,
1095 rs, NULL, task, task_cls);
1096 GNUNET_NETWORK_fdset_destroy (rs);
1102 * Schedule a new task to be run with a specified delay or when the
1103 * specified file descriptor is ready for writing. The delay can be
1104 * used as a timeout on the socket being ready. The task will be
1105 * scheduled for execution once either the delay has expired or the
1106 * socket operation is ready. It will be run with the priority of
1109 * @param sched scheduler to use
1110 * @param delay when should this operation time out? Use
1111 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1112 * @param wfd write file-descriptor
1113 * @param task main function of the task
1114 * @param task_cls closure of task
1115 * @return unique task identifier for the job
1116 * only valid until "task" is started!
1118 GNUNET_SCHEDULER_TaskIdentifier
1119 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1120 struct GNUNET_TIME_Relative delay,
1121 const struct GNUNET_DISK_FileHandle * wfd,
1122 GNUNET_SCHEDULER_Task task, void *task_cls)
1124 struct GNUNET_NETWORK_FDSet *ws;
1125 GNUNET_SCHEDULER_TaskIdentifier ret;
1127 GNUNET_assert (wfd != NULL);
1128 ws = GNUNET_NETWORK_fdset_create ();
1129 GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1130 ret = GNUNET_SCHEDULER_add_select (sched,
1131 GNUNET_SCHEDULER_PRIORITY_KEEP,
1132 GNUNET_SCHEDULER_NO_TASK,
1133 delay, NULL, ws, task, task_cls);
1134 GNUNET_NETWORK_fdset_destroy (ws);
1141 * Schedule a new task to be run with a specified delay or when any of
1142 * the specified file descriptor sets is ready. The delay can be used
1143 * as a timeout on the socket(s) being ready. The task will be
1144 * scheduled for execution once either the delay has expired or any of
1145 * the socket operations is ready. This is the most general
1146 * function of the "add" family. Note that the "prerequisite_task"
1147 * must be satisfied in addition to any of the other conditions. In
1148 * other words, the task will be started when
1150 * (prerequisite-run)
1154 * || (shutdown-active && run-on-shutdown) )
1157 * @param sched scheduler to use
1158 * @param prio how important is this task?
1159 * @param prerequisite_task run this task after the task with the given
1160 * task identifier completes (and any of our other
1161 * conditions, such as delay, read or write-readiness
1162 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1163 * on completion of other tasks.
1164 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1165 * which means that the task will only be run after we receive SIGTERM
1166 * @param rs set of file descriptors we want to read (can be NULL)
1167 * @param ws set of file descriptors we want to write (can be NULL)
1168 * @param task main function of the task
1169 * @param task_cls closure of task
1170 * @return unique task identifier for the job
1171 * only valid until "task" is started!
1173 GNUNET_SCHEDULER_TaskIdentifier
1174 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1175 enum GNUNET_SCHEDULER_Priority prio,
1176 GNUNET_SCHEDULER_TaskIdentifier
1178 struct GNUNET_TIME_Relative delay,
1179 const struct GNUNET_NETWORK_FDSet * rs,
1180 const struct GNUNET_NETWORK_FDSet * ws,
1181 GNUNET_SCHEDULER_Task task, void *task_cls)
1185 void *backtrace_array[MAX_TRACE_DEPTH];
1188 GNUNET_assert (NULL != task);
1189 t = GNUNET_malloc (sizeof (struct Task));
1191 t->callback_cls = task_cls;
1193 t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1194 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1198 t->read_set = GNUNET_NETWORK_fdset_create ();
1199 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1203 t->write_set = GNUNET_NETWORK_fdset_create ();
1204 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1206 t->id = ++sched->last_id;
1208 t->start_time = GNUNET_TIME_absolute_get ();
1210 t->prereq_id = prerequisite_task;
1211 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1213 check_priority ((prio ==
1214 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1216 t->next = sched->pending;
1219 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1220 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1225 for (i=0;i<t->num_backtrace_strings;i++)
1226 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1227 "Task %u trace %d: %s\n",
1230 t->backtrace_strings[i]);
1235 /* end of scheduler.c */