2 This file is part of GNUnet
3 (C) 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file util/scheduler.c
23 * @brief schedule computations using continuation passing style
24 * @author Christian Grothoff
27 #include "gnunet_common.h"
28 #include "gnunet_scheduler_lib.h"
29 #include "gnunet_signal_lib.h"
30 #include "gnunet_time_lib.h"
32 #define DEBUG_TASKS GNUNET_NO
35 * Linked list of pending tasks.
40 * This is a linked list.
45 * Function to run when ready.
47 GNUNET_SCHEDULER_Task callback;
50 * Closure for the callback.
55 * Set of file descriptors this task is waiting
56 * for for reading. Once ready, this is updated
57 * to reflect the set of file descriptors ready
60 struct GNUNET_NETWORK_FDSet *read_set;
63 * Set of file descriptors this task is waiting
64 * for for writing. Once ready, this is updated
65 * to reflect the set of file descriptors ready
68 struct GNUNET_NETWORK_FDSet *write_set;
71 * Unique task identifier.
73 GNUNET_SCHEDULER_TaskIdentifier id;
76 * Identifier of a prerequisite task.
78 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
81 * Absolute timeout value for the task, or
82 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
84 struct GNUNET_TIME_Absolute timeout;
87 * Why is the task ready? Set after task is added to ready queue.
88 * Initially set to zero. All reasons that have already been
89 * satisfied (i.e. read or write ready) will be set over time.
91 enum GNUNET_SCHEDULER_Reason reason;
96 enum GNUNET_SCHEDULER_Priority priority;
99 * Should this task be run on shutdown?
107 * Handle for the scheduling service.
109 struct GNUNET_SCHEDULER_Handle
113 * List of tasks waiting for an event.
115 struct Task *pending;
118 * List of tasks ready to run right now,
119 * grouped by importance.
121 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
124 * Identity of the last task queued. Incremented for each task to
125 * generate a unique task ID (it is virtually impossible to start
126 * more than 2^64 tasks during the lifetime of a process).
128 GNUNET_SCHEDULER_TaskIdentifier last_id;
131 * Highest number so that all tasks with smaller identifiers
132 * have already completed. Also the lowest number of a task
133 * still waiting to be executed.
135 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
138 * GNUNET_NO if we are running normally,
139 * GNUNET_YES if we are in shutdown mode.
144 * Number of tasks on the ready list.
146 unsigned int ready_count;
149 * How many tasks have we run so far?
151 unsigned long long tasks_run;
154 * Priority of the task running right now. Only
155 * valid while a task is running.
157 enum GNUNET_SCHEDULER_Priority current_priority;
163 * Check that the given priority is legal (and return it).
165 * @param p priority value to check
166 * @return p on success, 0 on error
168 static enum GNUNET_SCHEDULER_Priority
169 check_priority (enum GNUNET_SCHEDULER_Priority p)
171 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
174 return 0; /* make compiler happy */
179 * Is a task with this identifier still pending? Also updates
180 * "lowest_pending_id" as a side-effect (for faster checks in the
181 * future), but only if the return value is "GNUNET_NO" (and
182 * the "lowest_pending_id" check failed).
184 * @param sched the scheduler
185 * @param id which task are we checking for
186 * @return GNUNET_YES if so, GNUNET_NO if not
189 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
190 GNUNET_SCHEDULER_TaskIdentifier id)
193 enum GNUNET_SCHEDULER_Priority p;
194 GNUNET_SCHEDULER_TaskIdentifier min;
196 if (id < sched->lowest_pending_id)
198 min = -1; /* maximum value */
199 pos = sched->pending;
208 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
210 pos = sched->ready[p];
220 sched->lowest_pending_id = min;
226 * Update all sets and timeout for select.
228 * @param sched the scheduler
229 * @param rs read-set, set to all FDs we would like to read (updated)
230 * @param ws write-set, set to all FDs we would like to write (updated)
231 * @param timeout next timeout (updated)
234 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
235 struct GNUNET_NETWORK_FDSet * rs,
236 struct GNUNET_NETWORK_FDSet * ws,
237 struct GNUNET_TIME_Relative *timeout)
241 pos = sched->pending;
244 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
245 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
251 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
253 struct GNUNET_TIME_Relative to;
255 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
256 if (timeout->value > to.value)
259 if (pos->read_set != NULL)
260 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
261 if (pos->write_set != NULL)
262 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
269 * Check if the ready set overlaps with the set we want to have ready.
270 * If so, update the want set (set all FDs that are ready). If not,
273 * @param ready set that is ready
274 * @param want set that we want to be ready
275 * @return GNUNET_YES if there was some overlap
278 set_overlaps (const struct GNUNET_NETWORK_FDSet * ready,
279 struct GNUNET_NETWORK_FDSet * want)
283 if (GNUNET_NETWORK_fdset_overlap (ready, want))
285 /* copy all over (yes, there maybe unrelated bits,
286 but this should not hurt well-written clients) */
287 GNUNET_NETWORK_fdset_copy (want, ready);
295 * Check if the given task is eligible to run now.
296 * Also set the reason why it is eligible.
298 * @param sched the scheduler
299 * @param task task to check if it is ready
300 * @param now the current time
301 * @param rs set of FDs ready for reading
302 * @param ws set of FDs ready for writing
303 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
306 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
308 struct GNUNET_TIME_Absolute now,
309 const struct GNUNET_NETWORK_FDSet *rs,
310 const struct GNUNET_NETWORK_FDSet * ws)
312 if ((GNUNET_NO == task->run_on_shutdown) && (GNUNET_YES == sched->shutdown))
314 if ((GNUNET_YES == task->run_on_shutdown) &&
315 (GNUNET_YES == sched->shutdown))
316 task->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
317 if (now.value >= task->timeout.value)
318 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
319 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
320 (rs != NULL) && (set_overlaps (rs, task->read_set)))
321 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
322 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
323 (ws != NULL) && (set_overlaps (ws, task->write_set)))
324 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
325 if (task->reason == 0)
326 return GNUNET_NO; /* not ready */
327 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
329 if (GNUNET_YES == is_pending (sched, task->prereq_id))
330 return GNUNET_NO; /* prereq waiting */
331 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
338 * Put a task that is ready for execution into the ready queue.
340 * @param handle the scheduler
341 * @param task task ready for execution
344 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle,
347 task->next = handle->ready[check_priority (task->priority)];
348 handle->ready[check_priority (task->priority)] = task;
349 handle->ready_count++;
354 * Check which tasks are ready and move them
355 * to the respective ready queue.
357 * @param handle the scheduler
358 * @param rs FDs ready for reading
359 * @param ws FDs ready for writing
362 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
363 const struct GNUNET_NETWORK_FDSet * rs,
364 const struct GNUNET_NETWORK_FDSet * ws)
369 struct GNUNET_TIME_Absolute now;
371 now = GNUNET_TIME_absolute_get ();
373 pos = handle->pending;
378 "Checking readyness of task: %llu\n",
382 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
385 handle->pending = next;
388 queue_ready_task (handle, pos);
399 * Destroy a task (release associated resources)
401 * @param t task to destroy
403 static void destroy_task (struct Task *t)
405 if (NULL != t->read_set)
406 GNUNET_NETWORK_fdset_destroy (t->read_set);
407 if (NULL != t->write_set)
408 GNUNET_NETWORK_fdset_destroy (t->write_set);
414 * Run at least one task in the highest-priority queue that is not
415 * empty. Keep running tasks until we are either no longer running
416 * "URGENT" tasks or until we have at least one "pending" task (which
417 * may become ready, hence we should select on it). Naturally, if
418 * there are no more ready tasks, we also return.
420 * @param sched the scheduler
423 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
425 enum GNUNET_SCHEDULER_Priority p;
427 struct GNUNET_SCHEDULER_TaskContext tc;
431 if (sched->ready_count == 0)
433 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
434 /* yes, p>0 is correct, 0 is "KEEP" which should
435 always be an empty queue (see assertion)! */
436 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
438 pos = sched->ready[p];
442 GNUNET_assert (pos != NULL); /* ready_count wrong? */
443 sched->ready[p] = pos->next;
444 sched->ready_count--;
445 sched->current_priority = p;
446 GNUNET_assert (pos->priority == p);
448 tc.reason = pos->reason;
449 tc.read_ready = pos->read_set;
450 tc.write_ready = pos->write_set;
451 pos->callback (pos->callback_cls, &tc);
454 "Running task: %llu\n",
460 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
465 * Have we (ever) received a SIGINT/TERM/QUIT/HUP?
467 static volatile int sig_shutdown;
471 * Signal handler called for signals that should cause us to shutdown.
474 sighandler_shutdown ()
481 * Initialize a scheduler using this thread. This function will
482 * return when either a shutdown was initiated (via signal) and all
483 * tasks marked to "run_on_shutdown" have been completed or when all
484 * tasks in general have been completed.
486 * @param task task to run immediately
487 * @param cls closure of task
490 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
492 struct GNUNET_SCHEDULER_Handle sched;
493 struct GNUNET_NETWORK_FDSet *rs;
494 struct GNUNET_NETWORK_FDSet *ws;
495 struct GNUNET_TIME_Relative timeout;
497 struct GNUNET_SIGNAL_Context *shc_int;
498 struct GNUNET_SIGNAL_Context *shc_term;
499 struct GNUNET_SIGNAL_Context *shc_quit;
500 struct GNUNET_SIGNAL_Context *shc_hup;
502 unsigned long long last_tr;
503 unsigned int busy_wait_warning;
506 rs = GNUNET_NETWORK_fdset_create ();
507 ws = GNUNET_NETWORK_fdset_create ();
509 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
510 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
511 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
512 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
514 memset (&sched, 0, sizeof (sched));
515 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
516 GNUNET_SCHEDULER_add_continuation (&sched,
519 cls, GNUNET_SCHEDULER_REASON_STARTUP);
521 busy_wait_warning = 0;
522 while ((GNUNET_NO == sched.shutdown) &&
524 ((sched.pending != NULL) || (sched.ready_count > 0)))
526 GNUNET_NETWORK_fdset_zero (rs);
527 GNUNET_NETWORK_fdset_zero (ws);
528 timeout = GNUNET_TIME_relative_get_forever();
529 update_sets (&sched, rs, ws, &timeout);
530 if (sched.ready_count > 0)
532 /* no blocking, more work already ready! */
533 timeout = GNUNET_TIME_relative_get_zero();
535 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
536 if (last_tr == sched.tasks_run)
542 last_tr = sched.tasks_run;
543 busy_wait_warning = 0;
546 (timeout.value == 0) &&
547 (busy_wait_warning > 16) )
549 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
550 _("Looks like we're busy waiting...\n"));
551 sleep (1); /* mitigate */
553 if (ret == GNUNET_SYSERR)
557 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
560 check_ready (&sched, rs, ws);
564 sched.shutdown = GNUNET_YES;
565 GNUNET_SIGNAL_handler_uninstall (shc_int);
566 GNUNET_SIGNAL_handler_uninstall (shc_term);
567 GNUNET_SIGNAL_handler_uninstall (shc_quit);
568 GNUNET_SIGNAL_handler_uninstall (shc_hup);
572 check_ready (&sched, NULL, NULL);
574 while (sched.ready_count > 0);
575 while (NULL != (tpos = sched.pending))
577 sched.pending = tpos->next;
580 GNUNET_NETWORK_fdset_destroy (rs);
581 GNUNET_NETWORK_fdset_destroy (ws);
586 * Request the shutdown of a scheduler. This function can be used to
587 * stop a scheduling thread when created with the
588 * "GNUNET_SCHEDULER_init_thread" function or from within the signal
589 * handler for signals causing shutdowns.
591 * @param sched the scheduler
594 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
596 sched->shutdown = GNUNET_YES;
601 * Get information about the current load of this scheduler. Use this
602 * function to determine if an elective task should be added or simply
603 * dropped (if the decision should be made based on the number of
604 * tasks ready to run).
606 * @param sched scheduler to query
607 * @param p priority level to look at
608 * @return number of tasks pending right now
611 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
612 enum GNUNET_SCHEDULER_Priority p)
617 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
618 return sched->ready_count;
619 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
620 p = sched->current_priority;
622 pos = sched->ready[p];
633 * Cancel the task with the specified identifier.
634 * The task must not yet have run.
636 * @param sched scheduler to use
637 * @param task id of the task to cancel
638 * @return original closure of the task
641 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
642 GNUNET_SCHEDULER_TaskIdentifier task)
646 enum GNUNET_SCHEDULER_Priority p;
651 "Canceling task: %llu\n",
667 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
674 sched->ready_count--;
684 sched->pending = t->next;
686 sched->ready[p] = t->next;
689 prev->next = t->next;
690 ret = t->callback_cls;
696 #include <execinfo.h>
700 * Continue the current execution with the given function. This is
701 * similar to the other "add" functions except that there is no delay
702 * and the reason code can be specified.
704 * @param sched scheduler to use
705 * @param run_on_shutdown should this task be run if we are shutting down?
706 * @param main main function of the task
707 * @param cls closure for 'main'
708 * @param reason reason for task invocation
711 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
713 GNUNET_SCHEDULER_Task main,
715 enum GNUNET_SCHEDULER_Reason reason)
719 task = GNUNET_malloc (sizeof (struct Task));
720 task->callback = main;
721 task->callback_cls = cls;
722 task->id = ++sched->last_id;
723 task->reason = reason;
724 task->priority = sched->current_priority;
725 task->run_on_shutdown = run_on_shutdown;
730 "Adding continuation task: %llu\n",
732 backtrace_symbols_fd (ptrs, backtrace (ptrs, 20), 2);
735 queue_ready_task (sched, task);
740 * Schedule a new task to be run after the specified
741 * prerequisite task has completed.
743 * @param sched scheduler to use
744 * @param run_on_shutdown run on shutdown?
745 * @param prio how important is this task?
746 * @param prerequisite_task run this task after the task with the given
747 * task identifier completes (and any of our other
748 * conditions, such as delay, read or write-readyness
749 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
750 * on completion of other tasks.
751 * @param main main function of the task
752 * @param cls closure for 'main'
753 * @return unique task identifier for the job
754 * only valid until "main" is started!
756 GNUNET_SCHEDULER_TaskIdentifier
757 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
759 enum GNUNET_SCHEDULER_Priority prio,
760 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
761 GNUNET_SCHEDULER_Task main, void *cls)
763 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
765 GNUNET_TIME_UNIT_ZERO,
766 NULL, NULL, main, cls);
771 * Schedule a new task to be run with a specified delay. The task
772 * will be scheduled for execution once the delay has expired and the
773 * prerequisite task has completed.
775 * @param sched scheduler to use
776 * @param run_on_shutdown run on shutdown? You can use this
777 * argument to run a function only during shutdown
778 * by setting delay to -1. Set this
779 * argument to GNUNET_NO to skip this task if
780 * the user requested process termination.
781 * @param prio how important is this task?
782 * @param prerequisite_task run this task after the task with the given
783 * task identifier completes (and any of our other
784 * conditions, such as delay, read or write-readyness
785 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
786 * on completion of other tasks.
787 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
788 * @param main main function of the task
789 * @param cls closure of task
790 * @return unique task identifier for the job
791 * only valid until "main" is started!
793 GNUNET_SCHEDULER_TaskIdentifier
794 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
796 enum GNUNET_SCHEDULER_Priority prio,
797 GNUNET_SCHEDULER_TaskIdentifier
799 struct GNUNET_TIME_Relative delay,
800 GNUNET_SCHEDULER_Task main, void *cls)
802 return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
803 prerequisite_task, delay,
804 NULL, NULL, main, cls);
809 * Schedule a new task to be run with a specified delay or when the
810 * specified file descriptor is ready for reading. The delay can be
811 * used as a timeout on the socket being ready. The task will be
812 * scheduled for execution once either the delay has expired or the
813 * socket operation is ready.
815 * @param sched scheduler to use
816 * @param run_on_shutdown run on shutdown? Set this
817 * argument to GNUNET_NO to skip this task if
818 * the user requested process termination.
819 * @param prio how important is this task?
820 * @param prerequisite_task run this task after the task with the given
821 * task identifier completes (and any of our other
822 * conditions, such as delay, read or write-readyness
823 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
824 * on completion of other tasks.
825 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
826 * @param rfd read file-descriptor
827 * @param main main function of the task
828 * @param cls closure of task
829 * @return unique task identifier for the job
830 * only valid until "main" is started!
832 GNUNET_SCHEDULER_TaskIdentifier
833 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
835 enum GNUNET_SCHEDULER_Priority prio,
836 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
837 struct GNUNET_TIME_Relative delay,
838 struct GNUNET_NETWORK_Handle *rfd, GNUNET_SCHEDULER_Task main, void *cls)
840 struct GNUNET_NETWORK_FDSet *rs;
841 GNUNET_SCHEDULER_TaskIdentifier ret;
843 GNUNET_assert (rfd != NULL);
844 rs = GNUNET_NETWORK_fdset_create ();
845 GNUNET_NETWORK_fdset_set (rs, rfd);
846 ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
847 prerequisite_task, delay,
848 rs, NULL, main, cls);
849 GNUNET_NETWORK_fdset_destroy (rs);
855 * Schedule a new task to be run with a specified delay or when the
856 * specified file descriptor is ready for writing. The delay can be
857 * used as a timeout on the socket being ready. The task will be
858 * scheduled for execution once either the delay has expired or the
859 * socket operation is ready.
861 * @param sched scheduler to use
862 * @param run_on_shutdown run on shutdown? Set this
863 * argument to GNUNET_NO to skip this task if
864 * the user requested process termination.
865 * @param prio how important is this task?
866 * @param prerequisite_task run this task after the task with the given
867 * task identifier completes (and any of our other
868 * conditions, such as delay, read or write-readyness
869 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
870 * on completion of other tasks.
871 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
872 * @param wfd write file-descriptor
873 * @param main main function of the task
874 * @param cls closure of task
875 * @return unique task identifier for the job
876 * only valid until "main" is started!
878 GNUNET_SCHEDULER_TaskIdentifier
879 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
881 enum GNUNET_SCHEDULER_Priority prio,
882 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
883 struct GNUNET_TIME_Relative delay,
884 struct GNUNET_NETWORK_Handle *wfd, GNUNET_SCHEDULER_Task main, void *cls)
886 struct GNUNET_NETWORK_FDSet *ws;
887 GNUNET_SCHEDULER_TaskIdentifier ret;
889 GNUNET_assert (wfd != NULL);
890 ws = GNUNET_NETWORK_fdset_create ();
891 GNUNET_NETWORK_fdset_set (ws, wfd);
892 ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
893 prerequisite_task, delay,
894 NULL, ws, main, cls);
895 GNUNET_NETWORK_fdset_destroy (ws);
901 * Schedule a new task to be run with a specified delay or when any of
902 * the specified file descriptor sets is ready. The delay can be used
903 * as a timeout on the socket(s) being ready. The task will be
904 * scheduled for execution once either the delay has expired or any of
905 * the socket operations is ready. This is the most general
906 * function of the "add" family. Note that the "prerequisite_task"
907 * must be satisfied in addition to any of the other conditions. In
908 * other words, the task will be started when
914 * || (shutdown-active && run-on-shutdown) )
917 * @param sched scheduler to use
918 * @param run_on_shutdown run on shutdown? Set this
919 * argument to GNUNET_NO to skip this task if
920 * the user requested process termination.
921 * @param prio how important is this task?
922 * @param prerequisite_task run this task after the task with the given
923 * task identifier completes (and any of our other
924 * conditions, such as delay, read or write-readyness
925 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
926 * on completion of other tasks.
927 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
928 * @param rs set of file descriptors we want to read (can be NULL)
929 * @param ws set of file descriptors we want to write (can be NULL)
930 * @param main main function of the task
931 * @param cls closure of task
932 * @return unique task identifier for the job
933 * only valid until "main" is started!
935 GNUNET_SCHEDULER_TaskIdentifier
936 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
938 enum GNUNET_SCHEDULER_Priority prio,
939 GNUNET_SCHEDULER_TaskIdentifier
941 struct GNUNET_TIME_Relative delay,
942 const struct GNUNET_NETWORK_FDSet * rs,
943 const struct GNUNET_NETWORK_FDSet * ws,
944 GNUNET_SCHEDULER_Task main, void *cls)
948 task = GNUNET_malloc (sizeof (struct Task));
949 task->callback = main;
950 task->callback_cls = cls;
953 task->read_set = GNUNET_NETWORK_fdset_create ();
954 GNUNET_NETWORK_fdset_copy (task->read_set, rs);
958 task->write_set = GNUNET_NETWORK_fdset_create ();
959 GNUNET_NETWORK_fdset_copy (task->write_set, ws);
961 task->id = ++sched->last_id;
962 task->prereq_id = prerequisite_task;
963 task->timeout = GNUNET_TIME_relative_to_absolute (delay);
965 check_priority ((prio ==
966 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
968 task->run_on_shutdown = run_on_shutdown;
969 task->next = sched->pending;
970 sched->pending = task;
975 "Adding task: %llu\n",
977 backtrace_symbols_fd (ptrs, backtrace (ptrs, 20), 2);
984 * Schedule a new task to be run with a specified delay or when the
985 * specified file descriptor is ready for reading. The delay can be
986 * used as a timeout on the socket being ready. The task will be
987 * scheduled for execution once either the delay has expired or the
988 * socket operation is ready.
990 * @param sched scheduler to use
991 * @param run_on_shutdown run on shutdown? Set this
992 * argument to GNUNET_NO to skip this task if
993 * the user requested process termination.
994 * @param prio how important is this task?
995 * @param prerequisite_task run this task after the task with the given
996 * task identifier completes (and any of our other
997 * conditions, such as delay, read or write-readyness
998 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
999 * on completion of other tasks.
1000 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
1001 * @param rfd read file-descriptor
1002 * @param main main function of the task
1003 * @param cls closure of task
1004 * @return unique task identifier for the job
1005 * only valid until "main" is started!
1007 GNUNET_SCHEDULER_TaskIdentifier
1008 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1009 int run_on_shutdown,
1010 enum GNUNET_SCHEDULER_Priority prio,
1011 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
1012 struct GNUNET_TIME_Relative delay,
1013 const struct GNUNET_DISK_FileHandle *rfd,
1014 GNUNET_SCHEDULER_Task main, void *cls)
1016 struct GNUNET_NETWORK_FDSet *rs;
1017 GNUNET_SCHEDULER_TaskIdentifier ret;
1019 GNUNET_assert (rfd != NULL);
1020 rs = GNUNET_NETWORK_fdset_create ();
1021 GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1022 ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
1023 prerequisite_task, delay,
1024 rs, NULL, main, cls);
1025 GNUNET_NETWORK_fdset_destroy (rs);
1031 * Schedule a new task to be run with a specified delay or when the
1032 * specified file descriptor is ready for writing. The delay can be
1033 * used as a timeout on the socket being ready. The task will be
1034 * scheduled for execution once either the delay has expired or the
1035 * socket operation is ready.
1037 * @param sched scheduler to use
1038 * @param run_on_shutdown run on shutdown? Set this
1039 * argument to GNUNET_NO to skip this task if
1040 * the user requested process termination.
1041 * @param prio how important is this task?
1042 * @param prerequisite_task run this task after the task with the given
1043 * task identifier completes (and any of our other
1044 * conditions, such as delay, read or write-readyness
1045 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1046 * on completion of other tasks.
1047 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
1048 * @param wfd write file-descriptor
1049 * @param main main function of the task
1050 * @param cls closure of task
1051 * @return unique task identifier for the job
1052 * only valid until "main" is started!
1054 GNUNET_SCHEDULER_TaskIdentifier
1055 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1056 int run_on_shutdown,
1057 enum GNUNET_SCHEDULER_Priority prio,
1058 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
1059 struct GNUNET_TIME_Relative delay,
1060 const struct GNUNET_DISK_FileHandle *wfd,
1061 GNUNET_SCHEDULER_Task main, void *cls)
1063 struct GNUNET_NETWORK_FDSet *ws;
1064 GNUNET_SCHEDULER_TaskIdentifier ret;
1066 GNUNET_assert (wfd != NULL);
1067 ws = GNUNET_NETWORK_fdset_create ();
1068 GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1069 ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
1070 prerequisite_task, delay,
1071 NULL, ws, main, cls);
1072 GNUNET_NETWORK_fdset_destroy (ws);
1077 /* end of scheduler.c */