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"
34 #define EXECINFO GNUNET_NO
35 #define MAX_TRACE_DEPTH 50
38 #define DEBUG_TASKS GNUNET_NO
41 * Linked list of pending tasks.
46 * This is a linked list.
51 * Function to run when ready.
53 GNUNET_SCHEDULER_Task callback;
56 * Closure for the callback.
61 * Set of file descriptors this task is waiting
62 * for for reading. Once ready, this is updated
63 * to reflect the set of file descriptors ready
66 struct GNUNET_NETWORK_FDSet *read_set;
69 * Set of file descriptors this task is waiting for for writing.
70 * Once ready, this is updated to reflect the set of file
71 * descriptors ready for operation.
73 struct GNUNET_NETWORK_FDSet *write_set;
76 * Unique task identifier.
78 GNUNET_SCHEDULER_TaskIdentifier id;
81 * Identifier of a prerequisite task.
83 GNUNET_SCHEDULER_TaskIdentifier prereq_id;
86 * Absolute timeout value for the task, or
87 * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
89 struct GNUNET_TIME_Absolute timeout;
92 * Why is the task ready? Set after task is added to ready queue.
93 * Initially set to zero. All reasons that have already been
94 * satisfied (i.e. read or write ready) will be set over time.
96 enum GNUNET_SCHEDULER_Reason reason;
101 enum GNUNET_SCHEDULER_Priority priority;
105 * Array of strings which make up a backtrace from the point when this
106 * task was scheduled (essentially, who scheduled the task?)
108 char **backtrace_strings;
111 * Size of the backtrace_strings array
113 int num_backtrace_strings;
120 * Handle for the scheduling service.
122 struct GNUNET_SCHEDULER_Handle
126 * List of tasks waiting for an event.
128 struct Task *pending;
131 * ID of the task that is running right now.
133 struct Task *active_task;
136 * List of tasks ready to run right now,
137 * grouped by importance.
139 struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
142 * Identity of the last task queued. Incremented for each task to
143 * generate a unique task ID (it is virtually impossible to start
144 * more than 2^64 tasks during the lifetime of a process).
146 GNUNET_SCHEDULER_TaskIdentifier last_id;
149 * Highest number so that all tasks with smaller identifiers
150 * have already completed. Also the lowest number of a task
151 * still waiting to be executed.
153 GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
156 * Number of tasks on the ready list.
158 unsigned int ready_count;
161 * How many tasks have we run so far?
163 unsigned long long tasks_run;
166 * Priority of the task running right now. Only
167 * valid while a task is running.
169 enum GNUNET_SCHEDULER_Priority current_priority;
172 * How 'nice' are we right now?
180 * Check that the given priority is legal (and return it).
182 * @param p priority value to check
183 * @return p on success, 0 on error
185 static enum GNUNET_SCHEDULER_Priority
186 check_priority (enum GNUNET_SCHEDULER_Priority p)
188 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
191 return 0; /* make compiler happy */
196 * Is a task with this identifier still pending? Also updates
197 * "lowest_pending_id" as a side-effect (for faster checks in the
198 * future), but only if the return value is "GNUNET_NO" (and
199 * the "lowest_pending_id" check failed).
201 * @param sched the scheduler
202 * @param id which task are we checking for
203 * @return GNUNET_YES if so, GNUNET_NO if not
206 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
207 GNUNET_SCHEDULER_TaskIdentifier id)
210 enum GNUNET_SCHEDULER_Priority p;
211 GNUNET_SCHEDULER_TaskIdentifier min;
213 if (id < sched->lowest_pending_id)
215 min = -1; /* maximum value */
216 pos = sched->pending;
225 for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
227 pos = sched->ready[p];
237 sched->lowest_pending_id = min;
243 * Update all sets and timeout for select.
245 * @param sched the scheduler
246 * @param rs read-set, set to all FDs we would like to read (updated)
247 * @param ws write-set, set to all FDs we would like to write (updated)
248 * @param timeout next timeout (updated)
251 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
252 struct GNUNET_NETWORK_FDSet *rs,
253 struct GNUNET_NETWORK_FDSet *ws,
254 struct GNUNET_TIME_Relative *timeout)
258 pos = sched->pending;
261 if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
262 (GNUNET_YES == is_pending (sched, pos->prereq_id)))
268 if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
270 struct GNUNET_TIME_Relative to;
272 to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
273 if (timeout->value > to.value)
276 if (pos->read_set != NULL)
277 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
278 if (pos->write_set != NULL)
279 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
280 if (pos->reason != 0)
281 *timeout = GNUNET_TIME_UNIT_ZERO;
288 * Check if the ready set overlaps with the set we want to have ready.
289 * If so, update the want set (set all FDs that are ready). If not,
292 * @param ready set that is ready
293 * @param want set that we want to be ready
294 * @return GNUNET_YES if there was some overlap
297 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
298 struct GNUNET_NETWORK_FDSet *want)
302 if (GNUNET_NETWORK_fdset_overlap (ready, want))
304 /* copy all over (yes, there maybe unrelated bits,
305 but this should not hurt well-written clients) */
306 GNUNET_NETWORK_fdset_copy (want, ready);
314 * Check if the given task is eligible to run now.
315 * Also set the reason why it is eligible.
317 * @param sched the scheduler
318 * @param task task to check if it is ready
319 * @param now the current time
320 * @param rs set of FDs ready for reading
321 * @param ws set of FDs ready for writing
322 * @return GNUNET_YES if we can run it, GNUNET_NO if not.
325 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
327 struct GNUNET_TIME_Absolute now,
328 const struct GNUNET_NETWORK_FDSet *rs,
329 const struct GNUNET_NETWORK_FDSet *ws)
331 if (now.value >= task->timeout.value)
332 task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
333 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
334 (rs != NULL) && (set_overlaps (rs, task->read_set)))
335 task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
336 if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
337 (ws != NULL) && (set_overlaps (ws, task->write_set)))
338 task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
339 if (task->reason == 0)
340 return GNUNET_NO; /* not ready */
341 if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
343 if (GNUNET_YES == is_pending (sched, task->prereq_id))
344 return GNUNET_NO; /* prereq waiting */
345 task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
352 * Put a task that is ready for execution into the ready queue.
354 * @param handle the scheduler
355 * @param task task ready for execution
358 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
360 enum GNUNET_SCHEDULER_Priority p = task->priority;
361 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
362 p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
363 task->next = handle->ready[check_priority (p)];
364 handle->ready[check_priority (p)] = task;
365 handle->ready_count++;
370 * Check which tasks are ready and move them
371 * to the respective ready queue.
373 * @param handle the scheduler
374 * @param rs FDs ready for reading
375 * @param ws FDs ready for writing
378 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
379 const struct GNUNET_NETWORK_FDSet *rs,
380 const struct GNUNET_NETWORK_FDSet *ws)
385 struct GNUNET_TIME_Absolute now;
387 now = GNUNET_TIME_absolute_get ();
389 pos = handle->pending;
393 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 "Checking readiness of task: %llu / %p\n",
395 pos->id, pos->callback_cls);
398 if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
401 handle->pending = next;
404 queue_ready_task (handle, pos);
415 * Request the shutdown of a scheduler. Marks all currently
416 * pending tasks as ready because of shutdown. This will
417 * cause all tasks to run (as soon as possible, respecting
418 * priorities and prerequisite tasks). Note that tasks
419 * scheduled AFTER this call may still be delayed arbitrarily.
421 * @param sched the scheduler
424 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
429 pos = sched->pending;
432 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
433 /* we don't move the task into the ready queue yet; check_ready
434 will do that later, possibly adding additional
438 for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
440 pos = sched->ready[i];
443 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
444 /* we don't move the task into the ready queue yet; check_ready
445 will do that later, possibly adding additional
454 * Destroy a task (release associated resources)
456 * @param t task to destroy
459 destroy_task (struct Task *t)
461 if (NULL != t->read_set)
462 GNUNET_NETWORK_fdset_destroy (t->read_set);
463 if (NULL != t->write_set)
464 GNUNET_NETWORK_fdset_destroy (t->write_set);
466 GNUNET_free (t->backtrace_strings);
473 * Run at least one task in the highest-priority queue that is not
474 * empty. Keep running tasks until we are either no longer running
475 * "URGENT" tasks or until we have at least one "pending" task (which
476 * may become ready, hence we should select on it). Naturally, if
477 * there are no more ready tasks, we also return.
479 * @param sched the scheduler
482 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
484 enum GNUNET_SCHEDULER_Priority p;
486 struct GNUNET_SCHEDULER_TaskContext tc;
490 if (sched->ready_count == 0)
492 GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
493 /* yes, p>0 is correct, 0 is "KEEP" which should
494 always be an empty queue (see assertion)! */
495 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
497 pos = sched->ready[p];
501 GNUNET_assert (pos != NULL); /* ready_count wrong? */
502 sched->ready[p] = pos->next;
503 sched->ready_count--;
504 if (sched->current_priority != pos->priority)
506 sched->current_priority = pos->priority;
507 (void) GNUNET_OS_set_process_priority (0, pos->priority);
509 sched->active_task = pos;
511 tc.reason = pos->reason;
512 tc.read_ready = pos->read_set;
513 tc.write_ready = pos->write_set;
514 pos->callback (pos->callback_cls, &tc);
516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517 "Running task: %llu / %p\n", pos->id, pos->callback_cls);
519 sched->active_task = NULL;
523 while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
527 * Pipe used to communicate shutdown via signal.
529 static struct GNUNET_DISK_PipeHandle *sigpipe;
533 * Signal handler called for signals that should cause us to shutdown.
536 sighandler_shutdown ()
540 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
541 (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
547 * Initialize and run scheduler. This function will return when all
548 * tasks have completed. On systems with signals, receiving a SIGTERM
549 * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
550 * to be run after the active task is complete. As a result, SIGTERM
551 * causes all active tasks to be scheduled with reason
552 * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
553 * afterwards will execute normally!). Note that any particular signal
554 * will only shut down one scheduler; applications should always only
555 * create a single scheduler.
557 * @param task task to run immediately
558 * @param task_cls closure of task
561 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
563 struct GNUNET_SCHEDULER_Handle sched;
564 struct GNUNET_NETWORK_FDSet *rs;
565 struct GNUNET_NETWORK_FDSet *ws;
566 struct GNUNET_TIME_Relative timeout;
568 struct GNUNET_SIGNAL_Context *shc_int;
569 struct GNUNET_SIGNAL_Context *shc_term;
570 struct GNUNET_SIGNAL_Context *shc_quit;
571 struct GNUNET_SIGNAL_Context *shc_hup;
572 unsigned long long last_tr;
573 unsigned int busy_wait_warning;
574 const struct GNUNET_DISK_FileHandle *pr;
577 rs = GNUNET_NETWORK_fdset_create ();
578 ws = GNUNET_NETWORK_fdset_create ();
579 GNUNET_assert (sigpipe == NULL);
580 sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
581 GNUNET_assert (sigpipe != NULL);
582 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
583 GNUNET_assert (pr != NULL);
584 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
585 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
587 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
588 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
590 memset (&sched, 0, sizeof (sched));
591 sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
592 GNUNET_SCHEDULER_add_continuation (&sched,
595 GNUNET_SCHEDULER_REASON_STARTUP);
597 busy_wait_warning = 0;
598 while ((sched.pending != NULL) || (sched.ready_count > 0))
600 GNUNET_NETWORK_fdset_zero (rs);
601 GNUNET_NETWORK_fdset_zero (ws);
602 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
603 update_sets (&sched, rs, ws, &timeout);
604 GNUNET_NETWORK_fdset_handle_set (rs, pr);
605 if (sched.ready_count > 0)
607 /* no blocking, more work already ready! */
608 timeout = GNUNET_TIME_UNIT_ZERO;
610 ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
611 if (ret == GNUNET_SYSERR)
615 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
618 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
620 /* consume the signal */
621 GNUNET_DISK_file_read (pr, &c, sizeof (c));
622 /* mark all active tasks as ready due to shutdown */
623 GNUNET_SCHEDULER_shutdown (&sched);
625 if (last_tr == sched.tasks_run)
631 last_tr = sched.tasks_run;
632 busy_wait_warning = 0;
634 if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
636 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
637 _("Looks like we're busy waiting...\n"));
638 sleep (1); /* mitigate */
640 check_ready (&sched, rs, ws);
643 GNUNET_SIGNAL_handler_uninstall (shc_int);
644 GNUNET_SIGNAL_handler_uninstall (shc_term);
646 GNUNET_SIGNAL_handler_uninstall (shc_quit);
647 GNUNET_SIGNAL_handler_uninstall (shc_hup);
649 GNUNET_DISK_pipe_close (sigpipe);
651 GNUNET_NETWORK_fdset_destroy (rs);
652 GNUNET_NETWORK_fdset_destroy (ws);
657 * Obtain the reason code for why the current task was
658 * started. Will return the same value as
659 * the GNUNET_SCHEDULER_TaskContext's reason field.
661 * @param sched scheduler to query
662 * @return reason(s) why the current task is run
664 enum GNUNET_SCHEDULER_Reason
665 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
667 return sched->active_task->reason;
672 * Get information about the current load of this scheduler. Use this
673 * function to determine if an elective task should be added or simply
674 * dropped (if the decision should be made based on the number of
675 * tasks ready to run).
677 * @param sched scheduler to query
678 * @param p priority level to look at
679 * @return number of tasks pending right now
682 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
683 enum GNUNET_SCHEDULER_Priority p)
688 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
689 return sched->ready_count;
690 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
691 p = sched->current_priority;
693 pos = sched->ready[p];
704 * Cancel the task with the specified identifier.
705 * The task must not yet have run.
707 * @param sched scheduler to use
708 * @param task id of the task to cancel
709 * @return original closure of the task
712 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
713 GNUNET_SCHEDULER_TaskIdentifier task)
717 enum GNUNET_SCHEDULER_Priority p;
733 GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
740 sched->ready_count--;
750 sched->pending = t->next;
752 sched->ready[p] = t->next;
755 prev->next = t->next;
756 ret = t->callback_cls;
758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759 "Canceling task: %llu / %p\n", task, t->callback_cls);
767 * Continue the current execution with the given function. This is
768 * similar to the other "add" functions except that there is no delay
769 * and the reason code can be specified.
771 * @param sched scheduler to use
772 * @param task main function of the task
773 * @param task_cls closure for 'main'
774 * @param reason reason for task invocation
777 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
778 GNUNET_SCHEDULER_Task task,
780 enum GNUNET_SCHEDULER_Reason reason)
784 void *backtrace_array[50];
786 t = GNUNET_malloc (sizeof (struct Task));
788 t->num_backtrace_strings = backtrace(backtrace_array, 50);
789 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
792 t->callback_cls = task_cls;
793 t->id = ++sched->last_id;
795 t->priority = sched->current_priority;
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798 "Adding continuation task: %llu / %p\n",
799 t->id, t->callback_cls);
801 queue_ready_task (sched, t);
807 * Schedule a new task to be run after the specified prerequisite task
808 * has completed. It will be run with the priority of the calling
811 * @param sched scheduler to use
812 * @param prerequisite_task run this task after the task with the given
813 * task identifier completes (and any of our other
814 * conditions, such as delay, read or write-readiness
815 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
816 * on completion of other tasks (this will cause the task to run as
818 * @param task main function of the task
819 * @param task_cls closure of task
820 * @return unique task identifier for the job
821 * only valid until "task" is started!
823 GNUNET_SCHEDULER_TaskIdentifier
824 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
825 GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
826 GNUNET_SCHEDULER_Task task, void *task_cls)
828 return GNUNET_SCHEDULER_add_select (sched,
829 GNUNET_SCHEDULER_PRIORITY_KEEP,
831 GNUNET_TIME_UNIT_ZERO,
832 NULL, NULL, task, task_cls);
837 * Schedule a new task to be run with a specified priority.
839 * @param sched scheduler to use
840 * @param prio how important is the new task?
841 * @param task main function of the task
842 * @param task_cls closure of task
843 * @return unique task identifier for the job
844 * only valid until "task" is started!
846 GNUNET_SCHEDULER_TaskIdentifier
847 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
848 enum GNUNET_SCHEDULER_Priority prio,
849 GNUNET_SCHEDULER_Task task,
852 return GNUNET_SCHEDULER_add_select (sched,
854 GNUNET_SCHEDULER_NO_TASK,
855 GNUNET_TIME_UNIT_ZERO,
856 NULL, NULL, task, task_cls);
862 * Schedule a new task to be run with a specified delay. The task
863 * will be scheduled for execution once the delay has expired. It
864 * will be run with the priority of the calling task.
866 * @param sched scheduler to use
867 * @param delay when should this operation time out? Use
868 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
869 * @param task main function of the task
870 * @param task_cls closure of task
871 * @return unique task identifier for the job
872 * only valid until "task" is started!
874 GNUNET_SCHEDULER_TaskIdentifier
875 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
876 struct GNUNET_TIME_Relative delay,
877 GNUNET_SCHEDULER_Task task, void *task_cls)
879 return GNUNET_SCHEDULER_add_select (sched,
880 GNUNET_SCHEDULER_PRIORITY_KEEP,
881 GNUNET_SCHEDULER_NO_TASK, delay,
882 NULL, NULL, task, task_cls);
888 * Schedule a new task to be run as soon as possible. The task
889 * will be run with the priority of the calling task.
891 * @param sched scheduler to use
892 * @param task main function of the task
893 * @param task_cls closure of task
894 * @return unique task identifier for the job
895 * only valid until "task" is started!
897 GNUNET_SCHEDULER_TaskIdentifier
898 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
899 GNUNET_SCHEDULER_Task task,
902 return GNUNET_SCHEDULER_add_select (sched,
903 GNUNET_SCHEDULER_PRIORITY_KEEP,
904 GNUNET_SCHEDULER_NO_TASK,
905 GNUNET_TIME_UNIT_ZERO,
906 NULL, NULL, task, task_cls);
912 * Schedule a new task to be run with a specified delay or when the
913 * specified file descriptor is ready for reading. The delay can be
914 * used as a timeout on the socket being ready. The task will be
915 * scheduled for execution once either the delay has expired or the
916 * socket operation is ready. It will be run with the priority of
919 * @param sched scheduler to use
920 * @param delay when should this operation time out? Use
921 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
922 * @param rfd read file-descriptor
923 * @param task main function of the task
924 * @param task_cls closure of task
925 * @return unique task identifier for the job
926 * only valid until "task" is started!
928 GNUNET_SCHEDULER_TaskIdentifier
929 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
930 struct GNUNET_TIME_Relative delay,
931 struct GNUNET_NETWORK_Handle * rfd,
932 GNUNET_SCHEDULER_Task task, void *task_cls)
934 struct GNUNET_NETWORK_FDSet *rs;
935 GNUNET_SCHEDULER_TaskIdentifier ret;
937 GNUNET_assert (rfd != NULL);
938 rs = GNUNET_NETWORK_fdset_create ();
939 GNUNET_NETWORK_fdset_set (rs, rfd);
940 ret = GNUNET_SCHEDULER_add_select (sched,
941 GNUNET_SCHEDULER_PRIORITY_KEEP,
942 GNUNET_SCHEDULER_NO_TASK,
943 delay, rs, NULL, task, task_cls);
944 GNUNET_NETWORK_fdset_destroy (rs);
950 * Schedule a new task to be run with a specified delay or when the
951 * specified file descriptor is ready for writing. The delay can be
952 * used as a timeout on the socket being ready. The task will be
953 * scheduled for execution once either the delay has expired or the
954 * socket operation is ready. It will be run with the priority of
957 * @param sched scheduler to use
958 * @param delay when should this operation time out? Use
959 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
960 * @param wfd write file-descriptor
961 * @param task main function of the task
962 * @param task_cls closure of task
963 * @return unique task identifier for the job
964 * only valid until "task" is started!
966 GNUNET_SCHEDULER_TaskIdentifier
967 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
968 struct GNUNET_TIME_Relative delay,
969 struct GNUNET_NETWORK_Handle * wfd,
970 GNUNET_SCHEDULER_Task task, void *task_cls)
972 struct GNUNET_NETWORK_FDSet *ws;
973 GNUNET_SCHEDULER_TaskIdentifier ret;
975 GNUNET_assert (wfd != NULL);
976 ws = GNUNET_NETWORK_fdset_create ();
977 GNUNET_NETWORK_fdset_set (ws, wfd);
978 ret = GNUNET_SCHEDULER_add_select (sched,
979 GNUNET_SCHEDULER_PRIORITY_KEEP,
980 GNUNET_SCHEDULER_NO_TASK, delay,
981 NULL, ws, task, task_cls);
982 GNUNET_NETWORK_fdset_destroy (ws);
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_file (struct GNUNET_SCHEDULER_Handle * sched,
1006 struct GNUNET_TIME_Relative delay,
1007 const struct GNUNET_DISK_FileHandle * 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_handle_set (rs, rfd);
1016 ret = GNUNET_SCHEDULER_add_select (sched,
1017 GNUNET_SCHEDULER_PRIORITY_KEEP,
1018 GNUNET_SCHEDULER_NO_TASK, delay,
1019 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_file (struct GNUNET_SCHEDULER_Handle * sched,
1044 struct GNUNET_TIME_Relative delay,
1045 const struct GNUNET_DISK_FileHandle * 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_handle_set (ws, wfd);
1054 ret = GNUNET_SCHEDULER_add_select (sched,
1055 GNUNET_SCHEDULER_PRIORITY_KEEP,
1056 GNUNET_SCHEDULER_NO_TASK,
1057 delay, NULL, ws, task, task_cls);
1058 GNUNET_NETWORK_fdset_destroy (ws);
1065 * Schedule a new task to be run with a specified delay or when any of
1066 * the specified file descriptor sets is ready. The delay can be used
1067 * as a timeout on the socket(s) being ready. The task will be
1068 * scheduled for execution once either the delay has expired or any of
1069 * the socket operations is ready. This is the most general
1070 * function of the "add" family. Note that the "prerequisite_task"
1071 * must be satisfied in addition to any of the other conditions. In
1072 * other words, the task will be started when
1074 * (prerequisite-run)
1078 * || (shutdown-active && run-on-shutdown) )
1081 * @param sched scheduler to use
1082 * @param prio how important is this task?
1083 * @param prerequisite_task run this task after the task with the given
1084 * task identifier completes (and any of our other
1085 * conditions, such as delay, read or write-readiness
1086 * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1087 * on completion of other tasks.
1088 * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1089 * which means that the task will only be run after we receive SIGTERM
1090 * @param rs set of file descriptors we want to read (can be NULL)
1091 * @param ws set of file descriptors we want to write (can be NULL)
1092 * @param task main function of the task
1093 * @param task_cls closure of task
1094 * @return unique task identifier for the job
1095 * only valid until "task" is started!
1097 GNUNET_SCHEDULER_TaskIdentifier
1098 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1099 enum GNUNET_SCHEDULER_Priority prio,
1100 GNUNET_SCHEDULER_TaskIdentifier
1102 struct GNUNET_TIME_Relative delay,
1103 const struct GNUNET_NETWORK_FDSet * rs,
1104 const struct GNUNET_NETWORK_FDSet * ws,
1105 GNUNET_SCHEDULER_Task task, void *task_cls)
1109 void *backtrace_array[MAX_TRACE_DEPTH];
1112 GNUNET_assert (NULL != task);
1113 t = GNUNET_malloc (sizeof (struct Task));
1115 t->callback_cls = task_cls;
1117 t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1118 t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1122 t->read_set = GNUNET_NETWORK_fdset_create ();
1123 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1127 t->write_set = GNUNET_NETWORK_fdset_create ();
1128 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1130 t->id = ++sched->last_id;
1131 t->prereq_id = prerequisite_task;
1132 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1134 check_priority ((prio ==
1135 GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1137 t->next = sched->pending;
1140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141 "Adding task: %llu / %p\n", t->id, t->callback_cls);
1146 /* end of scheduler.c */