check
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       (C) 2009 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 2, or (at your
8       option) any later version.
9
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.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file util/scheduler.c
23  * @brief schedule computations using continuation passing style
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_common.h"
28 #include "gnunet_os_lib.h"
29 #include "gnunet_scheduler_lib.h"
30 #include "gnunet_signal_lib.h"
31 #include "gnunet_time_lib.h"
32 #include "disk.h"
33 #ifdef LINUX
34 #include "execinfo.h"
35
36
37 /**
38  * Use lsof to generate file descriptor reports on select error?
39  * (turn off for stable releases).
40  */
41 #define USE_LSOF GNUNET_YES
42
43 /**
44  * Obtain trace information for all scheduler calls that schedule tasks.
45  */
46 #define EXECINFO GNUNET_NO
47
48 /**
49  * Depth of the traces collected via EXECINFO.
50  */
51 #define MAX_TRACE_DEPTH 50
52 #endif
53
54 #define DEBUG_TASKS GNUNET_NO
55
56 /**
57  * Should we figure out which tasks are delayed for a while
58  * before they are run? (Consider using in combination with EXECINFO).
59  */
60 #define PROFILE_DELAYS GNUNET_NO
61
62 /**
63  * Task that were in the queue for longer than this are reported if
64  * PROFILE_DELAYS is active.
65  */
66 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
67
68 /**
69  * Linked list of pending tasks.
70  */
71 struct Task
72 {
73   /**
74    * This is a linked list.
75    */
76   struct Task *next;
77
78   /**
79    * Function to run when ready.
80    */
81   GNUNET_SCHEDULER_Task callback;
82
83   /**
84    * Closure for the callback.
85    */
86   void *callback_cls;
87
88   /**
89    * Set of file descriptors this task is waiting
90    * for for reading.  Once ready, this is updated
91    * to reflect the set of file descriptors ready
92    * for operation.
93    */
94   struct GNUNET_NETWORK_FDSet *read_set;
95
96   /**
97    * Set of file descriptors this task is waiting for for writing.
98    * Once ready, this is updated to reflect the set of file
99    * descriptors ready for operation.
100    */
101   struct GNUNET_NETWORK_FDSet *write_set;
102
103   /**
104    * Unique task identifier.
105    */
106   GNUNET_SCHEDULER_TaskIdentifier id;
107
108   /**
109    * Identifier of a prerequisite task.
110    */
111   GNUNET_SCHEDULER_TaskIdentifier prereq_id;
112
113   /**
114    * Absolute timeout value for the task, or
115    * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
116    */
117   struct GNUNET_TIME_Absolute timeout;
118
119 #if PROFILE_DELAYS
120   /**
121    * When was the task scheduled?
122    */
123   struct GNUNET_TIME_Absolute start_time;
124 #endif
125
126   /**
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.
130    */
131   enum GNUNET_SCHEDULER_Reason reason;
132
133   /**
134    * Task priority.
135    */
136   enum GNUNET_SCHEDULER_Priority priority;
137
138   /**
139    * Set if we only wait for reading from a single FD, otherwise -1.
140    */
141   int read_fd;
142
143   /**
144    * Set if we only wait for writing to a single FD, otherwise -1.
145    */
146   int write_fd;
147
148 #if EXECINFO
149   /**
150    * Array of strings which make up a backtrace from the point when this
151    * task was scheduled (essentially, who scheduled the task?)
152    */
153   char **backtrace_strings;
154
155   /**
156    * Size of the backtrace_strings array
157    */
158   int num_backtrace_strings;
159 #endif
160
161
162 };
163
164
165 /**
166  * Handle for the scheduling service.
167  */
168 struct GNUNET_SCHEDULER_Handle
169 {
170
171   /**
172    * List of tasks waiting for an event.
173    */
174   struct Task *pending;
175
176   /**
177    * ID of the task that is running right now.
178    */
179   struct Task *active_task;
180
181   /**
182    * List of tasks ready to run right now,
183    * grouped by importance.
184    */
185   struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
186
187   /**
188    * Identity of the last task queued.  Incremented for each task to
189    * generate a unique task ID (it is virtually impossible to start
190    * more than 2^64 tasks during the lifetime of a process).
191    */
192   GNUNET_SCHEDULER_TaskIdentifier last_id;
193
194   /**
195    * Highest number so that all tasks with smaller identifiers
196    * have already completed.  Also the lowest number of a task
197    * still waiting to be executed.
198    */
199   GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
200
201   /**
202    * Number of tasks on the ready list.
203    */
204   unsigned int ready_count;
205
206   /**
207    * How many tasks have we run so far?
208    */
209   unsigned long long tasks_run;
210
211   /**
212    * Priority of the task running right now.  Only
213    * valid while a task is running.
214    */
215   enum GNUNET_SCHEDULER_Priority current_priority;
216
217   /**
218    * Priority of the highest task added in the current select
219    * iteration.
220    */
221   enum GNUNET_SCHEDULER_Priority max_priority_added;
222
223   /**
224    * How 'nice' are we right now?
225    */
226   int nice_level;
227
228 };
229
230
231 /**
232  * Check that the given priority is legal (and return it).
233  *
234  * @param p priority value to check
235  * @return p on success, 0 on error
236  */
237 static enum GNUNET_SCHEDULER_Priority
238 check_priority (enum GNUNET_SCHEDULER_Priority p)
239 {
240   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
241     return p;
242   GNUNET_assert (0);
243   return 0;                     /* make compiler happy */
244 }
245
246
247 /**
248  * Is a task with this identifier still pending?  Also updates
249  * "lowest_pending_id" as a side-effect (for faster checks in the
250  * future), but only if the return value is "GNUNET_NO" (and
251  * the "lowest_pending_id" check failed).
252  *
253  * @param sched the scheduler
254  * @param id which task are we checking for
255  * @return GNUNET_YES if so, GNUNET_NO if not
256  */
257 static int
258 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
259             GNUNET_SCHEDULER_TaskIdentifier id)
260 {
261   struct Task *pos;
262   enum GNUNET_SCHEDULER_Priority p;
263   GNUNET_SCHEDULER_TaskIdentifier min;
264
265   if (id < sched->lowest_pending_id)
266     return GNUNET_NO;
267   min = -1;                     /* maximum value */
268   pos = sched->pending;
269   while (pos != NULL)
270     {
271       if (pos->id == id)
272         return GNUNET_YES;
273       if (pos->id < min)
274         min = pos->id;
275       pos = pos->next;
276     }
277   for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
278     {
279       pos = sched->ready[p];
280       while (pos != NULL)
281         {
282           if (pos->id == id)
283             return GNUNET_YES;
284           if (pos->id < min)
285             min = pos->id;
286           pos = pos->next;
287         }
288     }
289   sched->lowest_pending_id = min;
290   return GNUNET_NO;
291 }
292
293
294 /**
295  * Update all sets and timeout for select.
296  *
297  * @param sched the scheduler
298  * @param rs read-set, set to all FDs we would like to read (updated)
299  * @param ws write-set, set to all FDs we would like to write (updated)
300  * @param timeout next timeout (updated)
301  */
302 static void
303 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
304              struct GNUNET_NETWORK_FDSet *rs,
305              struct GNUNET_NETWORK_FDSet *ws,
306              struct GNUNET_TIME_Relative *timeout)
307 {
308   struct Task *pos;
309
310   pos = sched->pending;
311   while (pos != NULL)
312     {
313       if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
314           (GNUNET_YES == is_pending (sched, pos->prereq_id)))
315         {
316           pos = pos->next;
317           continue;
318         }
319
320       if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
321         {
322           struct GNUNET_TIME_Relative to;
323
324           to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
325           if (timeout->value > to.value)
326             *timeout = to;
327         }
328       if (pos->read_fd != -1)
329         GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
330       if (pos->write_fd != -1)
331         GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
332       if (pos->read_set != NULL)
333         GNUNET_NETWORK_fdset_add (rs, pos->read_set);
334       if (pos->write_set != NULL)
335         GNUNET_NETWORK_fdset_add (ws, pos->write_set);
336       if (pos->reason != 0)
337         *timeout = GNUNET_TIME_UNIT_ZERO;
338       pos = pos->next;
339     }
340 }
341
342
343 /**
344  * Check if the ready set overlaps with the set we want to have ready.
345  * If so, update the want set (set all FDs that are ready).  If not,
346  * return GNUNET_NO.
347  *
348  * @param ready set that is ready
349  * @param want set that we want to be ready
350  * @return GNUNET_YES if there was some overlap
351  */
352 static int
353 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
354               struct GNUNET_NETWORK_FDSet *want)
355 {
356   if ( (NULL == want) || (NULL == ready) )
357     return GNUNET_NO;
358   if (GNUNET_NETWORK_fdset_overlap (ready, want))
359     {
360       /* copy all over (yes, there maybe unrelated bits,
361          but this should not hurt well-written clients) */
362       GNUNET_NETWORK_fdset_copy (want, ready);
363       return GNUNET_YES;
364     }
365   return GNUNET_NO;
366 }
367
368
369 /**
370  * Check if the given task is eligible to run now.
371  * Also set the reason why it is eligible.
372  *
373  * @param sched the scheduler
374  * @param task task to check if it is ready
375  * @param now the current time
376  * @param rs set of FDs ready for reading
377  * @param ws set of FDs ready for writing
378  * @return GNUNET_YES if we can run it, GNUNET_NO if not.
379  */
380 static int
381 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
382           struct Task *task,
383           struct GNUNET_TIME_Absolute now,
384           const struct GNUNET_NETWORK_FDSet *rs,
385           const struct GNUNET_NETWORK_FDSet *ws)
386 {
387   if (now.value >= task->timeout.value)
388     task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
389   if ( (0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
390        ( (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd)) ||
391          (set_overlaps (rs, task->read_set) ) ) )
392     task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
393   if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
394       ( (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)) ||
395         (set_overlaps (ws, task->write_set) ) ) )
396     task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
397   if (task->reason == 0)
398     return GNUNET_NO;           /* not ready */    
399   if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
400     {
401       if (GNUNET_YES == is_pending (sched, task->prereq_id))
402         return GNUNET_NO;       /* prereq waiting */
403       task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
404     }
405   return GNUNET_YES;
406 }
407
408
409 /**
410  * Put a task that is ready for execution into the ready queue.
411  *
412  * @param handle the scheduler
413  * @param task task ready for execution
414  */
415 static void
416 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
417 {
418   enum GNUNET_SCHEDULER_Priority p = task->priority;
419   if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
420     p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
421   task->next = handle->ready[check_priority (p)];
422   handle->ready[check_priority (p)] = task;
423   handle->ready_count++;
424 }
425
426
427 /**
428  * Check which tasks are ready and move them
429  * to the respective ready queue.
430  *
431  * @param handle the scheduler
432  * @param rs FDs ready for reading
433  * @param ws FDs ready for writing
434  */
435 static void
436 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
437              const struct GNUNET_NETWORK_FDSet *rs,
438              const struct GNUNET_NETWORK_FDSet *ws)
439 {
440   struct Task *pos;
441   struct Task *prev;
442   struct Task *next;
443   struct GNUNET_TIME_Absolute now;
444
445   now = GNUNET_TIME_absolute_get ();
446   prev = NULL;
447   pos = handle->pending;
448   while (pos != NULL)
449     {
450 #if DEBUG_TASKS
451       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452                   "Checking readiness of task: %llu / %p\n",
453                   pos->id, pos->callback_cls);
454 #endif
455       next = pos->next;
456       if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
457         {
458           if (prev == NULL)
459             handle->pending = next;
460           else
461             prev->next = next;
462           queue_ready_task (handle, pos);
463           pos = next;
464           continue;
465         }
466       prev = pos;
467       pos = next;
468     }
469 }
470
471
472 /**
473  * Request the shutdown of a scheduler.  Marks all currently
474  * pending tasks as ready because of shutdown.  This will
475  * cause all tasks to run (as soon as possible, respecting
476  * priorities and prerequisite tasks).  Note that tasks
477  * scheduled AFTER this call may still be delayed arbitrarily.
478  *
479  * @param sched the scheduler
480  */
481 void
482 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
483 {
484   struct Task *pos;
485   int i;
486
487   pos = sched->pending;
488   while (pos != NULL)
489     {
490       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
491       /* we don't move the task into the ready queue yet; check_ready
492          will do that later, possibly adding additional
493          readiness-factors */
494       pos = pos->next;
495     }
496   for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
497     {
498       pos = sched->ready[i];
499       while (pos != NULL)
500         {
501           pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
502           /* we don't move the task into the ready queue yet; check_ready
503              will do that later, possibly adding additional
504              readiness-factors */
505           pos = pos->next;
506         }
507     }  
508 }
509
510
511 /**
512  * Destroy a task (release associated resources)
513  *
514  * @param t task to destroy
515  */
516 static void
517 destroy_task (struct Task *t)
518 {
519   if (NULL != t->read_set)
520     GNUNET_NETWORK_fdset_destroy (t->read_set);
521   if (NULL != t->write_set)
522     GNUNET_NETWORK_fdset_destroy (t->write_set);
523 #if EXECINFO
524   GNUNET_free (t->backtrace_strings);
525 #endif
526   GNUNET_free (t);
527 }
528
529
530 /**
531  * Run at least one task in the highest-priority queue that is not
532  * empty.  Keep running tasks until we are either no longer running
533  * "URGENT" tasks or until we have at least one "pending" task (which
534  * may become ready, hence we should select on it).  Naturally, if
535  * there are no more ready tasks, we also return.  
536  *
537  * @param sched the scheduler
538  * @param rs FDs ready for reading
539  * @param ws FDs ready for writing
540  */
541 static void
542 run_ready (struct GNUNET_SCHEDULER_Handle *sched,
543            struct GNUNET_NETWORK_FDSet *rs,
544            struct GNUNET_NETWORK_FDSet *ws)
545 {
546   enum GNUNET_SCHEDULER_Priority p;
547   struct Task *pos;
548   struct GNUNET_SCHEDULER_TaskContext tc;
549
550   sched->max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
551   do
552     {
553       if (sched->ready_count == 0)
554         return;
555       GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
556       /* yes, p>0 is correct, 0 is "KEEP" which should
557          always be an empty queue (see assertion)! */
558       for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
559         {
560           pos = sched->ready[p];
561           if (pos != NULL)
562             break;
563         }
564       GNUNET_assert (pos != NULL);      /* ready_count wrong? */
565       sched->ready[p] = pos->next;
566       sched->ready_count--;
567       if (sched->current_priority != pos->priority)
568         {
569           sched->current_priority = pos->priority;
570           (void) GNUNET_OS_set_process_priority (0, pos->priority);
571         }
572       sched->active_task = pos;
573 #if PROFILE_DELAYS
574       if (GNUNET_TIME_absolute_get_duration (pos->start_time).value >
575           DELAY_THRESHOLD.value)
576         {
577           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
578                       "Task %u took %llums to be scheduled\n",
579                       pos->id,
580                       (unsigned long long) GNUNET_TIME_absolute_get_duration (pos->start_time).value);
581         }
582 #endif
583       tc.sched = sched;
584       tc.reason = pos->reason;
585       tc.read_ready = (pos->read_set == NULL) ? rs : pos->read_set; 
586       if ( (pos->read_fd != -1) &&
587            (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) )
588         GNUNET_NETWORK_fdset_set_native (rs,
589                                          pos->read_fd);
590       tc.write_ready = (pos->write_set == NULL) ? ws : pos->write_set;
591       if ( (pos->write_fd != -1) &&
592            (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) )
593         GNUNET_NETWORK_fdset_set_native (ws,
594                                          pos->write_fd);
595       if ( ( (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0) &&
596            (pos->write_fd != -1) &&
597            (! GNUNET_NETWORK_fdset_test_native (ws,
598                                                 pos->write_fd))) 
599         abort (); // added to ready in previous select loop!
600 #if DEBUG_TASKS
601       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602                   "Running task: %llu / %p\n", pos->id, pos->callback_cls);
603 #endif
604       pos->callback (pos->callback_cls, &tc);
605 #if EXECINFO
606       int i;
607       for (i=0;i<pos->num_backtrace_strings;i++)
608         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
609                     "Task %u trace %d: %s\n",
610                     pos->id,
611                     i,
612                     pos->backtrace_strings[i]);
613 #endif
614       sched->active_task = NULL;
615       destroy_task (pos);
616       sched->tasks_run++;
617     }
618   while ((sched->pending == NULL) || (p >= sched->max_priority_added));
619 }
620
621 /**
622  * Pipe used to communicate shutdown via signal.
623  */
624 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
625
626 /**
627  * Signal handler called for SIGPIPE.
628  */
629 static void
630 sighandler_pipe ()
631 {
632   return;
633 }
634
635 /**
636  * Signal handler called for signals that should cause us to shutdown.
637  */
638 static void
639 sighandler_shutdown ()
640 {
641   static char c;
642
643   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
644                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE), &c,
645                           sizeof (c));
646 }
647
648
649 /**
650  * Initialize and run scheduler.  This function will return when all
651  * tasks have completed.  On systems with signals, receiving a SIGTERM
652  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
653  * to be run after the active task is complete.  As a result, SIGTERM
654  * causes all active tasks to be scheduled with reason
655  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
656  * afterwards will execute normally!). Note that any particular signal
657  * will only shut down one scheduler; applications should always only
658  * create a single scheduler.
659  *
660  * @param task task to run immediately
661  * @param task_cls closure of task
662  */
663 void
664 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
665 {
666   struct GNUNET_SCHEDULER_Handle sched;
667   struct GNUNET_NETWORK_FDSet *rs;
668   struct GNUNET_NETWORK_FDSet *ws;
669   struct GNUNET_TIME_Relative timeout;
670   int ret;
671   struct GNUNET_SIGNAL_Context *shc_int;
672   struct GNUNET_SIGNAL_Context *shc_term;
673   struct GNUNET_SIGNAL_Context *shc_quit;
674   struct GNUNET_SIGNAL_Context *shc_hup;
675   struct GNUNET_SIGNAL_Context *shc_pipe;
676   unsigned long long last_tr;
677   unsigned int busy_wait_warning;
678   const struct GNUNET_DISK_FileHandle *pr;
679   char c;
680
681   rs = GNUNET_NETWORK_fdset_create ();
682   ws = GNUNET_NETWORK_fdset_create ();
683   GNUNET_assert (shutdown_pipe_handle == NULL);
684   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO);
685   GNUNET_assert (shutdown_pipe_handle != NULL);
686   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_READ);
687   GNUNET_assert (pr != NULL);
688   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
689   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
690   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
691 #ifndef MINGW
692   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
693   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
694 #endif
695   memset (&sched, 0, sizeof (sched));
696   sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
697   GNUNET_SCHEDULER_add_continuation (&sched,
698                                      task,
699                                      task_cls,
700                                      GNUNET_SCHEDULER_REASON_STARTUP);
701   last_tr = 0;
702   busy_wait_warning = 0;
703   while ((sched.pending != NULL) || (sched.ready_count > 0))
704     {
705       GNUNET_NETWORK_fdset_zero (rs);
706       GNUNET_NETWORK_fdset_zero (ws);
707       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
708       update_sets (&sched, rs, ws, &timeout);
709       GNUNET_NETWORK_fdset_handle_set (rs, pr);
710       if (sched.ready_count > 0)
711         {
712           /* no blocking, more work already ready! */
713           timeout = GNUNET_TIME_UNIT_ZERO;
714         }
715       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
716       if (ret == GNUNET_SYSERR)
717         {
718           if (errno == EINTR)
719             continue;
720
721           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
722 #ifndef MINGW
723 #if USE_LSOF
724           char lsof[512];
725           snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
726           close (1);
727           dup2 (2, 1);
728           system (lsof);                  
729 #endif
730 #endif
731           abort ();
732           break;
733         }
734       if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
735         {
736           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
737                       _("Looks like we're busy waiting...\n"));
738           sleep (1);            /* mitigate */
739         }
740       check_ready (&sched, rs, ws);
741       run_ready (&sched, rs, ws);
742       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
743         {
744           /* consume the signal */
745           GNUNET_DISK_file_read (pr, &c, sizeof (c));
746           /* mark all active tasks as ready due to shutdown */
747           GNUNET_SCHEDULER_shutdown (&sched);
748         }
749       if (last_tr == sched.tasks_run)
750         {
751           busy_wait_warning++;
752         }
753       else
754         {
755           last_tr = sched.tasks_run;
756           busy_wait_warning = 0;
757         }
758     }
759   GNUNET_SIGNAL_handler_uninstall (shc_int);
760   GNUNET_SIGNAL_handler_uninstall (shc_term);
761 #ifndef MINGW
762   GNUNET_SIGNAL_handler_uninstall (shc_quit);
763   GNUNET_SIGNAL_handler_uninstall (shc_hup);
764 #endif
765   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
766   shutdown_pipe_handle = NULL;
767   GNUNET_NETWORK_fdset_destroy (rs);
768   GNUNET_NETWORK_fdset_destroy (ws);
769 }
770
771
772 /**
773  * Obtain the reason code for why the current task was
774  * started.  Will return the same value as 
775  * the GNUNET_SCHEDULER_TaskContext's reason field.
776  *
777  * @param sched scheduler to query
778  * @return reason(s) why the current task is run
779  */
780 enum GNUNET_SCHEDULER_Reason
781 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
782 {
783   return sched->active_task->reason;
784 }
785
786
787 /**
788  * Get information about the current load of this scheduler.  Use this
789  * function to determine if an elective task should be added or simply
790  * dropped (if the decision should be made based on the number of
791  * tasks ready to run).
792  *
793  * @param sched scheduler to query
794  * @param p priority level to look at
795  * @return number of tasks pending right now
796  */
797 unsigned int
798 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
799                            enum GNUNET_SCHEDULER_Priority p)
800 {
801   struct Task *pos;
802   unsigned int ret;
803
804   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
805     return sched->ready_count;
806   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
807     p = sched->current_priority;
808   ret = 0;
809   pos = sched->ready[p];
810   while (pos != NULL)
811     {
812       pos = pos->next;
813       ret++;
814     }
815   return ret;
816 }
817
818
819 /**
820  * Cancel the task with the specified identifier.
821  * The task must not yet have run.
822  *
823  * @param sched scheduler to use
824  * @param task id of the task to cancel
825  * @return original closure of the task
826  */
827 void *
828 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
829                          GNUNET_SCHEDULER_TaskIdentifier task)
830 {
831   struct Task *t;
832   struct Task *prev;
833   enum GNUNET_SCHEDULER_Priority p;
834   void *ret;
835
836   prev = NULL;
837   t = sched->pending;
838   while (t != NULL)
839     {
840       if (t->id == task)
841         break;
842       prev = t;
843       t = t->next;
844     }
845   p = 0;
846   while (t == NULL)
847     {
848       p++;
849       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
850       prev = NULL;
851       t = sched->ready[p];
852       while (t != NULL)
853         {
854           if (t->id == task)
855             {
856               sched->ready_count--;
857               break;
858             }
859           prev = t;
860           t = t->next;
861         }
862     }
863   if (prev == NULL)
864     {
865       if (p == 0)
866         sched->pending = t->next;
867       else
868         sched->ready[p] = t->next;
869     }
870   else
871     prev->next = t->next;
872   ret = t->callback_cls;
873 #if DEBUG_TASKS
874   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
875               "Canceling task: %llu / %p\n", task, t->callback_cls);
876 #endif
877   destroy_task (t);
878   return ret;
879 }
880
881
882 /**
883  * Continue the current execution with the given function.  This is
884  * similar to the other "add" functions except that there is no delay
885  * and the reason code can be specified.
886  *
887  * @param sched scheduler to use
888  * @param task main function of the task
889  * @param task_cls closure for 'main'
890  * @param reason reason for task invocation
891  */
892 void
893 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
894                                    GNUNET_SCHEDULER_Task task,
895                                    void *task_cls,
896                                    enum GNUNET_SCHEDULER_Reason reason)
897 {
898   struct Task *t;
899 #if EXECINFO
900   void *backtrace_array[50];
901 #endif
902   t = GNUNET_malloc (sizeof (struct Task));
903 #if EXECINFO
904   t->num_backtrace_strings = backtrace(backtrace_array, 50);
905   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
906 #endif
907   t->read_fd = -1;
908   t->write_fd = -1;
909   t->callback = task;
910   t->callback_cls = task_cls;
911   t->id = ++sched->last_id;
912 #if PROFILE_DELAYS
913   t->start_time = GNUNET_TIME_absolute_get ();
914 #endif
915   t->reason = reason;
916   t->priority = sched->current_priority;
917 #if DEBUG_TASKS
918   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
919               "Adding continuation task: %llu / %p\n",
920               t->id, t->callback_cls);
921 #endif
922   queue_ready_task (sched, t);
923 }
924
925
926
927 /**
928  * Schedule a new task to be run after the specified prerequisite task
929  * has completed. It will be run with the priority of the calling
930  * task.
931  *
932  * @param sched scheduler to use
933  * @param prerequisite_task run this task after the task with the given
934  *        task identifier completes (and any of our other
935  *        conditions, such as delay, read or write-readiness
936  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
937  *        on completion of other tasks (this will cause the task to run as
938  *        soon as possible).
939  * @param task main function of the task
940  * @param task_cls closure of task
941  * @return unique task identifier for the job
942  *         only valid until "task" is started!
943  */
944 GNUNET_SCHEDULER_TaskIdentifier
945 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
946                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
947                             GNUNET_SCHEDULER_Task task, void *task_cls)
948 {
949   return GNUNET_SCHEDULER_add_select (sched,
950                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
951                                       prerequisite_task,
952                                       GNUNET_TIME_UNIT_ZERO,
953                                       NULL, NULL, task, task_cls);
954 }
955
956
957 /**
958  * Schedule a new task to be run with a specified priority.
959  *
960  * @param sched scheduler to use
961  * @param prio how important is the new task?
962  * @param task main function of the task
963  * @param task_cls closure of task
964  * @return unique task identifier for the job
965  *         only valid until "task" is started!
966  */
967 GNUNET_SCHEDULER_TaskIdentifier
968 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
969                                     enum GNUNET_SCHEDULER_Priority prio,
970                                     GNUNET_SCHEDULER_Task task,
971                                     void *task_cls)
972 {
973   return GNUNET_SCHEDULER_add_select (sched,
974                                       prio,
975                                       GNUNET_SCHEDULER_NO_TASK,
976                                       GNUNET_TIME_UNIT_ZERO,
977                                       NULL, NULL, task, task_cls);
978 }
979
980
981
982 /**
983  * Schedule a new task to be run with a specified delay.  The task
984  * will be scheduled for execution once the delay has expired. It
985  * will be run with the priority of the calling task.
986  *
987  * @param sched scheduler to use
988  * @param delay when should this operation time out? Use 
989  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
990  * @param task main function of the task
991  * @param task_cls closure of task
992  * @return unique task identifier for the job
993  *         only valid until "task" is started!
994  */
995 GNUNET_SCHEDULER_TaskIdentifier
996 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
997                               struct GNUNET_TIME_Relative delay,
998                               GNUNET_SCHEDULER_Task task, void *task_cls)
999 {
1000   return GNUNET_SCHEDULER_add_select (sched,
1001                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1002                                       GNUNET_SCHEDULER_NO_TASK, delay,
1003                                       NULL, NULL, task, task_cls);
1004 }
1005
1006
1007
1008 /**
1009  * Schedule a new task to be run as soon as possible. The task
1010  * will be run with the priority of the calling task.
1011  *
1012  * @param sched scheduler to use
1013  * @param task main function of the task
1014  * @param task_cls closure of task
1015  * @return unique task identifier for the job
1016  *         only valid until "task" is started!
1017  */
1018 GNUNET_SCHEDULER_TaskIdentifier
1019 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
1020                           GNUNET_SCHEDULER_Task task,
1021                           void *task_cls)
1022 {
1023   return GNUNET_SCHEDULER_add_select (sched,
1024                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1025                                       GNUNET_SCHEDULER_NO_TASK,
1026                                       GNUNET_TIME_UNIT_ZERO,
1027                                       NULL, NULL, task, task_cls);
1028 }
1029
1030
1031
1032
1033 /**
1034  * Schedule a new task to be run with a specified delay or when any of
1035  * the specified file descriptor sets is ready.  The delay can be used
1036  * as a timeout on the socket(s) being ready.  The task will be
1037  * scheduled for execution once either the delay has expired or any of
1038  * the socket operations is ready.  This is the most general
1039  * function of the "add" family.  Note that the "prerequisite_task"
1040  * must be satisfied in addition to any of the other conditions.  In
1041  * other words, the task will be started when
1042  * <code>
1043  * (prerequisite-run)
1044  * && (delay-ready
1045  *     || any-rs-ready
1046  *     || any-ws-ready
1047  *     || (shutdown-active && run-on-shutdown) )
1048  * </code>
1049  *
1050  * @param sched scheduler to use
1051  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1052  *        which means that the task will only be run after we receive SIGTERM
1053  * @param rfd file descriptor we want to read (can be -1)
1054  * @param wfd file descriptors we want to write (can be -1)
1055  * @param task main function of the task
1056  * @param task_cls closure of task
1057  * @return unique task identifier for the job
1058  *         only valid until "task" is started!
1059  */
1060 GNUNET_SCHEDULER_TaskIdentifier
1061 add_without_sets (struct GNUNET_SCHEDULER_Handle * sched,
1062                   struct GNUNET_TIME_Relative delay,
1063                   int rfd,
1064                   int wfd,
1065                   GNUNET_SCHEDULER_Task task, void *task_cls)
1066 {
1067   struct Task *t;
1068 #if EXECINFO
1069   void *backtrace_array[MAX_TRACE_DEPTH];
1070 #endif
1071
1072   GNUNET_assert (NULL != task);
1073   t = GNUNET_malloc (sizeof (struct Task));
1074   t->callback = task;
1075   t->callback_cls = task_cls;
1076 #if EXECINFO
1077   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1078   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1079 #endif
1080   t->read_fd = rfd;
1081   t->write_fd = wfd;
1082   t->id = ++sched->last_id;
1083 #if PROFILE_DELAYS
1084   t->start_time = GNUNET_TIME_absolute_get ();
1085 #endif
1086   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1087   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1088   t->priority = check_priority (sched->current_priority);
1089   t->next = sched->pending;
1090   sched->pending = t;
1091   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1092                                           t->priority);
1093 #if DEBUG_TASKS
1094   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1096 #endif
1097 #if EXECINFO
1098   int i;
1099
1100   for (i=0;i<t->num_backtrace_strings;i++)
1101       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1102                   "Task %u trace %d: %s\n",
1103                   t->id,
1104                   i,
1105                   t->backtrace_strings[i]);
1106 #endif
1107   return t->id;
1108 }
1109
1110
1111
1112 /**
1113  * Schedule a new task to be run with a specified delay or when the
1114  * specified file descriptor is ready for reading.  The delay can be
1115  * used as a timeout on the socket being ready.  The task will be
1116  * scheduled for execution once either the delay has expired or the
1117  * socket operation is ready.  It will be run with the priority of
1118  * the calling task.
1119  *
1120  * @param sched scheduler to use
1121  * @param delay when should this operation time out? Use 
1122  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1123  * @param rfd read file-descriptor
1124  * @param task main function of the task
1125  * @param task_cls closure of task
1126  * @return unique task identifier for the job
1127  *         only valid until "task" is started!
1128  */
1129 GNUNET_SCHEDULER_TaskIdentifier
1130 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
1131                                struct GNUNET_TIME_Relative delay,
1132                                struct GNUNET_NETWORK_Handle * rfd,
1133                                GNUNET_SCHEDULER_Task task, void *task_cls)
1134 {
1135   return add_without_sets (sched,
1136                            delay,
1137                            GNUNET_NETWORK_get_fd (rfd),
1138                            -1,
1139                            task,
1140                            task_cls);
1141 }
1142
1143
1144 /**
1145  * Schedule a new task to be run with a specified delay or when the
1146  * specified file descriptor is ready for writing.  The delay can be
1147  * used as a timeout on the socket being ready.  The task will be
1148  * scheduled for execution once either the delay has expired or the
1149  * socket operation is ready.  It will be run with the priority of
1150  * the calling task.
1151  *
1152  * @param sched scheduler to use
1153  * @param delay when should this operation time out? Use 
1154  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1155  * @param wfd write file-descriptor
1156  * @param task main function of the task
1157  * @param task_cls closure of task
1158  * @return unique task identifier for the job
1159  *         only valid until "task" is started!
1160  */
1161 GNUNET_SCHEDULER_TaskIdentifier
1162 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
1163                                 struct GNUNET_TIME_Relative delay,
1164                                 struct GNUNET_NETWORK_Handle * wfd,
1165                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1166 {
1167   return add_without_sets (sched,
1168                            delay,
1169                            -1,
1170                            GNUNET_NETWORK_get_fd (wfd),
1171                            task,
1172                            task_cls);
1173 }
1174
1175
1176 /**
1177  * Schedule a new task to be run with a specified delay or when the
1178  * specified file descriptor is ready for reading.  The delay can be
1179  * used as a timeout on the socket being ready.  The task will be
1180  * scheduled for execution once either the delay has expired or the
1181  * socket operation is ready. It will be run with the priority of
1182  * the calling task.
1183  *
1184  * @param sched scheduler to use
1185  * @param delay when should this operation time out? Use 
1186  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1187  * @param rfd read file-descriptor
1188  * @param task main function of the task
1189  * @param task_cls closure of task
1190  * @return unique task identifier for the job
1191  *         only valid until "task" is started!
1192  */
1193 GNUNET_SCHEDULER_TaskIdentifier
1194 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1195                                 struct GNUNET_TIME_Relative delay,
1196                                 const struct GNUNET_DISK_FileHandle * rfd,
1197                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1198 {
1199 #if MINGW
1200   struct GNUNET_NETWORK_FDSet *rs;
1201   GNUNET_SCHEDULER_TaskIdentifier ret;
1202
1203   GNUNET_assert (rfd != NULL);
1204   rs = GNUNET_NETWORK_fdset_create ();
1205   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1206   ret = GNUNET_SCHEDULER_add_select (sched,
1207                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1208                                      GNUNET_SCHEDULER_NO_TASK, delay,
1209                                      rs, NULL, task, task_cls);
1210   GNUNET_NETWORK_fdset_destroy (rs);
1211   return ret;
1212 #else
1213   int fd;
1214
1215   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1216   return add_without_sets (sched,
1217                            delay,
1218                            fd,
1219                            -1,
1220                            task,
1221                            task_cls);
1222
1223 #endif
1224 }
1225
1226
1227 /**
1228  * Schedule a new task to be run with a specified delay or when the
1229  * specified file descriptor is ready for writing.  The delay can be
1230  * used as a timeout on the socket being ready.  The task will be
1231  * scheduled for execution once either the delay has expired or the
1232  * socket operation is ready. It will be run with the priority of
1233  * the calling task.
1234  *
1235  * @param sched scheduler to use
1236  * @param delay when should this operation time out? Use 
1237  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1238  * @param wfd write file-descriptor
1239  * @param task main function of the task
1240  * @param task_cls closure of task
1241  * @return unique task identifier for the job
1242  *         only valid until "task" is started!
1243  */
1244 GNUNET_SCHEDULER_TaskIdentifier
1245 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1246                                  struct GNUNET_TIME_Relative delay,
1247                                  const struct GNUNET_DISK_FileHandle * wfd,
1248                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1249 {
1250 #if MINGW
1251   struct GNUNET_NETWORK_FDSet *ws;
1252   GNUNET_SCHEDULER_TaskIdentifier ret;
1253
1254   GNUNET_assert (wfd != NULL);
1255   ws = GNUNET_NETWORK_fdset_create ();
1256   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1257   ret = GNUNET_SCHEDULER_add_select (sched,
1258                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1259                                      GNUNET_SCHEDULER_NO_TASK,
1260                                      delay, NULL, ws, task, task_cls);
1261   GNUNET_NETWORK_fdset_destroy (ws);
1262   return ret;
1263 #else
1264   int fd;
1265
1266   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1267   return add_without_sets (sched,
1268                            delay,
1269                            -1,
1270                            fd,
1271                            task,
1272                            task_cls);
1273
1274 #endif
1275 }
1276
1277
1278
1279 /**
1280  * Schedule a new task to be run with a specified delay or when any of
1281  * the specified file descriptor sets is ready.  The delay can be used
1282  * as a timeout on the socket(s) being ready.  The task will be
1283  * scheduled for execution once either the delay has expired or any of
1284  * the socket operations is ready.  This is the most general
1285  * function of the "add" family.  Note that the "prerequisite_task"
1286  * must be satisfied in addition to any of the other conditions.  In
1287  * other words, the task will be started when
1288  * <code>
1289  * (prerequisite-run)
1290  * && (delay-ready
1291  *     || any-rs-ready
1292  *     || any-ws-ready
1293  *     || (shutdown-active && run-on-shutdown) )
1294  * </code>
1295  *
1296  * @param sched scheduler to use
1297  * @param prio how important is this task?
1298  * @param prerequisite_task run this task after the task with the given
1299  *        task identifier completes (and any of our other
1300  *        conditions, such as delay, read or write-readiness
1301  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1302  *        on completion of other tasks.
1303  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1304  *        which means that the task will only be run after we receive SIGTERM
1305  * @param rs set of file descriptors we want to read (can be NULL)
1306  * @param ws set of file descriptors we want to write (can be NULL)
1307  * @param task main function of the task
1308  * @param task_cls closure of task
1309  * @return unique task identifier for the job
1310  *         only valid until "task" is started!
1311  */
1312 GNUNET_SCHEDULER_TaskIdentifier
1313 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1314                              enum GNUNET_SCHEDULER_Priority prio,
1315                              GNUNET_SCHEDULER_TaskIdentifier
1316                              prerequisite_task,
1317                              struct GNUNET_TIME_Relative delay,
1318                              const struct GNUNET_NETWORK_FDSet * rs,
1319                              const struct GNUNET_NETWORK_FDSet * ws,
1320                              GNUNET_SCHEDULER_Task task, void *task_cls)
1321 {
1322   struct Task *t;
1323 #if EXECINFO
1324   void *backtrace_array[MAX_TRACE_DEPTH];
1325 #endif
1326
1327   GNUNET_assert (NULL != task);
1328   t = GNUNET_malloc (sizeof (struct Task));
1329   t->callback = task;
1330   t->callback_cls = task_cls;
1331 #if EXECINFO
1332   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1333   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1334 #endif
1335   t->read_fd = -1;
1336   t->write_fd = -1;
1337   if (rs != NULL)
1338     {
1339       t->read_set = GNUNET_NETWORK_fdset_create ();
1340       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1341     }
1342   if (ws != NULL)
1343     {
1344       t->write_set = GNUNET_NETWORK_fdset_create ();
1345       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1346     }
1347   t->id = ++sched->last_id;
1348 #if PROFILE_DELAYS
1349   t->start_time = GNUNET_TIME_absolute_get ();
1350 #endif
1351   t->prereq_id = prerequisite_task;
1352   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1353   t->priority =
1354     check_priority ((prio ==
1355                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1356                     : prio);
1357   t->next = sched->pending; 
1358   sched->pending = t;
1359   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1360                                           t->priority);
1361 #if DEBUG_TASKS
1362   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1363               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1364 #endif
1365 #if EXECINFO
1366   int i;
1367
1368   for (i=0;i<t->num_backtrace_strings;i++)
1369       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1370                   "Task %u trace %d: %s\n",
1371                   t->id,
1372                   i,
1373                   t->backtrace_strings[i]);
1374 #endif
1375   return t->id;
1376 }
1377
1378 /* end of scheduler.c */