2 This file is part of GNUnet
3 Copyright (C) 2009-2013 GNUnet e.V.
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 3, 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file util/scheduler.c
23 * @brief schedule computations using continuation passing style
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
30 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
32 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
39 * Use lsof to generate file descriptor reports on select error?
40 * (turn off for stable releases).
42 #define USE_LSOF GNUNET_NO
45 * Obtain trace information for all scheduler calls that schedule tasks.
47 #define EXECINFO GNUNET_NO
50 * Check each file descriptor before adding
52 #define DEBUG_FDS GNUNET_NO
55 * Depth of the traces collected via EXECINFO.
57 #define MAX_TRACE_DEPTH 50
61 * Should we figure out which tasks are delayed for a while
62 * before they are run? (Consider using in combination with EXECINFO).
64 #define PROFILE_DELAYS GNUNET_NO
67 * Task that were in the queue for longer than this are reported if
68 * PROFILE_DELAYS is active.
70 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
74 * Entry in list of pending tasks.
76 struct GNUNET_SCHEDULER_Task
79 * This is a linked list.
81 struct GNUNET_SCHEDULER_Task *next;
84 * This is a linked list.
86 struct GNUNET_SCHEDULER_Task *prev;
89 * Function to run when ready.
91 GNUNET_SCHEDULER_TaskCallback callback;
94 * Closure for the @e callback.
99 * Set of file descriptors this task is waiting
100 * for for reading. Once ready, this is updated
101 * to reflect the set of file descriptors ready
104 struct GNUNET_NETWORK_FDSet *read_set;
107 * Set of file descriptors this task is waiting for for writing.
108 * Once ready, this is updated to reflect the set of file
109 * descriptors ready for operation.
111 struct GNUNET_NETWORK_FDSet *write_set;
114 * Absolute timeout value for the task, or
115 * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
117 struct GNUNET_TIME_Absolute timeout;
121 * When was the task scheduled?
123 struct GNUNET_TIME_Absolute start_time;
127 * Why is the task ready? Set after task is added to ready queue.
128 * Initially set to zero. All reasons that have already been
129 * satisfied (i.e. read or write ready) will be set over time.
131 enum GNUNET_SCHEDULER_Reason reason;
136 enum GNUNET_SCHEDULER_Priority priority;
139 * Set if we only wait for reading from a single FD, otherwise -1.
144 * Set if we only wait for writing to a single FD, otherwise -1.
149 * Should the existence of this task in the queue be counted as
150 * reason to not shutdown the scheduler?
155 * Is this task in the ready list?
161 * Array of strings which make up a backtrace from the point when this
162 * task was scheduled (essentially, who scheduled the task?)
164 char **backtrace_strings;
167 * Size of the backtrace_strings array
169 int num_backtrace_strings;
177 * Head of list of tasks waiting for an event.
179 static struct GNUNET_SCHEDULER_Task *pending_head;
182 * Tail of list of tasks waiting for an event.
184 static struct GNUNET_SCHEDULER_Task *pending_tail;
187 * List of tasks waiting ONLY for a timeout event.
188 * Sorted by timeout (earliest first). Used so that
189 * we do not traverse the list of these tasks when
190 * building select sets (we just look at the head
191 * to determine the respective timeout ONCE).
193 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
196 * List of tasks waiting ONLY for a timeout event.
197 * Sorted by timeout (earliest first). Used so that
198 * we do not traverse the list of these tasks when
199 * building select sets (we just look at the head
200 * to determine the respective timeout ONCE).
202 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
205 * Last inserted task waiting ONLY for a timeout event.
206 * Used to (heuristically) speed up insertion.
208 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
211 * ID of the task that is running right now.
213 static struct GNUNET_SCHEDULER_Task *active_task;
216 * Head of list of tasks ready to run right now, grouped by importance.
218 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
221 * Tail of list of tasks ready to run right now, grouped by importance.
223 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
226 * Number of tasks on the ready list.
228 static unsigned int ready_count;
231 * How many tasks have we run so far?
233 static unsigned long long tasks_run;
236 * Priority of the task running right now. Only
237 * valid while a task is running.
239 static enum GNUNET_SCHEDULER_Priority current_priority;
242 * Priority of the highest task added in the current select
245 static enum GNUNET_SCHEDULER_Priority max_priority_added;
248 * Value of the 'lifeness' flag for the current task.
250 static int current_lifeness;
253 * Function to use as a select() in the scheduler.
254 * If NULL, we use GNUNET_NETWORK_socket_select().
256 static GNUNET_SCHEDULER_select scheduler_select;
259 * Task context of the current task.
261 static struct GNUNET_SCHEDULER_TaskContext tc;
264 * Closure for #scheduler_select.
266 static void *scheduler_select_cls;
270 * Sets the select function to use in the scheduler (scheduler_select).
272 * @param new_select new select function to use
273 * @param new_select_cls closure for @a new_select
274 * @return previously used select function, NULL for default
277 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
278 void *new_select_cls)
280 scheduler_select = new_select;
281 scheduler_select_cls = new_select_cls;
286 * Check that the given priority is legal (and return it).
288 * @param p priority value to check
289 * @return p on success, 0 on error
291 static enum GNUNET_SCHEDULER_Priority
292 check_priority (enum GNUNET_SCHEDULER_Priority p)
294 if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
297 return 0; /* make compiler happy */
302 * Update all sets and timeout for select.
304 * @param rs read-set, set to all FDs we would like to read (updated)
305 * @param ws write-set, set to all FDs we would like to write (updated)
306 * @param timeout next timeout (updated)
309 update_sets (struct GNUNET_NETWORK_FDSet *rs,
310 struct GNUNET_NETWORK_FDSet *ws,
311 struct GNUNET_TIME_Relative *timeout)
313 struct GNUNET_SCHEDULER_Task *pos;
314 struct GNUNET_TIME_Absolute now;
315 struct GNUNET_TIME_Relative to;
317 now = GNUNET_TIME_absolute_get ();
318 pos = pending_timeout_head;
321 to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
322 if (timeout->rel_value_us > to.rel_value_us)
324 if (0 != pos->reason)
325 *timeout = GNUNET_TIME_UNIT_ZERO;
327 for (pos = pending_head; NULL != pos; pos = pos->next)
329 if (pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
331 to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
332 if (timeout->rel_value_us > to.rel_value_us)
335 if (-1 != pos->read_fd)
336 GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
337 if (-1 != pos->write_fd)
338 GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
339 if (NULL != pos->read_set)
340 GNUNET_NETWORK_fdset_add (rs, pos->read_set);
341 if (NULL != pos->write_set)
342 GNUNET_NETWORK_fdset_add (ws, pos->write_set);
343 if (0 != pos->reason)
344 *timeout = GNUNET_TIME_UNIT_ZERO;
350 * Check if the ready set overlaps with the set we want to have ready.
351 * If so, update the want set (set all FDs that are ready). If not,
354 * @param ready set that is ready
355 * @param want set that we want to be ready
356 * @return #GNUNET_YES if there was some overlap
359 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
360 struct GNUNET_NETWORK_FDSet *want)
362 if ((NULL == want) || (NULL == ready))
364 if (GNUNET_NETWORK_fdset_overlap (ready, want))
366 /* copy all over (yes, there maybe unrelated bits,
367 * but this should not hurt well-written clients) */
368 GNUNET_NETWORK_fdset_copy (want, ready);
376 * Check if the given task is eligible to run now.
377 * Also set the reason why it is eligible.
379 * @param task task to check if it is ready
380 * @param now the current time
381 * @param rs set of FDs ready for reading
382 * @param ws set of FDs ready for writing
383 * @return #GNUNET_YES if we can run it, #GNUNET_NO if not.
386 is_ready (struct GNUNET_SCHEDULER_Task *task,
387 struct GNUNET_TIME_Absolute now,
388 const struct GNUNET_NETWORK_FDSet *rs,
389 const struct GNUNET_NETWORK_FDSet *ws)
391 enum GNUNET_SCHEDULER_Reason reason;
393 reason = task->reason;
394 if (now.abs_value_us >= task->timeout.abs_value_us)
395 reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
396 if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
397 (((task->read_fd != -1) &&
398 (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd))) ||
399 (set_overlaps (rs, task->read_set))))
400 reason |= GNUNET_SCHEDULER_REASON_READ_READY;
401 if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
402 (((task->write_fd != -1) &&
403 (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)))
404 || (set_overlaps (ws, task->write_set))))
405 reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
407 return GNUNET_NO; /* not ready */
408 reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
409 task->reason = reason;
415 * Put a task that is ready for execution into the ready queue.
417 * @param task task ready for execution
420 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
422 enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
424 if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
425 p = task->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
426 GNUNET_CONTAINER_DLL_insert (ready_head[p],
429 task->in_ready_list = GNUNET_YES;
435 * Check which tasks are ready and move them
436 * to the respective ready queue.
438 * @param rs FDs ready for reading
439 * @param ws FDs ready for writing
442 check_ready (const struct GNUNET_NETWORK_FDSet *rs,
443 const struct GNUNET_NETWORK_FDSet *ws)
445 struct GNUNET_SCHEDULER_Task *pos;
446 struct GNUNET_SCHEDULER_Task *next;
447 struct GNUNET_TIME_Absolute now;
449 now = GNUNET_TIME_absolute_get ();
450 while (NULL != (pos = pending_timeout_head))
452 if (now.abs_value_us >= pos->timeout.abs_value_us)
453 pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
454 if (0 == pos->reason)
456 GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
457 pending_timeout_tail,
459 if (pending_timeout_last == pos)
460 pending_timeout_last = NULL;
461 queue_ready_task (pos);
467 if (GNUNET_YES == is_ready (pos, now, rs, ws))
469 GNUNET_CONTAINER_DLL_remove (pending_head,
472 queue_ready_task (pos);
480 * Request the shutdown of a scheduler. Marks all currently
481 * pending tasks as ready because of shutdown. This will
482 * cause all tasks to run (as soon as possible, respecting
483 * priorities and prerequisite tasks). Note that tasks
484 * scheduled AFTER this call may still be delayed arbitrarily.
486 * Note that we don't move the tasks into the ready queue yet;
487 * check_ready() will do that later, possibly adding additional
491 GNUNET_SCHEDULER_shutdown ()
493 struct GNUNET_SCHEDULER_Task *pos;
496 for (pos = pending_timeout_head; NULL != pos; pos = pos->next)
497 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
498 for (pos = pending_head; NULL != pos; pos = pos->next)
499 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
500 for (i = 0; i < GNUNET_SCHEDULER_PRIORITY_COUNT; i++)
501 for (pos = ready_head[i]; NULL != pos; pos = pos->next)
502 pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
507 * Destroy a task (release associated resources)
509 * @param t task to destroy
512 destroy_task (struct GNUNET_SCHEDULER_Task *t)
514 if (NULL != t->read_set)
515 GNUNET_NETWORK_fdset_destroy (t->read_set);
516 if (NULL != t->write_set)
517 GNUNET_NETWORK_fdset_destroy (t->write_set);
519 GNUNET_free (t->backtrace_strings);
526 * Run at least one task in the highest-priority queue that is not
527 * empty. Keep running tasks until we are either no longer running
528 * "URGENT" tasks or until we have at least one "pending" task (which
529 * may become ready, hence we should select on it). Naturally, if
530 * there are no more ready tasks, we also return.
532 * @param rs FDs ready for reading
533 * @param ws FDs ready for writing
536 run_ready (struct GNUNET_NETWORK_FDSet *rs,
537 struct GNUNET_NETWORK_FDSet *ws)
539 enum GNUNET_SCHEDULER_Priority p;
540 struct GNUNET_SCHEDULER_Task *pos;
542 max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
545 if (0 == ready_count)
547 GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
548 /* yes, p>0 is correct, 0 is "KEEP" which should
549 * always be an empty queue (see assertion)! */
550 for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
556 GNUNET_assert (NULL != pos); /* ready_count wrong? */
557 GNUNET_CONTAINER_DLL_remove (ready_head[p],
561 current_priority = pos->priority;
562 current_lifeness = pos->lifeness;
565 if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
566 DELAY_THRESHOLD.rel_value_us)
568 LOG (GNUNET_ERROR_TYPE_DEBUG,
569 "Task %p took %s to be scheduled\n",
571 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
575 tc.reason = pos->reason;
576 tc.read_ready = (NULL == pos->read_set) ? rs : pos->read_set;
577 if ((-1 != pos->read_fd) &&
578 (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)))
579 GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
580 tc.write_ready = (NULL == pos->write_set) ? ws : pos->write_set;
581 if ((-1 != pos->write_fd) &&
582 (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
583 GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
584 if ((0 != (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
585 (-1 != pos->write_fd) &&
586 (!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
587 GNUNET_assert (0); // added to ready in previous select loop!
588 LOG (GNUNET_ERROR_TYPE_DEBUG,
589 "Running task: %p\n",
591 pos->callback (pos->callback_cls);
595 for (i = 0; i < pos->num_backtrace_strings; i++)
596 LOG (GNUNET_ERROR_TYPE_DEBUG,
597 "Task %p trace %u: %s\n",
600 pos->backtrace_strings[i]);
606 while ((NULL == pending_head) || (p >= max_priority_added));
611 * Pipe used to communicate shutdown via signal.
613 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
616 * Process ID of this process at the time we installed the various
622 * Signal handler called for SIGPIPE.
634 * Wait for a short time.
635 * Sleeps for @a ms ms (as that should be long enough for virtually all
636 * modern systems to context switch and allow another process to do
639 * @param ms how many ms to wait
642 short_wait (unsigned int ms)
644 struct GNUNET_TIME_Relative timeout;
646 timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
647 (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
652 * Signal handler called for signals that should cause us to shutdown.
655 sighandler_shutdown ()
658 int old_errno = errno; /* backup errno */
660 if (getpid () != my_pid)
661 exit (1); /* we have fork'ed since the signal handler was created,
662 * ignore the signal, see https://gnunet.org/vfork discussion */
663 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
664 (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
671 * Check if the system is still life. Trigger shutdown if we
672 * have tasks, but none of them give us lifeness.
674 * @return #GNUNET_OK to continue the main loop,
680 struct GNUNET_SCHEDULER_Task *t;
684 for (t = pending_head; NULL != t; t = t->next)
685 if (t->lifeness == GNUNET_YES)
687 for (t = pending_timeout_head; NULL != t; t = t->next)
688 if (t->lifeness == GNUNET_YES)
690 if ((NULL != pending_head) || (NULL != pending_timeout_head))
692 GNUNET_SCHEDULER_shutdown ();
700 * Initialize and run scheduler. This function will return when all
701 * tasks have completed. On systems with signals, receiving a SIGTERM
702 * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
703 * to be run after the active task is complete. As a result, SIGTERM
704 * causes all active tasks to be scheduled with reason
705 * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added
706 * afterwards will execute normally!). Note that any particular signal
707 * will only shut down one scheduler; applications should always only
708 * create a single scheduler.
710 * @param task task to run immediately
711 * @param task_cls closure of @a task
714 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
717 struct GNUNET_NETWORK_FDSet *rs;
718 struct GNUNET_NETWORK_FDSet *ws;
719 struct GNUNET_TIME_Relative timeout;
721 struct GNUNET_SIGNAL_Context *shc_int;
722 struct GNUNET_SIGNAL_Context *shc_term;
723 #if (SIGTERM != GNUNET_TERM_SIG)
724 struct GNUNET_SIGNAL_Context *shc_gterm;
728 struct GNUNET_SIGNAL_Context *shc_quit;
729 struct GNUNET_SIGNAL_Context *shc_hup;
730 struct GNUNET_SIGNAL_Context *shc_pipe;
732 unsigned long long last_tr;
733 unsigned int busy_wait_warning;
734 const struct GNUNET_DISK_FileHandle *pr;
737 GNUNET_assert (NULL == active_task);
738 rs = GNUNET_NETWORK_fdset_create ();
739 ws = GNUNET_NETWORK_fdset_create ();
740 GNUNET_assert (NULL == shutdown_pipe_handle);
741 shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
745 GNUNET_assert (NULL != shutdown_pipe_handle);
746 pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
747 GNUNET_DISK_PIPE_END_READ);
748 GNUNET_assert (NULL != pr);
750 LOG (GNUNET_ERROR_TYPE_DEBUG,
751 "Registering signal handlers\n");
752 shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
753 shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
754 #if (SIGTERM != GNUNET_TERM_SIG)
755 shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG, &sighandler_shutdown);
758 shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
759 shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
760 shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
762 current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
763 current_lifeness = GNUNET_YES;
764 GNUNET_SCHEDULER_add_with_reason_and_priority (task,
766 GNUNET_SCHEDULER_REASON_STARTUP,
767 GNUNET_SCHEDULER_PRIORITY_DEFAULT);
768 active_task = (void *) (long) -1; /* force passing of sanity check */
769 GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
770 &GNUNET_OS_install_parent_control_handler,
774 busy_wait_warning = 0;
775 while (GNUNET_OK == check_lifeness ())
777 GNUNET_NETWORK_fdset_zero (rs);
778 GNUNET_NETWORK_fdset_zero (ws);
779 timeout = GNUNET_TIME_UNIT_FOREVER_REL;
780 update_sets (rs, ws, &timeout);
781 GNUNET_NETWORK_fdset_handle_set (rs, pr);
784 /* no blocking, more work already ready! */
785 timeout = GNUNET_TIME_UNIT_ZERO;
787 if (NULL == scheduler_select)
788 ret = GNUNET_NETWORK_socket_select (rs,
793 ret = scheduler_select (scheduler_select_cls,
798 if (ret == GNUNET_SYSERR)
803 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
808 snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
811 if (0 != system (lsof))
812 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
817 struct GNUNET_SCHEDULER_Task *t;
819 for (t = pending_head; NULL != t; t = t->next)
821 if (-1 != t->read_fd)
823 int flags = fcntl (t->read_fd, F_GETFD);
824 if ((flags == -1) && (errno == EBADF))
826 LOG (GNUNET_ERROR_TYPE_ERROR,
827 "Got invalid file descriptor %d!\n",
832 for (i = 0; i < t->num_backtrace_strings; i++)
833 LOG (GNUNET_ERROR_TYPE_ERROR,
835 t->backtrace_strings[i]);
839 if (-1 != t->write_fd)
841 int flags = fcntl (t->write_fd, F_GETFD);
842 if ((flags == -1) && (errno == EBADF))
844 LOG (GNUNET_ERROR_TYPE_ERROR,
845 "Got invalid file descriptor %d!\n",
850 for (i = 0; i < t->num_backtrace_strings; i++)
851 LOG (GNUNET_ERROR_TYPE_DEBUG,
853 t->backtrace_strings[i]);
864 (0 == timeout.rel_value_us) &&
865 (busy_wait_warning > 16) )
867 LOG (GNUNET_ERROR_TYPE_WARNING,
868 "Looks like we're busy waiting...\n");
869 short_wait (100); /* mitigate */
871 check_ready (rs, ws);
873 if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
875 /* consume the signal */
876 GNUNET_DISK_file_read (pr, &c, sizeof (c));
877 /* mark all active tasks as ready due to shutdown */
878 GNUNET_SCHEDULER_shutdown ();
880 if (last_tr == tasks_run)
888 busy_wait_warning = 0;
891 GNUNET_SIGNAL_handler_uninstall (shc_int);
892 GNUNET_SIGNAL_handler_uninstall (shc_term);
893 #if (SIGTERM != GNUNET_TERM_SIG)
894 GNUNET_SIGNAL_handler_uninstall (shc_gterm);
897 GNUNET_SIGNAL_handler_uninstall (shc_pipe);
898 GNUNET_SIGNAL_handler_uninstall (shc_quit);
899 GNUNET_SIGNAL_handler_uninstall (shc_hup);
901 GNUNET_DISK_pipe_close (shutdown_pipe_handle);
902 shutdown_pipe_handle = NULL;
903 GNUNET_NETWORK_fdset_destroy (rs);
904 GNUNET_NETWORK_fdset_destroy (ws);
909 * Obtain the task context, giving the reason why the current task was
912 * @return current tasks' scheduler context
914 const struct GNUNET_SCHEDULER_TaskContext *
915 GNUNET_SCHEDULER_get_task_context ()
917 GNUNET_assert (NULL != active_task);
923 * Get information about the current load of this scheduler. Use this
924 * function to determine if an elective task should be added or simply
925 * dropped (if the decision should be made based on the number of
926 * tasks ready to run).
928 * @param p priority level to look at
929 * @return number of tasks pending right now
932 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
934 struct GNUNET_SCHEDULER_Task *pos;
937 GNUNET_assert (NULL != active_task);
938 if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
940 if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
941 p = current_priority;
943 for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
950 * Cancel the task with the specified identifier.
951 * The task must not yet have run.
953 * @param task id of the task to cancel
954 * @return original closure of the task
957 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
959 enum GNUNET_SCHEDULER_Priority p;
962 GNUNET_assert (NULL != active_task);
963 if (! task->in_ready_list)
965 if ( (-1 == task->read_fd) &&
966 (-1 == task->write_fd) &&
967 (NULL == task->read_set) &&
968 (NULL == task->write_set) )
970 GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
971 pending_timeout_tail,
973 if (task == pending_timeout_last)
974 pending_timeout_last = NULL;
978 GNUNET_CONTAINER_DLL_remove (pending_head,
985 p = check_priority (task->priority);
986 GNUNET_CONTAINER_DLL_remove (ready_head[p],
991 ret = task->callback_cls;
992 LOG (GNUNET_ERROR_TYPE_DEBUG,
993 "Canceling task %p\n",
1001 * Continue the current execution with the given function. This is
1002 * similar to the other "add" functions except that there is no delay
1003 * and the reason code can be specified.
1005 * @param task main function of the task
1006 * @param task_cls closure for @a task
1007 * @param reason reason for task invocation
1008 * @param priority priority to use for the task
1011 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1013 enum GNUNET_SCHEDULER_Reason reason,
1014 enum GNUNET_SCHEDULER_Priority priority)
1016 struct GNUNET_SCHEDULER_Task *t;
1019 void *backtrace_array[50];
1022 GNUNET_assert (NULL != task);
1023 GNUNET_assert ((NULL != active_task) ||
1024 (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1025 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1027 t->num_backtrace_strings = backtrace (backtrace_array, 50);
1028 t->backtrace_strings =
1029 backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1034 t->callback_cls = task_cls;
1036 t->start_time = GNUNET_TIME_absolute_get ();
1039 t->priority = priority;
1040 t->lifeness = current_lifeness;
1041 LOG (GNUNET_ERROR_TYPE_DEBUG,
1042 "Adding continuation task %p\n",
1044 queue_ready_task (t);
1049 * Schedule a new task to be run with a specified delay. The task
1050 * will be scheduled for execution once the delay has expired.
1052 * @param delay when should this operation time out? Use
1053 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1054 * @param priority priority to use for the task
1055 * @param task main function of the task
1056 * @param task_cls closure of @a task
1057 * @return unique task identifier for the job
1058 * only valid until @a task is started!
1060 struct GNUNET_SCHEDULER_Task *
1061 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1062 enum GNUNET_SCHEDULER_Priority priority,
1063 GNUNET_SCHEDULER_TaskCallback task,
1066 struct GNUNET_SCHEDULER_Task *t;
1067 struct GNUNET_SCHEDULER_Task *pos;
1068 struct GNUNET_SCHEDULER_Task *prev;
1071 void *backtrace_array[MAX_TRACE_DEPTH];
1074 GNUNET_assert (NULL != active_task);
1075 GNUNET_assert (NULL != task);
1076 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1078 t->callback_cls = task_cls;
1080 t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1081 t->backtrace_strings =
1082 backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1087 t->start_time = GNUNET_TIME_absolute_get ();
1089 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1090 t->priority = priority;
1091 t->lifeness = current_lifeness;
1092 /* try tail first (optimization in case we are
1093 * appending to a long list of tasks with timeouts) */
1094 if (0 == delay.rel_value_us)
1096 GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1097 pending_timeout_tail,
1102 /* first move from heuristic start backwards to before start time */
1103 prev = pending_timeout_last;
1104 while ( (NULL != prev) &&
1105 (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1107 /* now, move from heuristic start (or head of list) forward to insertion point */
1109 pos = pending_timeout_head;
1112 while ( (NULL != pos) &&
1113 ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1114 (0 != pos->reason) ) )
1119 GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1120 pending_timeout_tail,
1123 /* finally, update heuristic insertion point to last insertion... */
1124 pending_timeout_last = t;
1127 LOG (GNUNET_ERROR_TYPE_DEBUG,
1128 "Adding task: %p\n",
1133 for (i = 0; i < t->num_backtrace_strings; i++)
1134 LOG (GNUNET_ERROR_TYPE_DEBUG,
1135 "Task %p trace %d: %s\n",
1138 t->backtrace_strings[i]);
1145 * Schedule a new task to be run with a specified priority.
1147 * @param prio how important is the new task?
1148 * @param task main function of the task
1149 * @param task_cls closure of @a task
1150 * @return unique task identifier for the job
1151 * only valid until @a task is started!
1153 struct GNUNET_SCHEDULER_Task *
1154 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1155 GNUNET_SCHEDULER_TaskCallback task,
1158 return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1166 * Schedule a new task to be run with a specified delay. The task
1167 * will be scheduled for execution once the delay has expired. It
1168 * will be run with the DEFAULT priority.
1170 * @param delay when should this operation time out? Use
1171 * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1172 * @param task main function of the task
1173 * @param task_cls closure of task
1174 * @return unique task identifier for the job
1175 * only valid until "task" is started!
1177 struct GNUNET_SCHEDULER_Task *
1178 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1179 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1181 return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1182 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1188 * Schedule a new task to be run as soon as possible. Note that this
1189 * does not guarantee that this will be the next task that is being
1190 * run, as other tasks with higher priority (or that are already ready
1191 * to run) might get to run first. Just as with delays, clients must
1192 * not rely on any particular order of execution between tasks
1193 * scheduled concurrently.
1195 * The task will be run with the DEFAULT priority.
1197 * @param task main function of the task
1198 * @param task_cls closure of @a task
1199 * @return unique task identifier for the job
1200 * only valid until "task" is started!
1202 struct GNUNET_SCHEDULER_Task *
1203 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1205 return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO, task, task_cls);
1210 * Schedule a new task to be run as soon as possible with the
1211 * (transitive) ignore-shutdown flag either explicitly set or
1212 * explicitly enabled. This task (and all tasks created from it,
1213 * other than by another call to this function) will either count or
1214 * not count for the "lifeness" of the process. This API is only
1215 * useful in a few special cases.
1217 * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1218 * @param task main function of the task
1219 * @param task_cls closure of @a task
1220 * @return unique task identifier for the job
1221 * only valid until @a task is started!
1223 struct GNUNET_SCHEDULER_Task *
1224 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1225 GNUNET_SCHEDULER_TaskCallback task,
1228 struct GNUNET_SCHEDULER_Task *ret;
1230 ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1231 ret->lifeness = lifeness;
1237 * Schedule a new task to be run with a specified delay or when any of
1238 * the specified file descriptor sets is ready. The delay can be used
1239 * as a timeout on the socket(s) being ready. The task will be
1240 * scheduled for execution once either the delay has expired or any of
1241 * the socket operations is ready. This is the most general
1242 * function of the "add" family. Note that the "prerequisite_task"
1243 * must be satisfied in addition to any of the other conditions. In
1244 * other words, the task will be started when
1246 * (prerequisite-run)
1250 * || shutdown-active )
1253 * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1254 * which means that the task will only be run after we receive SIGTERM
1255 * @param priority priority to use
1256 * @param rfd file descriptor we want to read (can be -1)
1257 * @param wfd file descriptors we want to write (can be -1)
1258 * @param task main function of the task
1259 * @param task_cls closure of @a task
1260 * @return unique task identifier for the job
1261 * only valid until @a task is started!
1264 static struct GNUNET_SCHEDULER_Task *
1265 add_without_sets (struct GNUNET_TIME_Relative delay,
1266 enum GNUNET_SCHEDULER_Priority priority,
1269 GNUNET_SCHEDULER_TaskCallback task,
1272 struct GNUNET_SCHEDULER_Task *t;
1275 void *backtrace_array[MAX_TRACE_DEPTH];
1278 GNUNET_assert (NULL != active_task);
1279 GNUNET_assert (NULL != task);
1280 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1282 t->callback_cls = task_cls;
1284 t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1285 t->backtrace_strings =
1286 backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1291 int flags = fcntl (rfd, F_GETFD);
1293 if ((flags == -1) && (errno == EBADF))
1295 LOG (GNUNET_ERROR_TYPE_ERROR,
1296 "Got invalid file descriptor %d!\n",
1301 for (i = 0; i < t->num_backtrace_strings; i++)
1302 LOG (GNUNET_ERROR_TYPE_ERROR,
1304 t->backtrace_strings[i]);
1311 int flags = fcntl (wfd, F_GETFD);
1313 if (flags == -1 && errno == EBADF)
1315 LOG (GNUNET_ERROR_TYPE_ERROR,
1316 "Got invalid file descriptor %d!\n",
1321 for (i = 0; i < t->num_backtrace_strings; i++)
1322 LOG (GNUNET_ERROR_TYPE_DEBUG,
1324 t->backtrace_strings[i]);
1331 GNUNET_assert (wfd >= -1);
1334 t->start_time = GNUNET_TIME_absolute_get ();
1336 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1337 t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1338 t->lifeness = current_lifeness;
1339 GNUNET_CONTAINER_DLL_insert (pending_head,
1342 max_priority_added = GNUNET_MAX (max_priority_added,
1344 LOG (GNUNET_ERROR_TYPE_DEBUG,
1350 for (i = 0; i < t->num_backtrace_strings; i++)
1351 LOG (GNUNET_ERROR_TYPE_DEBUG,
1352 "Task %p trace %d: %s\n",
1355 t->backtrace_strings[i]);
1363 * Schedule a new task to be run with a specified delay or when the
1364 * specified file descriptor is ready for reading. The delay can be
1365 * used as a timeout on the socket being ready. The task will be
1366 * scheduled for execution once either the delay has expired or the
1367 * socket operation is ready. It will be run with the DEFAULT priority.
1369 * @param delay when should this operation time out? Use
1370 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1371 * @param rfd read file-descriptor
1372 * @param task main function of the task
1373 * @param task_cls closure of @a task
1374 * @return unique task identifier for the job
1375 * only valid until @a task is started!
1377 struct GNUNET_SCHEDULER_Task *
1378 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1379 struct GNUNET_NETWORK_Handle *rfd,
1380 GNUNET_SCHEDULER_TaskCallback task,
1383 return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1384 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1385 rfd, task, task_cls);
1390 * Schedule a new task to be run with a specified priority and to be
1391 * run after the specified delay or when the specified file descriptor
1392 * is ready for reading. The delay can be used as a timeout on the
1393 * socket being ready. The task will be scheduled for execution once
1394 * either the delay has expired or the socket operation is ready. It
1395 * will be run with the DEFAULT priority.
1397 * @param delay when should this operation time out? Use
1398 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1399 * @param priority priority to use for the task
1400 * @param rfd read file-descriptor
1401 * @param task main function of the task
1402 * @param task_cls closure of @a task
1403 * @return unique task identifier for the job
1404 * only valid until @a task is started!
1406 struct GNUNET_SCHEDULER_Task *
1407 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1408 enum GNUNET_SCHEDULER_Priority priority,
1409 struct GNUNET_NETWORK_Handle *rfd,
1410 GNUNET_SCHEDULER_TaskCallback task,
1413 return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1422 * Schedule a new task to be run with a specified delay or when the
1423 * specified file descriptor is ready for writing. The delay can be
1424 * used as a timeout on the socket being ready. The task will be
1425 * scheduled for execution once either the delay has expired or the
1426 * socket operation is ready. It will be run with the priority of
1429 * @param delay when should this operation time out? Use
1430 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1431 * @param wfd write file-descriptor
1432 * @param task main function of the task
1433 * @param task_cls closure of @a task
1434 * @return unique task identifier for the job
1435 * only valid until @a task is started!
1437 struct GNUNET_SCHEDULER_Task *
1438 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1439 struct GNUNET_NETWORK_Handle *wfd,
1440 GNUNET_SCHEDULER_TaskCallback task,
1443 return GNUNET_SCHEDULER_add_net_with_priority (delay,
1444 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1446 GNUNET_NO, GNUNET_YES,
1451 * Schedule a new task to be run with a specified delay or when the
1452 * specified file descriptor is ready. The delay can be
1453 * used as a timeout on the socket being ready. The task will be
1454 * scheduled for execution once either the delay has expired or the
1455 * socket operation is ready.
1457 * @param delay when should this operation time out? Use
1458 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1459 * @param priority priority of the task
1460 * @param fd file-descriptor
1461 * @param on_read whether to poll the file-descriptor for readability
1462 * @param on_write whether to poll the file-descriptor for writability
1463 * @param task main function of the task
1464 * @param task_cls closure of task
1465 * @return unique task identifier for the job
1466 * only valid until "task" is started!
1468 struct GNUNET_SCHEDULER_Task *
1469 GNUNET_SCHEDULER_add_net_with_priority (struct GNUNET_TIME_Relative delay,
1470 enum GNUNET_SCHEDULER_Priority priority,
1471 struct GNUNET_NETWORK_Handle *fd,
1474 GNUNET_SCHEDULER_TaskCallback task,
1478 struct GNUNET_NETWORK_FDSet *s;
1479 struct GNUNET_SCHEDULER_Task * ret;
1481 GNUNET_assert (NULL != fd);
1482 s = GNUNET_NETWORK_fdset_create ();
1483 GNUNET_NETWORK_fdset_set (s, fd);
1484 ret = GNUNET_SCHEDULER_add_select (
1487 on_write ? s : NULL,
1489 GNUNET_NETWORK_fdset_destroy (s);
1492 GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1493 return add_without_sets (delay, priority,
1494 on_read ? GNUNET_NETWORK_get_fd (fd) : -1,
1495 on_write ? GNUNET_NETWORK_get_fd (fd) : -1,
1502 * Schedule a new task to be run with a specified delay or when the
1503 * specified file descriptor is ready for reading. The delay can be
1504 * used as a timeout on the socket being ready. The task will be
1505 * scheduled for execution once either the delay has expired or the
1506 * socket operation is ready. It will be run with the DEFAULT priority.
1508 * @param delay when should this operation time out? Use
1509 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1510 * @param rfd read file-descriptor
1511 * @param task main function of the task
1512 * @param task_cls closure of @a task
1513 * @return unique task identifier for the job
1514 * only valid until @a task is started!
1516 struct GNUNET_SCHEDULER_Task *
1517 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1518 const struct GNUNET_DISK_FileHandle *rfd,
1519 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1521 return GNUNET_SCHEDULER_add_file_with_priority (
1522 delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1523 rfd, GNUNET_YES, GNUNET_NO,
1529 * Schedule a new task to be run with a specified delay or when the
1530 * specified file descriptor is ready for writing. The delay can be
1531 * used as a timeout on the socket being ready. The task will be
1532 * scheduled for execution once either the delay has expired or the
1533 * socket operation is ready. It will be run with the DEFAULT priority.
1535 * @param delay when should this operation time out? Use
1536 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1537 * @param wfd write file-descriptor
1538 * @param task main function of the task
1539 * @param task_cls closure of @a task
1540 * @return unique task identifier for the job
1541 * only valid until @a task is started!
1543 struct GNUNET_SCHEDULER_Task *
1544 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1545 const struct GNUNET_DISK_FileHandle *wfd,
1546 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1548 return GNUNET_SCHEDULER_add_file_with_priority (
1549 delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1550 wfd, GNUNET_NO, GNUNET_YES,
1556 * Schedule a new task to be run with a specified delay or when the
1557 * specified file descriptor is ready. The delay can be
1558 * used as a timeout on the socket being ready. The task will be
1559 * scheduled for execution once either the delay has expired or the
1560 * socket operation is ready.
1562 * @param delay when should this operation time out? Use
1563 * #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1564 * @param priority priority of the task
1565 * @param fd file-descriptor
1566 * @param on_read whether to poll the file-descriptor for readability
1567 * @param on_write whether to poll the file-descriptor for writability
1568 * @param task main function of the task
1569 * @param task_cls closure of @a task
1570 * @return unique task identifier for the job
1571 * only valid until @a task is started!
1573 struct GNUNET_SCHEDULER_Task *
1574 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1575 enum GNUNET_SCHEDULER_Priority priority,
1576 const struct GNUNET_DISK_FileHandle *fd,
1577 int on_read, int on_write,
1578 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1581 struct GNUNET_NETWORK_FDSet *s;
1582 struct GNUNET_SCHEDULER_Task * ret;
1584 GNUNET_assert (NULL != fd);
1585 s = GNUNET_NETWORK_fdset_create ();
1586 GNUNET_NETWORK_fdset_handle_set (s, fd);
1587 ret = GNUNET_SCHEDULER_add_select (
1590 on_write ? s : NULL,
1592 GNUNET_NETWORK_fdset_destroy (s);
1597 GNUNET_DISK_internal_file_handle_ (fd, &real_fd, sizeof (int));
1598 GNUNET_assert (real_fd >= 0);
1599 return add_without_sets (
1601 on_read ? real_fd : -1,
1602 on_write ? real_fd : -1,
1609 * Schedule a new task to be run with a specified delay or when any of
1610 * the specified file descriptor sets is ready. The delay can be used
1611 * as a timeout on the socket(s) being ready. The task will be
1612 * scheduled for execution once either the delay has expired or any of
1613 * the socket operations is ready. This is the most general
1614 * function of the "add" family. Note that the "prerequisite_task"
1615 * must be satisfied in addition to any of the other conditions. In
1616 * other words, the task will be started when
1618 * (prerequisite-run)
1622 * || (shutdown-active && run-on-shutdown) )
1625 * @param prio how important is this task?
1626 * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1627 * which means that the task will only be run after we receive SIGTERM
1628 * @param rs set of file descriptors we want to read (can be NULL)
1629 * @param ws set of file descriptors we want to write (can be NULL)
1630 * @param task main function of the task
1631 * @param task_cls closure of @a task
1632 * @return unique task identifier for the job
1633 * only valid until @a task is started!
1635 struct GNUNET_SCHEDULER_Task *
1636 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1637 struct GNUNET_TIME_Relative delay,
1638 const struct GNUNET_NETWORK_FDSet *rs,
1639 const struct GNUNET_NETWORK_FDSet *ws,
1640 GNUNET_SCHEDULER_TaskCallback task,
1643 struct GNUNET_SCHEDULER_Task *t;
1645 void *backtrace_array[MAX_TRACE_DEPTH];
1648 if ( (NULL == rs) &&
1650 return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1654 GNUNET_assert (NULL != active_task);
1655 GNUNET_assert (NULL != task);
1656 t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1658 t->callback_cls = task_cls;
1660 t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1661 t->backtrace_strings =
1662 backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1668 t->read_set = GNUNET_NETWORK_fdset_create ();
1669 GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1673 t->write_set = GNUNET_NETWORK_fdset_create ();
1674 GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1677 t->start_time = GNUNET_TIME_absolute_get ();
1679 t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1681 check_priority ((prio ==
1682 GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1684 t->lifeness = current_lifeness;
1685 GNUNET_CONTAINER_DLL_insert (pending_head,
1688 max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
1689 LOG (GNUNET_ERROR_TYPE_DEBUG,
1695 for (i = 0; i < t->num_backtrace_strings; i++)
1696 LOG (GNUNET_ERROR_TYPE_DEBUG,
1697 "Task p trace %d: %s\n",
1700 t->backtrace_strings[i]);
1705 /* end of scheduler.c */