2 This file is part of GNUnet
3 Copyright (C) 2009-2017 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @file util/scheduler.c
22 * @brief schedule computations using continuation passing style
23 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
31 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
33 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
40 * Use lsof to generate file descriptor reports on select error?
41 * (turn off for stable releases).
43 #define USE_LSOF GNUNET_NO
46 * Obtain trace information for all scheduler calls that schedule tasks.
48 #define EXECINFO GNUNET_NO
51 * Check each file descriptor before adding
53 #define DEBUG_FDS GNUNET_NO
56 * Depth of the traces collected via EXECINFO.
58 #define MAX_TRACE_DEPTH 50
62 * Should we figure out which tasks are delayed for a while
63 * before they are run? (Consider using in combination with EXECINFO).
65 #define PROFILE_DELAYS GNUNET_NO
68 * Task that were in the queue for longer than this are reported if
69 * PROFILE_DELAYS is active.
71 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
75 * Argument to be passed from the driver to
76 * #GNUNET_SCHEDULER_do_work(). Contains the
77 * scheduler's internal state.
79 struct GNUNET_SCHEDULER_Handle
82 * Passed here to avoid constantly allocating/deallocating
83 * this element, but generally we want to get rid of this.
86 struct GNUNET_NETWORK_FDSet *rs;
89 * Passed here to avoid constantly allocating/deallocating
90 * this element, but generally we want to get rid of this.
93 struct GNUNET_NETWORK_FDSet *ws;
96 * context of the SIGINT handler
98 struct GNUNET_SIGNAL_Context *shc_int;
101 * context of the SIGTERM handler
103 struct GNUNET_SIGNAL_Context *shc_term;
105 #if (SIGTERM != GNUNET_TERM_SIG)
107 * context of the TERM_SIG handler
109 struct GNUNET_SIGNAL_Context *shc_gterm;
114 * context of the SIGQUIT handler
116 struct GNUNET_SIGNAL_Context *shc_quit;
119 * context of the SIGHUP handler
121 struct GNUNET_SIGNAL_Context *shc_hup;
124 * context of hte SIGPIPE handler
126 struct GNUNET_SIGNAL_Context *shc_pipe;
132 * Entry in list of pending tasks.
134 struct GNUNET_SCHEDULER_Task
137 * This is a linked list.
139 struct GNUNET_SCHEDULER_Task *next;
142 * This is a linked list.
144 struct GNUNET_SCHEDULER_Task *prev;
147 * Function to run when ready.
149 GNUNET_SCHEDULER_TaskCallback callback;
152 * Closure for the @e callback.
157 * Information about which FDs are ready for this task (and why).
159 struct GNUNET_SCHEDULER_FdInfo *fds;
162 * Storage location used for @e fds if we want to avoid
163 * a separate malloc() call in the common case that this
164 * task is only about a single FD.
166 struct GNUNET_SCHEDULER_FdInfo fdx;
169 * Size of the @e fds array.
171 unsigned int fds_len;
174 * Do we own the network and file handles referenced by the FdInfo
175 * structs in the fds array. This will only be GNUNET_YES if the
176 * task was created by the #GNUNET_SCHEDULER_add_select function.
181 * Absolute timeout value for the task, or
182 * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
184 struct GNUNET_TIME_Absolute timeout;
188 * When was the task scheduled?
190 struct GNUNET_TIME_Absolute start_time;
194 * Why is the task ready? Set after task is added to ready queue.
195 * Initially set to zero. All reasons that have already been
196 * satisfied (i.e. read or write ready) will be set over time.
198 enum GNUNET_SCHEDULER_Reason reason;
203 enum GNUNET_SCHEDULER_Priority priority;
206 * Set if we only wait for reading from a single FD, otherwise -1.
211 * Set if we only wait for writing to a single FD, otherwise -1.
216 * Should the existence of this task in the queue be counted as
217 * reason to not shutdown the scheduler?
222 * Is this task run on shutdown?
227 * Is this task in the ready list?
233 * Array of strings which make up a backtrace from the point when this
234 * task was scheduled (essentially, who scheduled the task?)
236 char **backtrace_strings;
239 * Size of the backtrace_strings array
241 int num_backtrace_strings;
245 * Asynchronous scope of the task that scheduled this scope,
247 struct GNUNET_AsyncScopeSave scope;
253 * A struct representing an event the select driver is waiting for
257 struct Scheduled *prev;
259 struct Scheduled *next;
262 * the task, the event is related to
264 struct GNUNET_SCHEDULER_Task *task;
267 * information about the network socket / file descriptor where
268 * the event is expected to occur
270 struct GNUNET_SCHEDULER_FdInfo *fdi;
273 * the event types (multiple event types can be ORed) the select
274 * driver is expected to wait for
276 enum GNUNET_SCHEDULER_EventType et;
281 * Driver context used by GNUNET_SCHEDULER_run
286 * the head of a DLL containing information about the events the
287 * select driver is waiting for
289 struct Scheduled *scheduled_head;
292 * the tail of a DLL containing information about the events the
293 * select driver is waiting for
295 struct Scheduled *scheduled_tail;
298 * the time when the select driver will wake up again (after
301 struct GNUNET_TIME_Absolute timeout;
306 * The driver used for the event loop. Will be handed over to
307 * the scheduler in #GNUNET_SCHEDULER_do_work(), persisted
308 * there in this variable for later use in functions like
309 * #GNUNET_SCHEDULER_add_select(), #add_without_sets() and
310 * #GNUNET_SCHEDULER_cancel().
312 static const struct GNUNET_SCHEDULER_Driver *scheduler_driver;
315 * Head of list of tasks waiting for an event.
317 static struct GNUNET_SCHEDULER_Task *pending_head;
320 * Tail of list of tasks waiting for an event.
322 static struct GNUNET_SCHEDULER_Task *pending_tail;
325 * Head of list of tasks waiting for shutdown.
327 static struct GNUNET_SCHEDULER_Task *shutdown_head;
330 * Tail of list of tasks waiting for shutdown.
332 static struct GNUNET_SCHEDULER_Task *shutdown_tail;
335 * List of tasks waiting ONLY for a timeout event.
336 * Sorted by timeout (earliest first). Used so that
337 * we do not traverse the list of these tasks when
338 * building select sets (we just look at the head
339 * to determine the respective timeout ONCE).
341 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
344 * List of tasks waiting ONLY for a timeout event.
345 * Sorted by timeout (earliest first). Used so that
346 * we do not traverse the list of these tasks when
347 * building select sets (we just look at the head
348 * to determine the respective timeout ONCE).
350 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
353 * Last inserted task waiting ONLY for a timeout event.
354 * Used to (heuristically) speed up insertion.
356 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
359 * ID of the task that is running right now.
361 static struct GNUNET_SCHEDULER_Task *active_task;
364 * Head of list of tasks ready to run right now, grouped by importance.
366 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
369 * Tail of list of tasks ready to run right now, grouped by importance.
371 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
374 * Task for installing parent control handlers (it might happen that the
375 * scheduler is shutdown before this task is executed, so
376 * GNUNET_SCHEDULER_shutdown must cancel it in that case)
378 static struct GNUNET_SCHEDULER_Task *install_parent_control_task;
381 * Task for reading from a pipe that signal handlers will use to initiate
384 static struct GNUNET_SCHEDULER_Task *shutdown_pipe_task;
387 * Number of tasks on the ready list.
389 static unsigned int ready_count;
392 * Priority of the task running right now. Only
393 * valid while a task is running.
395 static enum GNUNET_SCHEDULER_Priority current_priority;
398 * Priority of the highest task added in the current select
401 static enum GNUNET_SCHEDULER_Priority max_priority_added;
404 * Value of the 'lifeness' flag for the current task.
406 static int current_lifeness;
409 * Function to use as a select() in the scheduler.
410 * If NULL, we use GNUNET_NETWORK_socket_select().
412 static GNUNET_SCHEDULER_select scheduler_select;
415 * Task context of the current task.
417 static struct GNUNET_SCHEDULER_TaskContext tc;
420 * Closure for #scheduler_select.
422 static void *scheduler_select_cls;
426 * Sets the select function to use in the scheduler (scheduler_select).
428 * @param new_select new select function to use
429 * @param new_select_cls closure for @a new_select
430 * @return previously used select function, NULL for default
433 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
434 void *new_select_cls)
436 scheduler_select = new_select;
437 scheduler_select_cls = new_select_cls;
442 * Check that the given priority is legal (and return it).
444 * @param p priority value to check
445 * @return p on success, 0 on error
447 static enum GNUNET_SCHEDULER_Priority
448 check_priority (enum GNUNET_SCHEDULER_Priority p)
450 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
453 return 0; /* make compiler happy */
458 * chooses the nearest timeout from all pending tasks, to be used
459 * to tell the driver the next wakeup time (using its set_wakeup
462 struct GNUNET_TIME_Absolute
465 struct GNUNET_SCHEDULER_Task *pos;
466 struct GNUNET_TIME_Absolute now;
467 struct GNUNET_TIME_Absolute timeout;
469 pos = pending_timeout_head;
470 now = GNUNET_TIME_absolute_get ();
471 timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
474 if (0 != pos->reason)
480 timeout = pos->timeout;
483 for (pos = pending_head; NULL != pos; pos = pos->next)
485 if (0 != pos->reason)
489 else if ((pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
490 (timeout.abs_value_us > pos->timeout.abs_value_us))
492 timeout = pos->timeout;
500 * Put a task that is ready for execution into the ready queue.
502 * @param task task ready for execution
505 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
507 enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
509 GNUNET_CONTAINER_DLL_insert (ready_head[p],
512 task->in_ready_list = GNUNET_YES;
518 * Request the shutdown of a scheduler. Marks all tasks
519 * awaiting shutdown as ready. Note that tasks
520 * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
521 * will be delayed until the next shutdown signal.
524 GNUNET_SCHEDULER_shutdown ()
526 struct GNUNET_SCHEDULER_Task *pos;
528 LOG (GNUNET_ERROR_TYPE_DEBUG,
529 "GNUNET_SCHEDULER_shutdown\n");
530 if (NULL != install_parent_control_task)
532 GNUNET_SCHEDULER_cancel (install_parent_control_task);
533 install_parent_control_task = NULL;
535 if (NULL != shutdown_pipe_task)
537 GNUNET_SCHEDULER_cancel (shutdown_pipe_task);
538 shutdown_pipe_task = NULL;
540 while (NULL != (pos = shutdown_head))
542 GNUNET_CONTAINER_DLL_remove (shutdown_head,
545 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
546 queue_ready_task (pos);
552 * Output stack trace of task @a t.
554 * @param t task to dump stack trace of
557 dump_backtrace (struct GNUNET_SCHEDULER_Task *t)
560 for (unsigned int i = 0; i < t->num_backtrace_strings; i++)
561 LOG (GNUNET_ERROR_TYPE_WARNING,
562 "Task %p trace %u: %s\n",
565 t->backtrace_strings[i]);
573 * Destroy a task (release associated resources)
575 * @param t task to destroy
578 destroy_task (struct GNUNET_SCHEDULER_Task *t)
582 LOG (GNUNET_ERROR_TYPE_DEBUG,
583 "destroying task %p\n",
586 if (GNUNET_YES == t->own_handles)
588 for (i = 0; i != t->fds_len; ++i)
590 const struct GNUNET_NETWORK_Handle *fd = t->fds[i].fd;
591 const struct GNUNET_DISK_FileHandle *fh = t->fds[i].fh;
594 GNUNET_NETWORK_socket_free_memory_only_ ((struct GNUNET_NETWORK_Handle *) fd);
598 // FIXME: on WIN32 this is not enough! A function
599 // GNUNET_DISK_file_free_memory_only would be nice
600 GNUNET_free ((void *) fh);
606 GNUNET_array_grow (t->fds, t->fds_len, 0);
609 GNUNET_free (t->backtrace_strings);
616 * Pipe used to communicate shutdown via signal.
618 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
621 * Process ID of this process at the time we installed the various
627 * Signal handler called for SIGPIPE.
639 // * Wait for a short time.
640 // * Sleeps for @a ms ms (as that should be long enough for virtually all
641 // * modern systems to context switch and allow another process to do
642 // * some 'real' work).
644 // * @param ms how many ms to wait
647 //short_wait (unsigned int ms)
649 // struct GNUNET_TIME_Relative timeout;
651 // timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
652 // (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
657 * Signal handler called for signals that should cause us to shutdown.
660 sighandler_shutdown ()
663 int old_errno = errno; /* backup errno */
665 if (getpid () != my_pid)
666 _exit (1); /* we have fork'ed since the signal handler was created,
667 * ignore the signal, see https://gnunet.org/vfork discussion */
668 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
669 (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
676 shutdown_if_no_lifeness ()
678 struct GNUNET_SCHEDULER_Task *t;
682 for (t = pending_head; NULL != t; t = t->next)
683 if (GNUNET_YES == t->lifeness)
685 for (t = shutdown_head; NULL != t; t = t->next)
686 if (GNUNET_YES == t->lifeness)
688 for (t = pending_timeout_head; NULL != t; t = t->next)
689 if (GNUNET_YES == t->lifeness)
692 GNUNET_SCHEDULER_shutdown ();
697 select_loop (struct GNUNET_SCHEDULER_Handle *sh,
698 struct DriverContext *context);
702 * Initialize and run scheduler. This function will return when all
703 * tasks have completed. On systems with signals, receiving a SIGTERM
704 * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
705 * to be run after the active task is complete. As a result, SIGTERM
706 * causes all active tasks to be scheduled with reason
707 * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added
708 * afterwards will execute normally!). Note that any particular signal
709 * will only shut down one scheduler; applications should always only
710 * create a single scheduler.
712 * @param task task to run immediately
713 * @param task_cls closure of @a task
716 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
719 struct GNUNET_SCHEDULER_Handle *sh;
720 struct GNUNET_SCHEDULER_Driver *driver;
721 struct DriverContext context = {.scheduled_head = NULL,
722 .scheduled_tail = NULL,
723 .timeout = GNUNET_TIME_absolute_get ()};
725 driver = GNUNET_SCHEDULER_driver_select ();
726 driver->cls = &context;
727 sh = GNUNET_SCHEDULER_driver_init (driver);
728 GNUNET_SCHEDULER_add_with_reason_and_priority (task,
730 GNUNET_SCHEDULER_REASON_STARTUP,
731 GNUNET_SCHEDULER_PRIORITY_DEFAULT);
734 GNUNET_SCHEDULER_driver_done (sh);
735 GNUNET_free (driver);
740 * Obtain the task context, giving the reason why the current task was
743 * @return current tasks' scheduler context
745 const struct GNUNET_SCHEDULER_TaskContext *
746 GNUNET_SCHEDULER_get_task_context ()
748 GNUNET_assert (NULL != active_task);
754 * Get information about the current load of this scheduler. Use this
755 * function to determine if an elective task should be added or simply
756 * dropped (if the decision should be made based on the number of
757 * tasks ready to run).
759 * @param p priority level to look at
760 * @return number of tasks pending right now
763 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
765 struct GNUNET_SCHEDULER_Task *pos;
768 GNUNET_assert (NULL != active_task);
769 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
771 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
772 p = current_priority;
774 for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
781 init_fd_info (struct GNUNET_SCHEDULER_Task *t,
782 const struct GNUNET_NETWORK_Handle *const *read_nh,
783 unsigned int read_nh_len,
784 const struct GNUNET_NETWORK_Handle *const *write_nh,
785 unsigned int write_nh_len,
786 const struct GNUNET_DISK_FileHandle *const *read_fh,
787 unsigned int read_fh_len,
788 const struct GNUNET_DISK_FileHandle *const *write_fh,
789 unsigned int write_fh_len)
791 // FIXME: if we have exactly two network handles / exactly two file handles
792 // and they are equal, we can make one FdInfo with both
793 // GNUNET_SCHEDULER_ET_IN and GNUNET_SCHEDULER_ET_OUT set.
794 struct GNUNET_SCHEDULER_FdInfo *fdi;
796 t->fds_len = read_nh_len + write_nh_len + read_fh_len + write_fh_len;
801 if (1 == read_nh_len)
803 GNUNET_assert (NULL != read_nh);
804 GNUNET_assert (NULL != *read_nh);
806 fdi->et = GNUNET_SCHEDULER_ET_IN;
807 fdi->sock = GNUNET_NETWORK_get_fd (*read_nh);
808 t->read_fd = fdi->sock;
811 else if (1 == write_nh_len)
813 GNUNET_assert (NULL != write_nh);
814 GNUNET_assert (NULL != *write_nh);
816 fdi->et = GNUNET_SCHEDULER_ET_OUT;
817 fdi->sock = GNUNET_NETWORK_get_fd (*write_nh);
819 t->write_fd = fdi->sock;
821 else if (1 == read_fh_len)
823 GNUNET_assert (NULL != read_fh);
824 GNUNET_assert (NULL != *read_fh);
826 fdi->et = GNUNET_SCHEDULER_ET_IN;
827 fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
828 t->read_fd = fdi->sock;
833 GNUNET_assert (NULL != write_fh);
834 GNUNET_assert (NULL != *write_fh);
836 fdi->et = GNUNET_SCHEDULER_ET_OUT;
837 fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
839 t->write_fd = fdi->sock;
844 fdi = GNUNET_new_array (t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
849 for (i = 0; i != read_nh_len; ++i)
851 fdi->fd = read_nh[i];
852 GNUNET_assert (NULL != fdi->fd);
853 fdi->et = GNUNET_SCHEDULER_ET_IN;
854 fdi->sock = GNUNET_NETWORK_get_fd (read_nh[i]);
857 for (i = 0; i != write_nh_len; ++i)
859 fdi->fd = write_nh[i];
860 GNUNET_assert (NULL != fdi->fd);
861 fdi->et = GNUNET_SCHEDULER_ET_OUT;
862 fdi->sock = GNUNET_NETWORK_get_fd (write_nh[i]);
865 for (i = 0; i != read_fh_len; ++i)
867 fdi->fh = read_fh[i];
868 GNUNET_assert (NULL != fdi->fh);
869 fdi->et = GNUNET_SCHEDULER_ET_IN;
870 fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
873 for (i = 0; i != write_fh_len; ++i)
875 fdi->fh = write_fh[i];
876 GNUNET_assert (NULL != fdi->fh);
877 fdi->et = GNUNET_SCHEDULER_ET_OUT;
878 fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
886 * calls the given function @a func on each FdInfo related to @a t.
887 * Optionally updates the event type field in each FdInfo after calling
891 * @param driver_func the function to call with each FdInfo contained in
893 * @param if_not_ready only call @a driver_func on FdInfos that are not
895 * @param et the event type to be set in each FdInfo after calling
896 * @a driver_func on it, or -1 if no updating not desired.
899 driver_add_multiple (struct GNUNET_SCHEDULER_Task *t)
901 struct GNUNET_SCHEDULER_FdInfo *fdi;
902 int success = GNUNET_YES;
904 for (unsigned int i = 0; i != t->fds_len; ++i)
907 success = scheduler_driver->add (scheduler_driver->cls,
910 fdi->et = GNUNET_SCHEDULER_ET_NONE;
912 if (GNUNET_YES != success)
914 LOG (GNUNET_ERROR_TYPE_ERROR,
915 "driver could not add task\n");
921 install_parent_control_handler (void *cls)
924 install_parent_control_task = NULL;
925 GNUNET_OS_install_parent_control_handler (NULL);
930 shutdown_pipe_cb (void *cls)
933 const struct GNUNET_DISK_FileHandle *pr;
936 shutdown_pipe_task = NULL;
937 pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
938 GNUNET_DISK_PIPE_END_READ);
939 GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
940 /* consume the signal */
941 GNUNET_DISK_file_read (pr, &c, sizeof (c));
942 /* mark all active tasks as ready due to shutdown */
943 GNUNET_SCHEDULER_shutdown ();
945 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
953 * Cancel the task with the specified identifier.
954 * The task must not yet have run. Only allowed to be called as long as the
955 * scheduler is running, that is one of the following conditions is met:
957 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
958 * - #GNUNET_SCHEDULER_driver_init has been run and
959 * #GNUNET_SCHEDULER_driver_done has not been called yet
961 * @param task id of the task to cancel
962 * @return original closure of the task
965 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
967 enum GNUNET_SCHEDULER_Priority p;
971 LOG (GNUNET_ERROR_TYPE_DEBUG,
972 "canceling task %p\n",
975 /* scheduler must be running */
976 GNUNET_assert (NULL != scheduler_driver);
977 is_fd_task = (NULL != task->fds);
980 int del_result = scheduler_driver->del (scheduler_driver->cls, task);
981 if (GNUNET_OK != del_result)
983 LOG (GNUNET_ERROR_TYPE_ERROR,
984 "driver could not delete task\n");
988 if (! task->in_ready_list)
992 GNUNET_CONTAINER_DLL_remove (pending_head,
996 else if (GNUNET_YES == task->on_shutdown)
998 GNUNET_CONTAINER_DLL_remove (shutdown_head,
1004 GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1005 pending_timeout_tail,
1007 if (pending_timeout_last == task)
1008 pending_timeout_last = NULL;
1013 p = check_priority (task->priority);
1014 GNUNET_CONTAINER_DLL_remove (ready_head[p],
1019 ret = task->callback_cls;
1020 destroy_task (task);
1026 * Initialize backtrace data for task @a t
1028 * @param t task to initialize
1031 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
1034 void *backtrace_array[MAX_TRACE_DEPTH];
1036 t->num_backtrace_strings
1037 = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1038 t->backtrace_strings =
1039 backtrace_symbols (backtrace_array,
1040 t->num_backtrace_strings);
1049 * Continue the current execution with the given function. This is
1050 * similar to the other "add" functions except that there is no delay
1051 * and the reason code can be specified.
1053 * @param task main function of the task
1054 * @param task_cls closure for @a task
1055 * @param reason reason for task invocation
1056 * @param priority priority to use for the task
1059 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1061 enum GNUNET_SCHEDULER_Reason reason,
1062 enum GNUNET_SCHEDULER_Priority priority)
1064 struct GNUNET_SCHEDULER_Task *t;
1066 /* scheduler must be running */
1067 GNUNET_assert (NULL != scheduler_driver);
1068 GNUNET_assert (NULL != task);
1069 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1073 t->callback_cls = task_cls;
1075 t->start_time = GNUNET_TIME_absolute_get ();
1078 t->priority = check_priority (priority);
1079 t->lifeness = current_lifeness;
1080 LOG (GNUNET_ERROR_TYPE_DEBUG,
1081 "Adding continuation task %p\n",
1084 queue_ready_task (t);
1089 * Schedule a new task to be run at the specified time. The task
1090 * will be scheduled for execution at time @a at.
1092 * @param at time when the operation should run
1093 * @param priority priority to use for the task
1094 * @param task main function of the task
1095 * @param task_cls closure of @a task
1096 * @return unique task identifier for the job
1097 * only valid until @a task is started!
1099 struct GNUNET_SCHEDULER_Task *
1100 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1101 enum GNUNET_SCHEDULER_Priority priority,
1102 GNUNET_SCHEDULER_TaskCallback task,
1105 struct GNUNET_SCHEDULER_Task *t;
1106 struct GNUNET_SCHEDULER_Task *pos;
1107 struct GNUNET_SCHEDULER_Task *prev;
1109 /* scheduler must be running */
1110 GNUNET_assert (NULL != scheduler_driver);
1111 GNUNET_assert (NULL != task);
1112 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1113 GNUNET_async_scope_get (&t->scope);
1115 t->callback_cls = task_cls;
1119 t->start_time = GNUNET_TIME_absolute_get ();
1122 t->priority = check_priority (priority);
1123 t->lifeness = current_lifeness;
1124 /* try tail first (optimization in case we are
1125 * appending to a long list of tasks with timeouts) */
1126 if ( (NULL == pending_timeout_head) ||
1127 (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
1129 GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1130 pending_timeout_tail,
1135 /* first move from heuristic start backwards to before start time */
1136 prev = pending_timeout_last;
1137 while ( (NULL != prev) &&
1138 (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1140 /* now, move from heuristic start (or head of list) forward to insertion point */
1142 pos = pending_timeout_head;
1145 while ((NULL != pos) && (pos->timeout.abs_value_us <= t->timeout.abs_value_us))
1150 GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1151 pending_timeout_tail,
1155 /* finally, update heuristic insertion point to last insertion... */
1156 pending_timeout_last = t;
1158 LOG (GNUNET_ERROR_TYPE_DEBUG,
1167 * Schedule a new task to be run with a specified delay. The task
1168 * will be scheduled for execution once the delay has expired.
1170 * @param delay when should this operation time out?
1171 * @param priority priority to use for the task
1172 * @param task main function of the task
1173 * @param task_cls closure of @a task
1174 * @return unique task identifier for the job
1175 * only valid until @a task is started!
1177 struct GNUNET_SCHEDULER_Task *
1178 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1179 enum GNUNET_SCHEDULER_Priority priority,
1180 GNUNET_SCHEDULER_TaskCallback task,
1183 return GNUNET_SCHEDULER_add_at_with_priority (GNUNET_TIME_relative_to_absolute (delay),
1191 * Schedule a new task to be run with a specified priority.
1193 * @param prio how important is the new task?
1194 * @param task main function of the task
1195 * @param task_cls closure of @a task
1196 * @return unique task identifier for the job
1197 * only valid until @a task is started!
1199 struct GNUNET_SCHEDULER_Task *
1200 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1201 GNUNET_SCHEDULER_TaskCallback task,
1204 return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1212 * Schedule a new task to be run at the specified time. The task
1213 * will be scheduled for execution once specified time has been
1214 * reached. It will be run with the DEFAULT priority.
1216 * @param at time at which this operation should run
1217 * @param task main function of the task
1218 * @param task_cls closure of @a task
1219 * @return unique task identifier for the job
1220 * only valid until @a task is started!
1222 struct GNUNET_SCHEDULER_Task *
1223 GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1224 GNUNET_SCHEDULER_TaskCallback task,
1227 return GNUNET_SCHEDULER_add_at_with_priority (at,
1228 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1235 * Schedule a new task to be run with a specified delay. The task
1236 * will be scheduled for execution once the delay has expired. It
1237 * will be run with the DEFAULT priority.
1239 * @param delay when should this operation time out?
1240 * @param task main function of the task
1241 * @param task_cls closure of @a task
1242 * @return unique task identifier for the job
1243 * only valid until @a task is started!
1245 struct GNUNET_SCHEDULER_Task *
1246 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1247 GNUNET_SCHEDULER_TaskCallback task,
1250 return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1251 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1258 * Schedule a new task to be run as soon as possible. Note that this
1259 * does not guarantee that this will be the next task that is being
1260 * run, as other tasks with higher priority (or that are already ready
1261 * to run) might get to run first. Just as with delays, clients must
1262 * not rely on any particular order of execution between tasks
1263 * scheduled concurrently.
1265 * The task will be run with the DEFAULT priority.
1267 * @param task main function of the task
1268 * @param task_cls closure of @a task
1269 * @return unique task identifier for the job
1270 * only valid until @a task is started!
1272 struct GNUNET_SCHEDULER_Task *
1273 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1276 return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
1283 * Schedule a new task to be run on shutdown, that is when a CTRL-C
1284 * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1287 * @param task main function of the task
1288 * @param task_cls closure of @a task
1289 * @return unique task identifier for the job
1290 * only valid until @a task is started!
1292 struct GNUNET_SCHEDULER_Task *
1293 GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1296 struct GNUNET_SCHEDULER_Task *t;
1298 /* scheduler must be running */
1299 GNUNET_assert (NULL != scheduler_driver);
1300 GNUNET_assert (NULL != task);
1301 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1302 GNUNET_async_scope_get (&t->scope);
1304 t->callback_cls = task_cls;
1308 t->start_time = GNUNET_TIME_absolute_get ();
1310 t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1311 t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1312 t->on_shutdown = GNUNET_YES;
1313 t->lifeness = GNUNET_NO;
1314 GNUNET_CONTAINER_DLL_insert (shutdown_head,
1317 LOG (GNUNET_ERROR_TYPE_DEBUG,
1318 "Adding shutdown task %p\n",
1326 * Schedule a new task to be run as soon as possible with the
1327 * (transitive) ignore-shutdown flag either explicitly set or
1328 * explicitly enabled. This task (and all tasks created from it,
1329 * other than by another call to this function) will either count or
1330 * not count for the "lifeness" of the process. This API is only
1331 * useful in a few special cases.
1333 * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1334 * @param task main function of the task
1335 * @param task_cls closure of @a task
1336 * @return unique task identifier for the job
1337 * only valid until @a task is started!
1339 struct GNUNET_SCHEDULER_Task *
1340 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1341 GNUNET_SCHEDULER_TaskCallback task,
1344 struct GNUNET_SCHEDULER_Task *ret;
1346 ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1347 ret->lifeness = lifeness;
1354 * check a raw file descriptor and abort if it is bad (for debugging purposes)
1356 * @param t the task related to the file descriptor
1357 * @param raw_fd the raw file descriptor to check
1360 check_fd (struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1364 int flags = fcntl (raw_fd, F_GETFD);
1366 if ((flags == -1) && (errno == EBADF))
1368 LOG (GNUNET_ERROR_TYPE_ERROR,
1369 "Got invalid file descriptor %d!\n",
1380 * Schedule a new task to be run with a specified delay or when any of
1381 * the specified file descriptor sets is ready. The delay can be used
1382 * as a timeout on the socket(s) being ready. The task will be
1383 * scheduled for execution once either the delay has expired or any of
1384 * the socket operations is ready. This is the most general
1385 * function of the "add" family. Note that the "prerequisite_task"
1386 * must be satisfied in addition to any of the other conditions. In
1387 * other words, the task will be started when
1389 * (prerequisite-run)
1395 * @param delay how long should we wait?
1396 * @param priority priority to use
1397 * @param rfd file descriptor we want to read (can be -1)
1398 * @param wfd file descriptors we want to write (can be -1)
1399 * @param task main function of the task
1400 * @param task_cls closure of @a task
1401 * @return unique task identifier for the job
1402 * only valid until @a task is started!
1405 static struct GNUNET_SCHEDULER_Task *
1406 add_without_sets (struct GNUNET_TIME_Relative delay,
1407 enum GNUNET_SCHEDULER_Priority priority,
1408 const struct GNUNET_NETWORK_Handle *read_nh,
1409 const struct GNUNET_NETWORK_Handle *write_nh,
1410 const struct GNUNET_DISK_FileHandle *read_fh,
1411 const struct GNUNET_DISK_FileHandle *write_fh,
1412 GNUNET_SCHEDULER_TaskCallback task,
1415 struct GNUNET_SCHEDULER_Task *t;
1417 /* scheduler must be running */
1418 GNUNET_assert (NULL != scheduler_driver);
1419 GNUNET_assert (NULL != task);
1420 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1421 GNUNET_async_scope_get (&t->scope);
1432 t->callback_cls = task_cls;
1434 check_fd (t, NULL != read_nh ? GNUNET_NETWORK_get_fd (read_nh) : -1);
1435 check_fd (t, NULL != write_nh ? GNUNET_NETWORK_get_fd (write_nh) : -1);
1436 check_fd (t, NULL != read_fh ? read_fh->fd : -1);
1437 check_fd (t, NULL != write_fh ? write_fh->fd : -1);
1440 t->start_time = GNUNET_TIME_absolute_get ();
1442 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1443 t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1444 t->lifeness = current_lifeness;
1445 GNUNET_CONTAINER_DLL_insert (pending_head,
1448 driver_add_multiple (t);
1449 max_priority_added = GNUNET_MAX (max_priority_added,
1458 * Schedule a new task to be run with a specified delay or when the
1459 * specified file descriptor is ready for reading. The delay can be
1460 * used as a timeout on the socket being ready. The task will be
1461 * scheduled for execution once either the delay has expired or the
1462 * socket operation is ready. It will be run with the DEFAULT priority.
1463 * Only allowed to be called as long as the scheduler is running, that
1464 * is one of the following conditions is met:
1466 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1467 * - #GNUNET_SCHEDULER_driver_init has been run and
1468 * #GNUNET_SCHEDULER_driver_done has not been called yet
1470 * @param delay when should this operation time out?
1471 * @param rfd read file-descriptor
1472 * @param task main function of the task
1473 * @param task_cls closure of @a task
1474 * @return unique task identifier for the job
1475 * only valid until @a task is started!
1477 struct GNUNET_SCHEDULER_Task *
1478 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1479 struct GNUNET_NETWORK_Handle *rfd,
1480 GNUNET_SCHEDULER_TaskCallback task,
1483 return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1484 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1485 rfd, task, task_cls);
1490 * Schedule a new task to be run with a specified priority and to be
1491 * run after the specified delay or when the specified file descriptor
1492 * is ready for reading. The delay can be used as a timeout on the
1493 * socket being ready. The task will be scheduled for execution once
1494 * either the delay has expired or the socket operation is ready. It
1495 * will be run with the DEFAULT priority.
1496 * Only allowed to be called as long as the scheduler is running, that
1497 * is one of the following conditions is met:
1499 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1500 * - #GNUNET_SCHEDULER_driver_init has been run and
1501 * #GNUNET_SCHEDULER_driver_done has not been called yet
1503 * @param delay when should this operation time out?
1504 * @param priority priority to use for the task
1505 * @param rfd read file-descriptor
1506 * @param task main function of the task
1507 * @param task_cls closure of @a task
1508 * @return unique task identifier for the job
1509 * only valid until @a task is started!
1511 struct GNUNET_SCHEDULER_Task *
1512 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1513 enum GNUNET_SCHEDULER_Priority priority,
1514 struct GNUNET_NETWORK_Handle *rfd,
1515 GNUNET_SCHEDULER_TaskCallback task,
1518 return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1527 * Schedule a new task to be run with a specified delay or when the
1528 * specified file descriptor is ready for writing. The delay can be
1529 * used as a timeout on the socket being ready. The task will be
1530 * scheduled for execution once either the delay has expired or the
1531 * socket operation is ready. It will be run with the priority of
1533 * Only allowed to be called as long as the scheduler is running, that
1534 * is one of the following conditions is met:
1536 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1537 * - #GNUNET_SCHEDULER_driver_init has been run and
1538 * #GNUNET_SCHEDULER_driver_done has not been called yet
1540 * @param delay when should this operation time out?
1541 * @param wfd write file-descriptor
1542 * @param task main function of the task
1543 * @param task_cls closure of @a task
1544 * @return unique task identifier for the job
1545 * only valid until @a task is started!
1547 struct GNUNET_SCHEDULER_Task *
1548 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1549 struct GNUNET_NETWORK_Handle *wfd,
1550 GNUNET_SCHEDULER_TaskCallback task,
1553 return GNUNET_SCHEDULER_add_net_with_priority (delay,
1554 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1556 GNUNET_NO, GNUNET_YES,
1561 * Schedule a new task to be run with a specified delay or when the
1562 * specified file descriptor is ready. The delay can be
1563 * used as a timeout on the socket being ready. The task will be
1564 * scheduled for execution once either the delay has expired or the
1565 * socket operation is ready.
1566 * Only allowed to be called as long as the scheduler is running, that
1567 * is one of the following conditions is met:
1569 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1570 * - #GNUNET_SCHEDULER_driver_init has been run and
1571 * #GNUNET_SCHEDULER_driver_done has not been called yet
1573 * @param delay when should this operation time out?
1574 * @param priority priority of the task
1575 * @param fd file-descriptor
1576 * @param on_read whether to poll the file-descriptor for readability
1577 * @param on_write whether to poll the file-descriptor for writability
1578 * @param task main function of the task
1579 * @param task_cls closure of task
1580 * @return unique task identifier for the job
1581 * only valid until "task" is started!
1583 struct GNUNET_SCHEDULER_Task *
1584 GNUNET_SCHEDULER_add_net_with_priority (struct GNUNET_TIME_Relative delay,
1585 enum GNUNET_SCHEDULER_Priority priority,
1586 struct GNUNET_NETWORK_Handle *fd,
1589 GNUNET_SCHEDULER_TaskCallback task,
1592 /* scheduler must be running */
1593 GNUNET_assert (NULL != scheduler_driver);
1596 struct GNUNET_NETWORK_FDSet *s;
1597 struct GNUNET_SCHEDULER_Task * ret;
1599 GNUNET_assert (NULL != fd);
1600 s = GNUNET_NETWORK_fdset_create ();
1601 GNUNET_NETWORK_fdset_set (s, fd);
1602 ret = GNUNET_SCHEDULER_add_select (
1605 on_write ? s : NULL,
1607 GNUNET_NETWORK_fdset_destroy (s);
1610 GNUNET_assert (on_read || on_write);
1611 GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1612 return add_without_sets (delay, priority,
1613 on_read ? fd : NULL,
1614 on_write ? fd : NULL,
1623 * Schedule a new task to be run with a specified delay or when the
1624 * specified file descriptor is ready for reading. The delay can be
1625 * used as a timeout on the socket being ready. The task will be
1626 * scheduled for execution once either the delay has expired or the
1627 * socket operation is ready. It will be run with the DEFAULT priority.
1628 * Only allowed to be called as long as the scheduler is running, that
1629 * is one of the following conditions is met:
1631 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1632 * - #GNUNET_SCHEDULER_driver_init has been run and
1633 * #GNUNET_SCHEDULER_driver_done has not been called yet
1635 * @param delay when should this operation time out?
1636 * @param rfd read file-descriptor
1637 * @param task main function of the task
1638 * @param task_cls closure of @a task
1639 * @return unique task identifier for the job
1640 * only valid until @a task is started!
1642 struct GNUNET_SCHEDULER_Task *
1643 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1644 const struct GNUNET_DISK_FileHandle *rfd,
1645 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1647 return GNUNET_SCHEDULER_add_file_with_priority (
1648 delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1649 rfd, GNUNET_YES, GNUNET_NO,
1655 * Schedule a new task to be run with a specified delay or when the
1656 * specified file descriptor is ready for writing. The delay can be
1657 * used as a timeout on the socket being ready. The task will be
1658 * scheduled for execution once either the delay has expired or the
1659 * socket operation is ready. It will be run with the DEFAULT priority.
1660 * Only allowed to be called as long as the scheduler is running, that
1661 * is one of the following conditions is met:
1663 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1664 * - #GNUNET_SCHEDULER_driver_init has been run and
1665 * #GNUNET_SCHEDULER_driver_done has not been called yet
1667 * @param delay when should this operation time out?
1668 * @param wfd write file-descriptor
1669 * @param task main function of the task
1670 * @param task_cls closure of @a task
1671 * @return unique task identifier for the job
1672 * only valid until @a task is started!
1674 struct GNUNET_SCHEDULER_Task *
1675 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1676 const struct GNUNET_DISK_FileHandle *wfd,
1677 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1679 return GNUNET_SCHEDULER_add_file_with_priority (
1680 delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1681 wfd, GNUNET_NO, GNUNET_YES,
1687 * Schedule a new task to be run with a specified delay or when the
1688 * specified file descriptor is ready. The delay can be
1689 * used as a timeout on the socket being ready. The task will be
1690 * scheduled for execution once either the delay has expired or the
1691 * socket operation is ready.
1692 * Only allowed to be called as long as the scheduler is running, that
1693 * is one of the following conditions is met:
1695 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1696 * - #GNUNET_SCHEDULER_driver_init has been run and
1697 * #GNUNET_SCHEDULER_driver_done has not been called yet
1699 * @param delay when should this operation time out?
1700 * @param priority priority of the task
1701 * @param fd file-descriptor
1702 * @param on_read whether to poll the file-descriptor for readability
1703 * @param on_write whether to poll the file-descriptor for writability
1704 * @param task main function of the task
1705 * @param task_cls closure of @a task
1706 * @return unique task identifier for the job
1707 * only valid until @a task is started!
1709 struct GNUNET_SCHEDULER_Task *
1710 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1711 enum GNUNET_SCHEDULER_Priority priority,
1712 const struct GNUNET_DISK_FileHandle *fd,
1713 int on_read, int on_write,
1714 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1716 /* scheduler must be running */
1717 GNUNET_assert (NULL != scheduler_driver);
1720 struct GNUNET_NETWORK_FDSet *s;
1721 struct GNUNET_SCHEDULER_Task * ret;
1723 GNUNET_assert (NULL != fd);
1724 s = GNUNET_NETWORK_fdset_create ();
1725 GNUNET_NETWORK_fdset_handle_set (s, fd);
1726 ret = GNUNET_SCHEDULER_add_select (
1729 on_write ? s : NULL,
1731 GNUNET_NETWORK_fdset_destroy (s);
1734 GNUNET_assert (on_read || on_write);
1735 GNUNET_assert (fd->fd >= 0);
1736 return add_without_sets (delay, priority,
1739 on_read ? fd : NULL,
1740 on_write ? fd : NULL,
1747 extract_handles (const struct GNUNET_NETWORK_FDSet *fdset,
1748 const struct GNUNET_NETWORK_Handle ***ntarget,
1749 unsigned int *extracted_nhandles,
1750 const struct GNUNET_DISK_FileHandle ***ftarget,
1751 unsigned int *extracted_fhandles)
1753 // FIXME: this implementation only works for unix, for WIN32 the file handles
1754 // in fdset must be handled separately
1755 const struct GNUNET_NETWORK_Handle **nhandles;
1756 const struct GNUNET_DISK_FileHandle **fhandles;
1757 unsigned int nhandles_len;
1758 unsigned int fhandles_len;
1764 for (int sock = 0; sock != fdset->nsds; ++sock)
1766 if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
1768 struct GNUNET_NETWORK_Handle *nhandle;
1769 struct GNUNET_DISK_FileHandle *fhandle;
1771 nhandle = GNUNET_NETWORK_socket_box_native (sock);
1772 if (NULL != nhandle)
1774 GNUNET_array_append (nhandles, nhandles_len, nhandle);
1778 fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
1779 if (NULL != fhandle)
1781 GNUNET_array_append (fhandles, fhandles_len, fhandle);
1790 *ntarget = nhandles_len > 0 ? nhandles : NULL;
1791 *ftarget = fhandles_len > 0 ? fhandles : NULL;
1792 *extracted_nhandles = nhandles_len;
1793 *extracted_fhandles = fhandles_len;
1798 * Schedule a new task to be run with a specified delay or when any of
1799 * the specified file descriptor sets is ready. The delay can be used
1800 * as a timeout on the socket(s) being ready. The task will be
1801 * scheduled for execution once either the delay has expired or any of
1802 * the socket operations is ready. This is the most general
1803 * function of the "add" family. Note that the "prerequisite_task"
1804 * must be satisfied in addition to any of the other conditions. In
1805 * other words, the task will be started when
1807 * (prerequisite-run)
1810 * || any-ws-ready) )
1812 * Only allowed to be called as long as the scheduler is running, that
1813 * is one of the following conditions is met:
1815 * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1816 * - #GNUNET_SCHEDULER_driver_init has been run and
1817 * #GNUNET_SCHEDULER_driver_done has not been called yet
1819 * @param prio how important is this task?
1820 * @param delay how long should we wait?
1821 * @param rs set of file descriptors we want to read (can be NULL)
1822 * @param ws set of file descriptors we want to write (can be NULL)
1823 * @param task main function of the task
1824 * @param task_cls closure of @a task
1825 * @return unique task identifier for the job
1826 * only valid until @a task is started!
1828 struct GNUNET_SCHEDULER_Task *
1829 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1830 struct GNUNET_TIME_Relative delay,
1831 const struct GNUNET_NETWORK_FDSet *rs,
1832 const struct GNUNET_NETWORK_FDSet *ws,
1833 GNUNET_SCHEDULER_TaskCallback task,
1836 struct GNUNET_SCHEDULER_Task *t;
1837 const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
1838 const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
1839 const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
1840 const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
1841 unsigned int read_nhandles_len = 0;
1842 unsigned int write_nhandles_len = 0;
1843 unsigned int read_fhandles_len = 0;
1844 unsigned int write_fhandles_len = 0;
1846 /* scheduler must be running */
1847 GNUNET_assert (NULL != scheduler_driver);
1848 GNUNET_assert (NULL != task);
1849 int no_rs = (NULL == rs);
1850 int no_ws = (NULL == ws);
1851 int empty_rs = (NULL != rs) && (0 == rs->nsds);
1852 int empty_ws = (NULL != ws) && (0 == ws->nsds);
1853 int no_fds = (no_rs && no_ws) ||
1854 (empty_rs && empty_ws) ||
1855 (no_rs && empty_ws) ||
1856 (no_ws && empty_rs);
1861 extract_handles (rs,
1865 &read_fhandles_len);
1869 extract_handles (ws,
1871 &write_nhandles_len,
1873 &write_fhandles_len);
1877 * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
1878 * although its maximum FD number (nsds) is greater than 0. We handle
1879 * this case gracefully because some libraries such as libmicrohttpd
1880 * only provide a hint what the maximum FD number in an FD set might be
1881 * and not the exact FD number (see e.g. gnunet-rest-service.c)
1883 int no_fds_extracted = (0 == read_nhandles_len) &&
1884 (0 == read_fhandles_len) &&
1885 (0 == write_nhandles_len) &&
1886 (0 == write_fhandles_len);
1887 if (no_fds || no_fds_extracted)
1888 return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1892 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1893 GNUNET_async_scope_get (&t->scope);
1902 write_fhandles_len);
1904 t->callback_cls = task_cls;
1905 t->own_handles = GNUNET_YES;
1906 /* free the arrays of pointers to network / file handles, the actual
1907 * handles will be freed in destroy_task */
1908 GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
1909 GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
1910 GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
1911 GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
1913 t->start_time = GNUNET_TIME_absolute_get ();
1915 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1917 check_priority ((prio ==
1918 GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1920 t->lifeness = current_lifeness;
1921 GNUNET_CONTAINER_DLL_insert (pending_head,
1924 driver_add_multiple (t);
1925 max_priority_added = GNUNET_MAX (max_priority_added,
1927 LOG (GNUNET_ERROR_TYPE_DEBUG,
1936 * Function used by event-loop implementations to signal the scheduler
1937 * that a particular @a task is ready due to an event specified in the
1938 * et field of @a fdi.
1940 * This function will then queue the task to notify the application
1941 * that the task is ready (with the respective priority).
1943 * @param task the task that is ready
1944 * @param fdi information about the related FD
1947 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1948 struct GNUNET_SCHEDULER_FdInfo *fdi)
1950 enum GNUNET_SCHEDULER_Reason reason;
1952 reason = task->reason;
1953 if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1954 (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)) )
1955 reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1956 if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1957 (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)) )
1958 reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1959 reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1960 task->reason = reason;
1961 if (GNUNET_NO == task->in_ready_list)
1963 GNUNET_CONTAINER_DLL_remove (pending_head,
1966 queue_ready_task (task);
1972 * Function called by external event loop implementations to tell the
1973 * scheduler to run some of the tasks that are ready. Must be called
1974 * only after #GNUNET_SCHEDULER_driver_init has been called and before
1975 * #GNUNET_SCHEDULER_driver_done is called.
1976 * This function may return even though there are tasks left to run
1977 * just to give other tasks a chance as well. If we return #GNUNET_YES,
1978 * the event loop implementation should call this function again as
1979 * soon as possible, while if we return #GNUNET_NO it must block until
1980 * either the operating system has more work (the scheduler has no more
1981 * work to do right now) or the timeout set by the scheduler (using the
1982 * set_wakeup callback) is reached.
1984 * @param sh scheduler handle that was returned by
1985 * #GNUNET_SCHEDULER_driver_init
1986 * @return #GNUNET_YES if there are more tasks that are ready,
1987 * and thus we would like to run more (yield to avoid
1988 * blocking other activities for too long) #GNUNET_NO
1989 * if we are done running tasks (yield to block)
1992 GNUNET_SCHEDULER_do_work (struct GNUNET_SCHEDULER_Handle *sh)
1994 enum GNUNET_SCHEDULER_Priority p;
1995 struct GNUNET_SCHEDULER_Task *pos;
1996 struct GNUNET_TIME_Absolute now;
1998 /* check for tasks that reached the timeout! */
1999 now = GNUNET_TIME_absolute_get ();
2000 pos = pending_timeout_head;
2003 struct GNUNET_SCHEDULER_Task *next = pos->next;
2004 if (now.abs_value_us >= pos->timeout.abs_value_us)
2005 pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2006 if (0 == pos->reason)
2008 GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
2009 pending_timeout_tail,
2011 if (pending_timeout_last == pos)
2012 pending_timeout_last = NULL;
2013 queue_ready_task (pos);
2019 struct GNUNET_SCHEDULER_Task *next = pos->next;
2020 if (now.abs_value_us >= pos->timeout.abs_value_us)
2022 pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2023 GNUNET_CONTAINER_DLL_remove (pending_head,
2026 queue_ready_task (pos);
2031 if (0 == ready_count)
2033 struct GNUNET_TIME_Absolute timeout = get_timeout ();
2035 if (timeout.abs_value_us > now.abs_value_us)
2038 * The event loop called this function before the current timeout was
2039 * reached (and no FD tasks are ready). This is acceptable if
2041 * - the system time was changed while the driver was waiting for
2043 * - an external event loop called GNUnet API functions outside of
2044 * the callbacks called in GNUNET_SCHEDULER_do_work and thus
2045 * wasn't notified about the new timeout
2047 * It might also mean we are busy-waiting because of a programming
2048 * error in the external event loop.
2050 LOG (GNUNET_ERROR_TYPE_DEBUG,
2051 "GNUNET_SCHEDULER_do_work did not find any ready "
2052 "tasks and timeout has not been reached yet.\n");
2057 * the current timeout was reached but no ready tasks were found,
2058 * internal scheduler error!
2065 /* find out which task priority level we are going to
2066 process this time */
2067 max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
2068 GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
2069 /* yes, p>0 is correct, 0 is "KEEP" which should
2070 * always be an empty queue (see assertion)! */
2071 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
2073 pos = ready_head[p];
2077 GNUNET_assert (NULL != pos); /* ready_count wrong? */
2079 /* process all tasks at this priority level, then yield */
2080 while (NULL != (pos = ready_head[p]))
2082 GNUNET_CONTAINER_DLL_remove (ready_head[p],
2086 current_priority = pos->priority;
2087 current_lifeness = pos->lifeness;
2090 if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
2091 DELAY_THRESHOLD.rel_value_us)
2093 LOG (GNUNET_ERROR_TYPE_DEBUG,
2094 "Task %p took %s to be scheduled\n",
2096 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
2100 tc.reason = pos->reason;
2101 GNUNET_NETWORK_fdset_zero (sh->rs);
2102 GNUNET_NETWORK_fdset_zero (sh->ws);
2103 // FIXME: do we have to remove FdInfos from fds if they are not ready?
2104 tc.fds_len = pos->fds_len;
2106 for (unsigned int i = 0; i != pos->fds_len; ++i)
2108 struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
2109 if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
2111 GNUNET_NETWORK_fdset_set_native (sh->rs,
2114 if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
2116 GNUNET_NETWORK_fdset_set_native (sh->ws,
2120 tc.read_ready = sh->rs;
2121 tc.write_ready = sh->ws;
2122 LOG (GNUNET_ERROR_TYPE_DEBUG,
2123 "Running task %p\n",
2125 GNUNET_assert (NULL != pos->callback);
2127 struct GNUNET_AsyncScopeSave old_scope;
2128 if (pos->scope.have_scope)
2129 GNUNET_async_scope_enter (&pos->scope.scope_id, &old_scope);
2131 GNUNET_async_scope_get (&old_scope);
2132 pos->callback (pos->callback_cls);
2133 GNUNET_async_scope_restore (&old_scope);
2135 if (NULL != pos->fds)
2137 int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
2138 if (GNUNET_OK != del_result)
2140 LOG (GNUNET_ERROR_TYPE_ERROR,
2141 "driver could not delete task %p\n", pos);
2146 dump_backtrace (pos);
2150 shutdown_if_no_lifeness ();
2151 if (0 == ready_count)
2153 scheduler_driver->set_wakeup (scheduler_driver->cls,
2157 scheduler_driver->set_wakeup (scheduler_driver->cls,
2158 GNUNET_TIME_absolute_get ());
2164 * Function called by external event loop implementations to initialize
2165 * the scheduler. An external implementation has to provide @a driver
2166 * which contains callbacks for the scheduler (see definition of struct
2167 * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
2168 * external implementation to watch for events. If it detects any of
2169 * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
2170 * the scheduler handle it. If an event is related to a specific task
2171 * (e.g. the scheduler gave instructions to watch a file descriptor),
2172 * the external implementation is expected to mark that task ready
2173 * before by calling #GNUNET_SCHEDULER_task_ready.
2175 * This function has to be called before any tasks are scheduled and
2176 * before GNUNET_SCHEDULER_do_work is called for the first time. It
2177 * allocates resources that have to be freed again by calling
2178 * #GNUNET_SCHEDULER_driver_done.
2180 * This function installs the same signal handlers as
2181 * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
2182 * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
2183 * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
2184 * active tasks to be scheduled with reason
2185 * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
2186 * will execute normally!). Note that any particular signal will only
2187 * shut down one scheduler; applications should always only create a
2190 * @param driver to use for the event loop
2191 * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
2192 * #GNUNET_SCHEDULER_driver_done
2194 struct GNUNET_SCHEDULER_Handle *
2195 GNUNET_SCHEDULER_driver_init (const struct GNUNET_SCHEDULER_Driver *driver)
2197 struct GNUNET_SCHEDULER_Handle *sh;
2198 const struct GNUNET_DISK_FileHandle *pr;
2200 /* scheduler must not be running */
2201 GNUNET_assert (NULL == scheduler_driver);
2202 GNUNET_assert (NULL == shutdown_pipe_handle);
2203 /* general set-up */
2204 sh = GNUNET_new (struct GNUNET_SCHEDULER_Handle);
2205 shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
2209 GNUNET_assert (NULL != shutdown_pipe_handle);
2210 pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2211 GNUNET_DISK_PIPE_END_READ);
2213 scheduler_driver = driver;
2215 /* install signal handlers */
2216 LOG (GNUNET_ERROR_TYPE_DEBUG,
2217 "Registering signal handlers\n");
2218 sh->shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2219 &sighandler_shutdown);
2220 sh->shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2221 &sighandler_shutdown);
2222 #if (SIGTERM != GNUNET_TERM_SIG)
2223 sh->shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2224 &sighandler_shutdown);
2227 sh->shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2229 sh->shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2230 &sighandler_shutdown);
2231 sh->shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2232 &sighandler_shutdown);
2235 /* Setup initial tasks */
2236 current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2237 current_lifeness = GNUNET_NO;
2238 install_parent_control_task =
2239 GNUNET_SCHEDULER_add_now (&install_parent_control_handler,
2241 shutdown_pipe_task =
2242 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2246 current_lifeness = GNUNET_YES;
2247 scheduler_driver->set_wakeup (scheduler_driver->cls,
2249 /* begin main event loop */
2250 sh->rs = GNUNET_NETWORK_fdset_create ();
2251 sh->ws = GNUNET_NETWORK_fdset_create ();
2252 GNUNET_NETWORK_fdset_handle_set (sh->rs, pr);
2258 * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
2259 * by external event loop implementations after the scheduler has
2260 * shut down. This is the case if both of the following conditions
2263 * - all tasks the scheduler has added through the driver's add
2264 * callback have been removed again through the driver's del
2266 * - the timeout the scheduler has set through the driver's
2267 * add_wakeup callback is FOREVER
2269 * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
2272 GNUNET_SCHEDULER_driver_done (struct GNUNET_SCHEDULER_Handle *sh)
2274 GNUNET_assert (NULL == pending_head);
2275 GNUNET_assert (NULL == pending_timeout_head);
2276 GNUNET_assert (NULL == shutdown_head);
2277 for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
2279 GNUNET_assert (NULL == ready_head[i]);
2281 GNUNET_NETWORK_fdset_destroy (sh->rs);
2282 GNUNET_NETWORK_fdset_destroy (sh->ws);
2284 /* uninstall signal handlers */
2285 GNUNET_SIGNAL_handler_uninstall (sh->shc_int);
2286 GNUNET_SIGNAL_handler_uninstall (sh->shc_term);
2287 #if (SIGTERM != GNUNET_TERM_SIG)
2288 GNUNET_SIGNAL_handler_uninstall (sh->shc_gterm);
2291 GNUNET_SIGNAL_handler_uninstall (sh->shc_pipe);
2292 GNUNET_SIGNAL_handler_uninstall (sh->shc_quit);
2293 GNUNET_SIGNAL_handler_uninstall (sh->shc_hup);
2295 GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2296 shutdown_pipe_handle = NULL;
2297 scheduler_driver = NULL;
2303 select_loop (struct GNUNET_SCHEDULER_Handle *sh,
2304 struct DriverContext *context)
2306 struct GNUNET_NETWORK_FDSet *rs;
2307 struct GNUNET_NETWORK_FDSet *ws;
2310 GNUNET_assert (NULL != context);
2311 rs = GNUNET_NETWORK_fdset_create ();
2312 ws = GNUNET_NETWORK_fdset_create ();
2313 while ( (NULL != context->scheduled_head) ||
2314 (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != context->timeout.abs_value_us) )
2316 LOG (GNUNET_ERROR_TYPE_DEBUG,
2317 "select timeout = %s\n",
2318 GNUNET_STRINGS_absolute_time_to_string (context->timeout));
2320 GNUNET_NETWORK_fdset_zero (rs);
2321 GNUNET_NETWORK_fdset_zero (ws);
2323 for (struct Scheduled *pos = context->scheduled_head;
2327 if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2329 GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2331 if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2333 GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2336 struct GNUNET_TIME_Relative time_remaining =
2337 GNUNET_TIME_absolute_get_remaining (context->timeout);
2338 if (NULL == scheduler_select)
2340 select_result = GNUNET_NETWORK_socket_select (rs,
2347 select_result = scheduler_select (scheduler_select_cls,
2353 if (select_result == GNUNET_SYSERR)
2358 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2370 if (0 != system (lsof))
2371 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2376 for (struct Scheduled *s = context->scheduled_head;
2380 int flags = fcntl (s->fdi->sock,
2383 if ( (flags == -1) &&
2386 LOG (GNUNET_ERROR_TYPE_ERROR,
2387 "Got invalid file descriptor %d!\n",
2390 dump_backtrace (s->task);
2396 GNUNET_NETWORK_fdset_destroy (rs);
2397 GNUNET_NETWORK_fdset_destroy (ws);
2398 return GNUNET_SYSERR;
2400 if (select_result > 0)
2402 for (struct Scheduled *pos = context->scheduled_head;
2406 int is_ready = GNUNET_NO;
2408 if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2410 GNUNET_NETWORK_fdset_test_native (rs,
2413 pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2414 is_ready = GNUNET_YES;
2416 if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2418 GNUNET_NETWORK_fdset_test_native (ws,
2421 pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2422 is_ready = GNUNET_YES;
2424 if (GNUNET_YES == is_ready)
2426 GNUNET_SCHEDULER_task_ready (pos->task,
2431 if (GNUNET_YES == GNUNET_SCHEDULER_do_work (sh))
2433 LOG (GNUNET_ERROR_TYPE_DEBUG,
2434 "scheduler has more tasks ready!\n");
2437 GNUNET_NETWORK_fdset_destroy (rs);
2438 GNUNET_NETWORK_fdset_destroy (ws);
2444 select_add (void *cls,
2445 struct GNUNET_SCHEDULER_Task *task,
2446 struct GNUNET_SCHEDULER_FdInfo *fdi)
2448 struct DriverContext *context = cls;
2449 GNUNET_assert (NULL != context);
2450 GNUNET_assert (NULL != task);
2451 GNUNET_assert (NULL != fdi);
2452 GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2453 0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2455 if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2457 /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2458 return GNUNET_SYSERR;
2461 struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2462 scheduled->task = task;
2463 scheduled->fdi = fdi;
2464 scheduled->et = fdi->et;
2466 GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2467 context->scheduled_tail,
2474 select_del (void *cls,
2475 struct GNUNET_SCHEDULER_Task *task)
2477 struct DriverContext *context;
2478 struct Scheduled *pos;
2481 GNUNET_assert (NULL != cls);
2484 ret = GNUNET_SYSERR;
2485 pos = context->scheduled_head;
2488 struct Scheduled *next = pos->next;
2489 if (pos->task == task)
2491 GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2492 context->scheduled_tail,
2504 select_set_wakeup (void *cls,
2505 struct GNUNET_TIME_Absolute dt)
2507 struct DriverContext *context = cls;
2509 GNUNET_assert (NULL != context);
2510 context->timeout = dt;
2515 * Obtain the driver for using select() as the event loop.
2517 * @return NULL on error
2519 struct GNUNET_SCHEDULER_Driver *
2520 GNUNET_SCHEDULER_driver_select ()
2522 struct GNUNET_SCHEDULER_Driver *select_driver;
2523 select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2525 select_driver->add = &select_add;
2526 select_driver->del = &select_del;
2527 select_driver->set_wakeup = &select_set_wakeup;
2529 return select_driver;
2534 * Change the async scope for the currently executing task and (transitively)
2535 * for all tasks scheduled by the current task after calling this function.
2536 * Nested tasks can begin their own nested async scope.
2538 * Once the current task is finished, the async scope ID is reset to
2539 * its previous value.
2541 * Must only be called from a running task.
2543 * @param aid the asynchronous scope id to enter
2546 GNUNET_SCHEDULER_begin_async_scope (struct GNUNET_AsyncScopeId *aid)
2548 struct GNUNET_AsyncScopeSave dummy_old_scope;
2550 GNUNET_assert (NULL != active_task);
2551 /* Since we're in a task, the context will be automatically
2552 restored by the scheduler. */
2553 GNUNET_async_scope_enter (aid, &dummy_old_scope);
2557 /* end of scheduler.c */