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"
33 #define EXECINFO GNUNET_NO
34 #define MAX_TRACE_DEPTH 50
37 #define DEBUG_TASKS GNUNET_NO
40 * Linked list of pending tasks.
45 * This is a linked list.
50 * Function to run when ready.
52 GNUNET_SCHEDULER_Task callback;
55 * Closure for the callback.
60 * Set of file descriptors this task is waiting
61 * for for reading. Once ready, this is updated
62 * to reflect the set of file descriptors ready
65 struct GNUNET_NETWORK_FDSet *read_set;
68 * Set of file descriptors this task is waiting for for writing.
69 * Once ready, this is updated to reflect the set of file
70 * descriptors ready for operation.
72 struct GNUNET_NETWORK_FDSet *write_set;
75 * Unique task identifier.
77 GNUNET_SCHEDULER_TaskIdentifier id;
80 * Identifier of a prerequisite task.
82 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
85 * Absolute timeout value for the task, or
86 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
88 struct GNUNET_TIME_Absolute timeout;
91 * Why is the task ready? Set after task is added to ready queue.
92 * Initially set to zero. All reasons that have already been
93 * satisfied (i.e. read or write ready) will be set over time.
95 enum GNUNET_SCHEDULER_Reason reason;
100 enum GNUNET_SCHEDULER_Priority priority;
104 * Array of strings which make up a backtrace from the point when this
105 * task was scheduled (essentially, who scheduled the task?)
107 char **backtrace_strings;
110 * Size of the backtrace_strings array
112 int num_backtrace_strings;
119 * Handle for the scheduling service.
121 struct GNUNET_SCHEDULER_Handle
125 * List of tasks waiting for an event.
127 struct Task *pending;
130 * ID of the task that is running right now.
132 struct Task *active_task;
135 * List of tasks ready to run right now,
136 * grouped by importance.
138 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
141 * Identity of the last task queued. Incremented for each task to
142 * generate a unique task ID (it is virtually impossible to start
143 * more than 2^64 tasks during the lifetime of a process).
145 GNUNET_SCHEDULER_TaskIdentifier last_id;
148 * Highest number so that all tasks with smaller identifiers
149 * have already completed. Also the lowest number of a task
150 * still waiting to be executed.
152 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
155 * Number of tasks on the ready list.
157 unsigned int ready_count;
160 * How many tasks have we run so far?
162 unsigned long long tasks_run;
165 * Priority of the task running right now. Only
166 * valid while a task is running.
168 enum GNUNET_SCHEDULER_Priority current_priority;
174 * Check that the given priority is legal (and return it).
176 * @param p priority value to check
177 * @return p on success, 0 on error
179 static enum GNUNET_SCHEDULER_Priority
180 check_priority (enum GNUNET_SCHEDULER_Priority p)
182 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
185 return 0; /* make compiler happy */
190 * Is a task with this identifier still pending? Also updates
191 * "lowest_pending_id" as a side-effect (for faster checks in the
192 * future), but only if the return value is "GNUNET_NO" (and
193 * the "lowest_pending_id" check failed).
195 * @param sched the scheduler
196 * @param id which task are we checking for
197 * @return GNUNET_YES if so, GNUNET_NO if not
200 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
201 GNUNET_SCHEDULER_TaskIdentifier id)
204 enum GNUNET_SCHEDULER_Priority p;
205 GNUNET_SCHEDULER_TaskIdentifier min;
207 if (id < sched->lowest_pending_id)
209 min = -1; /* maximum value */
210 pos = sched->pending;
219 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
221 pos = sched->ready[p];
231 sched->lowest_pending_id = min;
237 * Update all sets and timeout for select.
239 * @param sched the scheduler
240 * @param rs read-set, set to all FDs we would like to read (updated)
241 * @param ws write-set, set to all FDs we would like to write (updated)
242 * @param timeout next timeout (updated)
245 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
246 struct GNUNET_NETWORK_FDSet *rs,
247 struct GNUNET_NETWORK_FDSet *ws,
248 struct GNUNET_TIME_Relative *timeout)
252 pos = sched->pending;
255 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
256 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
262 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
264 struct GNUNET_TIME_Relative to;
266 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
267 if (timeout->value > to.value)
270 if (pos->read_set != NULL)
271 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
272 if (pos->write_set != NULL)
273 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
274 if (pos->reason != 0)
275 *timeout = GNUNET_TIME_UNIT_ZERO;
282 * Check if the ready set overlaps with the set we want to have ready.
283 * If so, update the want set (set all FDs that are ready). If not,
286 * @param ready set that is ready
287 * @param want set that we want to be ready
288 * @return GNUNET_YES if there was some overlap
291 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
292 struct GNUNET_NETWORK_FDSet *want)
296 if (GNUNET_NETWORK_fdset_overlap (ready, want))
298 /* copy all over (yes, there maybe unrelated bits,
299 but this should not hurt well-written clients) */
300 GNUNET_NETWORK_fdset_copy (want, ready);
308 * Check if the given task is eligible to run now.
309 * Also set the reason why it is eligible.
311 * @param sched the scheduler
312 * @param task task to check if it is ready
313 * @param now the current time
314 * @param rs set of FDs ready for reading
315 * @param ws set of FDs ready for writing
316 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
319 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
321 struct GNUNET_TIME_Absolute now,
322 const struct GNUNET_NETWORK_FDSet *rs,
323 const struct GNUNET_NETWORK_FDSet *ws)
325 if (now.value >= task->timeout.value)
326 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
327 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
328 (rs != NULL) && (set_overlaps (rs, task->read_set)))
329 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
330 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
331 (ws != NULL) && (set_overlaps (ws, task->write_set)))
332 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
333 if (task->reason == 0)
334 return GNUNET_NO; /* not ready */
335 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
337 if (GNUNET_YES == is_pending (sched, task->prereq_id))
338 return GNUNET_NO; /* prereq waiting */
339 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
346 * Put a task that is ready for execution into the ready queue.
348 * @param handle the scheduler
349 * @param task task ready for execution
352 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
354 enum GNUNET_SCHEDULER_Priority p = task->priority;
355 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
356 p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
357 task->next = handle->ready[check_priority (p)];
358 handle->ready[check_priority (p)] = task;
359 handle->ready_count++;
364 * Check which tasks are ready and move them
365 * to the respective ready queue.
367 * @param handle the scheduler
368 * @param rs FDs ready for reading
369 * @param ws FDs ready for writing
372 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
373 const struct GNUNET_NETWORK_FDSet *rs,
374 const struct GNUNET_NETWORK_FDSet *ws)
379 struct GNUNET_TIME_Absolute now;
381 now = GNUNET_TIME_absolute_get ();
383 pos = handle->pending;
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
388 "Checking readiness of task: %llu / %p\n",
389 pos->id, pos->callback_cls);
392 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
395 handle->pending = next;
398 queue_ready_task (handle, pos);
409 * Request the shutdown of a scheduler. Marks all currently
410 * pending tasks as ready because of shutdown. This will
411 * cause all tasks to run (as soon as possible, respecting
412 * priorities and prerequisite tasks). Note that tasks
413 * scheduled AFTER this call may still be delayed arbitrarily.
415 * @param sched the scheduler
418 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
423 pos = sched->pending;
426 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
427 /* we don't move the task into the ready queue yet; check_ready
428 will do that later, possibly adding additional
432 for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
434 pos = sched->ready[i];
437 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
438 /* we don't move the task into the ready queue yet; check_ready
439 will do that later, possibly adding additional
448 * Destroy a task (release associated resources)
450 * @param t task to destroy
453 destroy_task (struct Task *t)
455 if (NULL != t->read_set)
456 GNUNET_NETWORK_fdset_destroy (t->read_set);
457 if (NULL != t->write_set)
458 GNUNET_NETWORK_fdset_destroy (t->write_set);
460 GNUNET_free (t->backtrace_strings);
467 * Run at least one task in the highest-priority queue that is not
468 * empty. Keep running tasks until we are either no longer running
469 * "URGENT" tasks or until we have at least one "pending" task (which
470 * may become ready, hence we should select on it). Naturally, if
471 * there are no more ready tasks, we also return.
473 * @param sched the scheduler
476 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
478 enum GNUNET_SCHEDULER_Priority p;
480 struct GNUNET_SCHEDULER_TaskContext tc;
484 if (sched->ready_count == 0)
486 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
487 /* yes, p>0 is correct, 0 is "KEEP" which should
488 always be an empty queue (see assertion)! */
489 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
491 pos = sched->ready[p];
495 GNUNET_assert (pos != NULL); /* ready_count wrong? */
496 sched->ready[p] = pos->next;
497 sched->ready_count--;
498 sched->current_priority = pos->priority;
499 sched->active_task = pos;
501 tc.reason = pos->reason;
502 tc.read_ready = pos->read_set;
503 tc.write_ready = pos->write_set;
504 pos->callback (pos->callback_cls, &tc);
506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
507 "Running task: %llu / %p\n", pos->id, pos->callback_cls);
509 sched->active_task = NULL;
513 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
517 * Pipe used to communicate shutdown via signal.
519 static struct GNUNET_DISK_PipeHandle *sigpipe;
523 * Signal handler called for signals that should cause us to shutdown.
526 sighandler_shutdown ()
530 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
531 (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
537 * Initialize and run scheduler. This function will return when all
538 * tasks have completed. On systems with signals, receiving a SIGTERM
539 * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
540 * to be run after the active task is complete. As a result, SIGTERM
541 * causes all active tasks to be scheduled with reason
542 * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
543 * afterwards will execute normally!). Note that any particular signal
544 * will only shut down one scheduler; applications should always only
545 * create a single scheduler.
547 * @param task task to run immediately
548 * @param task_cls closure of task
551 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
553 struct GNUNET_SCHEDULER_Handle sched;
554 struct GNUNET_NETWORK_FDSet *rs;
555 struct GNUNET_NETWORK_FDSet *ws;
556 struct GNUNET_TIME_Relative timeout;
558 struct GNUNET_SIGNAL_Context *shc_int;
559 struct GNUNET_SIGNAL_Context *shc_term;
560 struct GNUNET_SIGNAL_Context *shc_quit;
561 struct GNUNET_SIGNAL_Context *shc_hup;
562 unsigned long long last_tr;
563 unsigned int busy_wait_warning;
564 const struct GNUNET_DISK_FileHandle *pr;
567 rs = GNUNET_NETWORK_fdset_create ();
568 ws = GNUNET_NETWORK_fdset_create ();
569 GNUNET_assert (sigpipe == NULL);
570 sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
571 GNUNET_assert (sigpipe != NULL);
572 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
573 GNUNET_assert (pr != NULL);
574 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
575 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
577 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
578 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
580 memset (&sched, 0, sizeof (sched));
581 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
582 GNUNET_SCHEDULER_add_continuation (&sched,
585 GNUNET_SCHEDULER_REASON_STARTUP);
587 busy_wait_warning = 0;
588 while ((sched.pending != NULL) || (sched.ready_count > 0))
590 GNUNET_NETWORK_fdset_zero (rs);
591 GNUNET_NETWORK_fdset_zero (ws);
592 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
593 update_sets (&sched, rs, ws, &timeout);
594 GNUNET_NETWORK_fdset_handle_set (rs, pr);
595 if (sched.ready_count > 0)
597 /* no blocking, more work already ready! */
598 timeout = GNUNET_TIME_UNIT_ZERO;
600 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
601 if (ret == GNUNET_SYSERR)
605 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
608 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
610 /* consume the signal */
611 GNUNET_DISK_file_read (pr, &c, sizeof (c));
612 /* mark all active tasks as ready due to shutdown */
613 GNUNET_SCHEDULER_shutdown (&sched);
615 if (last_tr == sched.tasks_run)
621 last_tr = sched.tasks_run;
622 busy_wait_warning = 0;
624 if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
626 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
627 _("Looks like we're busy waiting...\n"));
628 sleep (1); /* mitigate */
630 check_ready (&sched, rs, ws);
633 GNUNET_SIGNAL_handler_uninstall (shc_int);
634 GNUNET_SIGNAL_handler_uninstall (shc_term);
636 GNUNET_SIGNAL_handler_uninstall (shc_quit);
637 GNUNET_SIGNAL_handler_uninstall (shc_hup);
639 GNUNET_DISK_pipe_close (sigpipe);
641 GNUNET_NETWORK_fdset_destroy (rs);
642 GNUNET_NETWORK_fdset_destroy (ws);
647 * Obtain the reason code for why the current task was
648 * started. Will return the same value as
649 * the GNUNET_SCHEDULER_TaskContext's reason field.
651 * @param sched scheduler to query
652 * @return reason(s) why the current task is run
654 enum GNUNET_SCHEDULER_Reason
655 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
657 return sched->active_task->reason;
662 * Get information about the current load of this scheduler. Use this
663 * function to determine if an elective task should be added or simply
664 * dropped (if the decision should be made based on the number of
665 * tasks ready to run).
667 * @param sched scheduler to query
668 * @param p priority level to look at
669 * @return number of tasks pending right now
672 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
673 enum GNUNET_SCHEDULER_Priority p)
678 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
679 return sched->ready_count;
680 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
681 p = sched->current_priority;
683 pos = sched->ready[p];
694 * Cancel the task with the specified identifier.
695 * The task must not yet have run.
697 * @param sched scheduler to use
698 * @param task id of the task to cancel
699 * @return original closure of the task
702 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
703 GNUNET_SCHEDULER_TaskIdentifier task)
707 enum GNUNET_SCHEDULER_Priority p;
723 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
730 sched->ready_count--;
740 sched->pending = t->next;
742 sched->ready[p] = t->next;
745 prev->next = t->next;
746 ret = t->callback_cls;
748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
749 "Canceling task: %llu / %p\n", task, t->callback_cls);
757 * Continue the current execution with the given function. This is
758 * similar to the other "add" functions except that there is no delay
759 * and the reason code can be specified.
761 * @param sched scheduler to use
762 * @param task main function of the task
763 * @param task_cls closure for 'main'
764 * @param reason reason for task invocation
767 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
768 GNUNET_SCHEDULER_Task task,
770 enum GNUNET_SCHEDULER_Reason reason)
774 void *backtrace_array[50];
776 t = GNUNET_malloc (sizeof (struct Task));
778 t->num_backtrace_strings = backtrace(backtrace_array, 50);
779 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
782 t->callback_cls = task_cls;
783 t->id = ++sched->last_id;
785 t->priority = sched->current_priority;
787 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
788 "Adding continuation task: %llu / %p\n",
789 t->id, t->callback_cls);
791 queue_ready_task (sched, t);
797 * Schedule a new task to be run after the specified prerequisite task
798 * has completed. It will be run with the priority of the calling
801 * @param sched scheduler to use
802 * @param prerequisite_task run this task after the task with the given
803 * task identifier completes (and any of our other
804 * conditions, such as delay, read or write-readiness
805 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
806 * on completion of other tasks (this will cause the task to run as
808 * @param task main function of the task
809 * @param task_cls closure of task
810 * @return unique task identifier for the job
811 * only valid until "task" is started!
813 GNUNET_SCHEDULER_TaskIdentifier
814 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
815 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
816 GNUNET_SCHEDULER_Task task, void *task_cls)
818 return GNUNET_SCHEDULER_add_select (sched,
819 GNUNET_SCHEDULER_PRIORITY_KEEP,
821 GNUNET_TIME_UNIT_ZERO,
822 NULL, NULL, task, task_cls);
827 * Schedule a new task to be run with a specified priority.
829 * @param sched scheduler to use
830 * @param prio how important is the new task?
831 * @param task main function of the task
832 * @param task_cls closure of task
833 * @return unique task identifier for the job
834 * only valid until "task" is started!
836 GNUNET_SCHEDULER_TaskIdentifier
837 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
838 enum GNUNET_SCHEDULER_Priority prio,
839 GNUNET_SCHEDULER_Task task,
842 return GNUNET_SCHEDULER_add_select (sched,
844 GNUNET_SCHEDULER_NO_TASK,
845 GNUNET_TIME_UNIT_ZERO,
846 NULL, NULL, task, task_cls);
852 * Schedule a new task to be run with a specified delay. The task
853 * will be scheduled for execution once the delay has expired. It
854 * will be run with the priority of the calling task.
856 * @param sched scheduler to use
857 * @param delay when should this operation time out? Use
858 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
859 * @param task main function of the task
860 * @param task_cls closure of task
861 * @return unique task identifier for the job
862 * only valid until "task" is started!
864 GNUNET_SCHEDULER_TaskIdentifier
865 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
866 struct GNUNET_TIME_Relative delay,
867 GNUNET_SCHEDULER_Task task, void *task_cls)
869 return GNUNET_SCHEDULER_add_select (sched,
870 GNUNET_SCHEDULER_PRIORITY_KEEP,
871 GNUNET_SCHEDULER_NO_TASK, delay,
872 NULL, NULL, task, task_cls);
878 * Schedule a new task to be run as soon as possible. The task
879 * will be run with the priority of the calling task.
881 * @param sched scheduler to use
882 * @param task main function of the task
883 * @param task_cls closure of task
884 * @return unique task identifier for the job
885 * only valid until "task" is started!
887 GNUNET_SCHEDULER_TaskIdentifier
888 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
889 GNUNET_SCHEDULER_Task task,
892 return GNUNET_SCHEDULER_add_select (sched,
893 GNUNET_SCHEDULER_PRIORITY_KEEP,
894 GNUNET_SCHEDULER_NO_TASK,
895 GNUNET_TIME_UNIT_ZERO,
896 NULL, NULL, task, task_cls);
902 * Schedule a new task to be run with a specified delay or when the
903 * specified file descriptor is ready for reading. The delay can be
904 * used as a timeout on the socket being ready. The task will be
905 * scheduled for execution once either the delay has expired or the
906 * socket operation is ready. It will be run with the priority of
909 * @param sched scheduler to use
910 * @param delay when should this operation time out? Use
911 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
912 * @param rfd read file-descriptor
913 * @param task main function of the task
914 * @param task_cls closure of task
915 * @return unique task identifier for the job
916 * only valid until "task" is started!
918 GNUNET_SCHEDULER_TaskIdentifier
919 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
920 struct GNUNET_TIME_Relative delay,
921 struct GNUNET_NETWORK_Handle * rfd,
922 GNUNET_SCHEDULER_Task task, void *task_cls)
924 struct GNUNET_NETWORK_FDSet *rs;
925 GNUNET_SCHEDULER_TaskIdentifier ret;
927 GNUNET_assert (rfd != NULL);
928 rs = GNUNET_NETWORK_fdset_create ();
929 GNUNET_NETWORK_fdset_set (rs, rfd);
930 ret = GNUNET_SCHEDULER_add_select (sched,
931 GNUNET_SCHEDULER_PRIORITY_KEEP,
932 GNUNET_SCHEDULER_NO_TASK,
933 delay, rs, NULL, task, task_cls);
934 GNUNET_NETWORK_fdset_destroy (rs);
940 * Schedule a new task to be run with a specified delay or when the
941 * specified file descriptor is ready for writing. The delay can be
942 * used as a timeout on the socket being ready. The task will be
943 * scheduled for execution once either the delay has expired or the
944 * socket operation is ready. It will be run with the priority of
947 * @param sched scheduler to use
948 * @param delay when should this operation time out? Use
949 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
950 * @param wfd write file-descriptor
951 * @param task main function of the task
952 * @param task_cls closure of task
953 * @return unique task identifier for the job
954 * only valid until "task" is started!
956 GNUNET_SCHEDULER_TaskIdentifier
957 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
958 struct GNUNET_TIME_Relative delay,
959 struct GNUNET_NETWORK_Handle * wfd,
960 GNUNET_SCHEDULER_Task task, void *task_cls)
962 struct GNUNET_NETWORK_FDSet *ws;
963 GNUNET_SCHEDULER_TaskIdentifier ret;
965 GNUNET_assert (wfd != NULL);
966 ws = GNUNET_NETWORK_fdset_create ();
967 GNUNET_NETWORK_fdset_set (ws, wfd);
968 ret = GNUNET_SCHEDULER_add_select (sched,
969 GNUNET_SCHEDULER_PRIORITY_KEEP,
970 GNUNET_SCHEDULER_NO_TASK, delay,
971 NULL, ws, task, task_cls);
972 GNUNET_NETWORK_fdset_destroy (ws);
978 * Schedule a new task to be run with a specified delay or when the
979 * specified file descriptor is ready for reading. The delay can be
980 * used as a timeout on the socket being ready. The task will be
981 * scheduled for execution once either the delay has expired or the
982 * socket operation is ready. It will be run with the priority of
985 * @param sched scheduler to use
986 * @param delay when should this operation time out? Use
987 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
988 * @param rfd read file-descriptor
989 * @param task main function of the task
990 * @param task_cls closure of task
991 * @return unique task identifier for the job
992 * only valid until "task" is started!
994 GNUNET_SCHEDULER_TaskIdentifier
995 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
996 struct GNUNET_TIME_Relative delay,
997 const struct GNUNET_DISK_FileHandle * rfd,
998 GNUNET_SCHEDULER_Task task, void *task_cls)
1000 struct GNUNET_NETWORK_FDSet *rs;
1001 GNUNET_SCHEDULER_TaskIdentifier ret;
1003 GNUNET_assert (rfd != NULL);
1004 rs = GNUNET_NETWORK_fdset_create ();
1005 GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1006 ret = GNUNET_SCHEDULER_add_select (sched,
1007 GNUNET_SCHEDULER_PRIORITY_KEEP,
1008 GNUNET_SCHEDULER_NO_TASK, delay,
1009 rs, NULL, task, task_cls);
1010 GNUNET_NETWORK_fdset_destroy (rs);
1016 * Schedule a new task to be run with a specified delay or when the
1017 * specified file descriptor is ready for writing. The delay can be
1018 * used as a timeout on the socket being ready. The task will be
1019 * scheduled for execution once either the delay has expired or the
1020 * socket operation is ready. It will be run with the priority of
1023 * @param sched scheduler to use
1024 * @param delay when should this operation time out? Use
1025 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1026 * @param wfd write file-descriptor
1027 * @param task main function of the task
1028 * @param task_cls closure of task
1029 * @return unique task identifier for the job
1030 * only valid until "task" is started!
1032 GNUNET_SCHEDULER_TaskIdentifier
1033 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1034 struct GNUNET_TIME_Relative delay,
1035 const struct GNUNET_DISK_FileHandle * wfd,
1036 GNUNET_SCHEDULER_Task task, void *task_cls)
1038 struct GNUNET_NETWORK_FDSet *ws;
1039 GNUNET_SCHEDULER_TaskIdentifier ret;
1041 GNUNET_assert (wfd != NULL);
1042 ws = GNUNET_NETWORK_fdset_create ();
1043 GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1044 ret = GNUNET_SCHEDULER_add_select (sched,
1045 GNUNET_SCHEDULER_PRIORITY_KEEP,
1046 GNUNET_SCHEDULER_NO_TASK,
1047 delay, NULL, ws, task, task_cls);
1048 GNUNET_NETWORK_fdset_destroy (ws);
1055 * Schedule a new task to be run with a specified delay or when any of
1056 * the specified file descriptor sets is ready. The delay can be used
1057 * as a timeout on the socket(s) being ready. The task will be
1058 * scheduled for execution once either the delay has expired or any of
1059 * the socket operations is ready. This is the most general
1060 * function of the "add" family. Note that the "prerequisite_task"
1061 * must be satisfied in addition to any of the other conditions. In
1062 * other words, the task will be started when
1064 * (prerequisite-run)
1068 * || (shutdown-active && run-on-shutdown) )
1071 * @param sched scheduler to use
1072 * @param prio how important is this task?
1073 * @param prerequisite_task run this task after the task with the given
1074 * task identifier completes (and any of our other
1075 * conditions, such as delay, read or write-readiness
1076 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1077 * on completion of other tasks.
1078 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1079 * which means that the task will only be run after we receive SIGTERM
1080 * @param rs set of file descriptors we want to read (can be NULL)
1081 * @param ws set of file descriptors we want to write (can be NULL)
1082 * @param task main function of the task
1083 * @param task_cls closure of task
1084 * @return unique task identifier for the job
1085 * only valid until "task" is started!
1087 GNUNET_SCHEDULER_TaskIdentifier
1088 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1089 enum GNUNET_SCHEDULER_Priority prio,
1090 GNUNET_SCHEDULER_TaskIdentifier
1092 struct GNUNET_TIME_Relative delay,
1093 const struct GNUNET_NETWORK_FDSet * rs,
1094 const struct GNUNET_NETWORK_FDSet * ws,
1095 GNUNET_SCHEDULER_Task task, void *task_cls)
1099 void *backtrace_array[MAX_TRACE_DEPTH];
1101 t = GNUNET_malloc (sizeof (struct Task));
1103 t->callback_cls = task_cls;
1105 t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1106 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1110 t->read_set = GNUNET_NETWORK_fdset_create ();
1111 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1115 t->write_set = GNUNET_NETWORK_fdset_create ();
1116 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1118 t->id = ++sched->last_id;
1119 t->prereq_id = prerequisite_task;
1120 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1122 check_priority ((prio ==
1123 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1125 t->next = sched->pending;
1128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1129 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1134 /* end of scheduler.c */