(no commit message)
[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 *sigpipe;
625
626 /**
627  * Signal handler called for sigpipe.
628  *
629  * FIXME: what should we do here?
630  */
631 static void
632 sighandler_pipe ()
633 {
634   return;
635 }
636
637 /**
638  * Signal handler called for signals that should cause us to shutdown.
639  */
640 static void
641 sighandler_shutdown ()
642 {
643   static char c;
644
645   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
646                           (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
647                           sizeof (c));
648 }
649
650
651 /**
652  * Initialize and run scheduler.  This function will return when all
653  * tasks have completed.  On systems with signals, receiving a SIGTERM
654  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
655  * to be run after the active task is complete.  As a result, SIGTERM
656  * causes all active tasks to be scheduled with reason
657  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
658  * afterwards will execute normally!). Note that any particular signal
659  * will only shut down one scheduler; applications should always only
660  * create a single scheduler.
661  *
662  * @param task task to run immediately
663  * @param task_cls closure of task
664  */
665 void
666 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
667 {
668   struct GNUNET_SCHEDULER_Handle sched;
669   struct GNUNET_NETWORK_FDSet *rs;
670   struct GNUNET_NETWORK_FDSet *ws;
671   struct GNUNET_TIME_Relative timeout;
672   int ret;
673   struct GNUNET_SIGNAL_Context *shc_int;
674   struct GNUNET_SIGNAL_Context *shc_term;
675   struct GNUNET_SIGNAL_Context *shc_quit;
676   struct GNUNET_SIGNAL_Context *shc_hup;
677   struct GNUNET_SIGNAL_Context *shc_pipe;
678   unsigned long long last_tr;
679   unsigned int busy_wait_warning;
680   const struct GNUNET_DISK_FileHandle *pr;
681   char c;
682
683   rs = GNUNET_NETWORK_fdset_create ();
684   ws = GNUNET_NETWORK_fdset_create ();
685   GNUNET_assert (sigpipe == NULL);
686   sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
687   GNUNET_assert (sigpipe != NULL);
688   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
689   GNUNET_assert (pr != NULL);
690   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
691   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
692   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
693 #ifndef MINGW
694   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
695   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
696 #endif
697   memset (&sched, 0, sizeof (sched));
698   sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
699   GNUNET_SCHEDULER_add_continuation (&sched,
700                                      task,
701                                      task_cls,
702                                      GNUNET_SCHEDULER_REASON_STARTUP);
703   last_tr = 0;
704   busy_wait_warning = 0;
705   while ((sched.pending != NULL) || (sched.ready_count > 0))
706     {
707       GNUNET_NETWORK_fdset_zero (rs);
708       GNUNET_NETWORK_fdset_zero (ws);
709       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
710       update_sets (&sched, rs, ws, &timeout);
711       GNUNET_NETWORK_fdset_handle_set (rs, pr);
712       if (sched.ready_count > 0)
713         {
714           /* no blocking, more work already ready! */
715           timeout = GNUNET_TIME_UNIT_ZERO;
716         }
717       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
718       if (ret == GNUNET_SYSERR)
719         {
720           if (errno == EINTR)
721             continue;
722
723           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
724 #ifndef MINGW
725 #if USE_LSOF
726           char lsof[512];
727           snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
728           close (1);
729           dup2 (2, 1);
730           system (lsof);                  
731 #endif
732 #endif
733           abort ();
734           break;
735         }
736       if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
737         {
738           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
739                       _("Looks like we're busy waiting...\n"));
740           sleep (1);            /* mitigate */
741         }
742       check_ready (&sched, rs, ws);
743       run_ready (&sched, rs, ws);
744       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
745         {
746           /* consume the signal */
747           GNUNET_DISK_file_read (pr, &c, sizeof (c));
748           /* mark all active tasks as ready due to shutdown */
749           GNUNET_SCHEDULER_shutdown (&sched);
750         }
751       if (last_tr == sched.tasks_run)
752         {
753           busy_wait_warning++;
754         }
755       else
756         {
757           last_tr = sched.tasks_run;
758           busy_wait_warning = 0;
759         }
760     }
761   GNUNET_SIGNAL_handler_uninstall (shc_int);
762   GNUNET_SIGNAL_handler_uninstall (shc_term);
763 #ifndef MINGW
764   GNUNET_SIGNAL_handler_uninstall (shc_quit);
765   GNUNET_SIGNAL_handler_uninstall (shc_hup);
766 #endif
767   GNUNET_DISK_pipe_close (sigpipe);
768   sigpipe = NULL;
769   GNUNET_NETWORK_fdset_destroy (rs);
770   GNUNET_NETWORK_fdset_destroy (ws);
771 }
772
773
774 /**
775  * Obtain the reason code for why the current task was
776  * started.  Will return the same value as 
777  * the GNUNET_SCHEDULER_TaskContext's reason field.
778  *
779  * @param sched scheduler to query
780  * @return reason(s) why the current task is run
781  */
782 enum GNUNET_SCHEDULER_Reason
783 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
784 {
785   return sched->active_task->reason;
786 }
787
788
789 /**
790  * Get information about the current load of this scheduler.  Use this
791  * function to determine if an elective task should be added or simply
792  * dropped (if the decision should be made based on the number of
793  * tasks ready to run).
794  *
795  * @param sched scheduler to query
796  * @param p priority level to look at
797  * @return number of tasks pending right now
798  */
799 unsigned int
800 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
801                            enum GNUNET_SCHEDULER_Priority p)
802 {
803   struct Task *pos;
804   unsigned int ret;
805
806   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
807     return sched->ready_count;
808   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
809     p = sched->current_priority;
810   ret = 0;
811   pos = sched->ready[p];
812   while (pos != NULL)
813     {
814       pos = pos->next;
815       ret++;
816     }
817   return ret;
818 }
819
820
821 /**
822  * Cancel the task with the specified identifier.
823  * The task must not yet have run.
824  *
825  * @param sched scheduler to use
826  * @param task id of the task to cancel
827  * @return original closure of the task
828  */
829 void *
830 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
831                          GNUNET_SCHEDULER_TaskIdentifier task)
832 {
833   struct Task *t;
834   struct Task *prev;
835   enum GNUNET_SCHEDULER_Priority p;
836   void *ret;
837
838   prev = NULL;
839   t = sched->pending;
840   while (t != NULL)
841     {
842       if (t->id == task)
843         break;
844       prev = t;
845       t = t->next;
846     }
847   p = 0;
848   while (t == NULL)
849     {
850       p++;
851       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
852       prev = NULL;
853       t = sched->ready[p];
854       while (t != NULL)
855         {
856           if (t->id == task)
857             {
858               sched->ready_count--;
859               break;
860             }
861           prev = t;
862           t = t->next;
863         }
864     }
865   if (prev == NULL)
866     {
867       if (p == 0)
868         sched->pending = t->next;
869       else
870         sched->ready[p] = t->next;
871     }
872   else
873     prev->next = t->next;
874   ret = t->callback_cls;
875 #if DEBUG_TASKS
876   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
877               "Canceling task: %llu / %p\n", task, t->callback_cls);
878 #endif
879   destroy_task (t);
880   return ret;
881 }
882
883
884 /**
885  * Continue the current execution with the given function.  This is
886  * similar to the other "add" functions except that there is no delay
887  * and the reason code can be specified.
888  *
889  * @param sched scheduler to use
890  * @param task main function of the task
891  * @param task_cls closure for 'main'
892  * @param reason reason for task invocation
893  */
894 void
895 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
896                                    GNUNET_SCHEDULER_Task task,
897                                    void *task_cls,
898                                    enum GNUNET_SCHEDULER_Reason reason)
899 {
900   struct Task *t;
901 #if EXECINFO
902   void *backtrace_array[50];
903 #endif
904   t = GNUNET_malloc (sizeof (struct Task));
905 #if EXECINFO
906   t->num_backtrace_strings = backtrace(backtrace_array, 50);
907   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
908 #endif
909   t->read_fd = -1;
910   t->write_fd = -1;
911   t->callback = task;
912   t->callback_cls = task_cls;
913   t->id = ++sched->last_id;
914 #if PROFILE_DELAYS
915   t->start_time = GNUNET_TIME_absolute_get ();
916 #endif
917   t->reason = reason;
918   t->priority = sched->current_priority;
919 #if DEBUG_TASKS
920   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
921               "Adding continuation task: %llu / %p\n",
922               t->id, t->callback_cls);
923 #endif
924   queue_ready_task (sched, t);
925 }
926
927
928
929 /**
930  * Schedule a new task to be run after the specified prerequisite task
931  * has completed. It will be run with the priority of the calling
932  * task.
933  *
934  * @param sched scheduler to use
935  * @param prerequisite_task run this task after the task with the given
936  *        task identifier completes (and any of our other
937  *        conditions, such as delay, read or write-readiness
938  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
939  *        on completion of other tasks (this will cause the task to run as
940  *        soon as possible).
941  * @param task main function of the task
942  * @param task_cls closure of task
943  * @return unique task identifier for the job
944  *         only valid until "task" is started!
945  */
946 GNUNET_SCHEDULER_TaskIdentifier
947 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
948                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
949                             GNUNET_SCHEDULER_Task task, void *task_cls)
950 {
951   return GNUNET_SCHEDULER_add_select (sched,
952                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
953                                       prerequisite_task,
954                                       GNUNET_TIME_UNIT_ZERO,
955                                       NULL, NULL, task, task_cls);
956 }
957
958
959 /**
960  * Schedule a new task to be run with a specified priority.
961  *
962  * @param sched scheduler to use
963  * @param prio how important is the new task?
964  * @param task main function of the task
965  * @param task_cls closure of task
966  * @return unique task identifier for the job
967  *         only valid until "task" is started!
968  */
969 GNUNET_SCHEDULER_TaskIdentifier
970 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
971                                     enum GNUNET_SCHEDULER_Priority prio,
972                                     GNUNET_SCHEDULER_Task task,
973                                     void *task_cls)
974 {
975   return GNUNET_SCHEDULER_add_select (sched,
976                                       prio,
977                                       GNUNET_SCHEDULER_NO_TASK,
978                                       GNUNET_TIME_UNIT_ZERO,
979                                       NULL, NULL, task, task_cls);
980 }
981
982
983
984 /**
985  * Schedule a new task to be run with a specified delay.  The task
986  * will be scheduled for execution once the delay has expired. It
987  * will be run with the priority of the calling task.
988  *
989  * @param sched scheduler to use
990  * @param delay when should this operation time out? Use 
991  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
992  * @param task main function of the task
993  * @param task_cls closure of task
994  * @return unique task identifier for the job
995  *         only valid until "task" is started!
996  */
997 GNUNET_SCHEDULER_TaskIdentifier
998 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
999                               struct GNUNET_TIME_Relative delay,
1000                               GNUNET_SCHEDULER_Task task, void *task_cls)
1001 {
1002   return GNUNET_SCHEDULER_add_select (sched,
1003                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1004                                       GNUNET_SCHEDULER_NO_TASK, delay,
1005                                       NULL, NULL, task, task_cls);
1006 }
1007
1008
1009
1010 /**
1011  * Schedule a new task to be run as soon as possible. The task
1012  * will be run with the priority of the calling task.
1013  *
1014  * @param sched scheduler to use
1015  * @param task main function of the task
1016  * @param task_cls closure of task
1017  * @return unique task identifier for the job
1018  *         only valid until "task" is started!
1019  */
1020 GNUNET_SCHEDULER_TaskIdentifier
1021 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
1022                           GNUNET_SCHEDULER_Task task,
1023                           void *task_cls)
1024 {
1025   return GNUNET_SCHEDULER_add_select (sched,
1026                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1027                                       GNUNET_SCHEDULER_NO_TASK,
1028                                       GNUNET_TIME_UNIT_ZERO,
1029                                       NULL, NULL, task, task_cls);
1030 }
1031
1032
1033
1034
1035 /**
1036  * Schedule a new task to be run with a specified delay or when any of
1037  * the specified file descriptor sets is ready.  The delay can be used
1038  * as a timeout on the socket(s) being ready.  The task will be
1039  * scheduled for execution once either the delay has expired or any of
1040  * the socket operations is ready.  This is the most general
1041  * function of the "add" family.  Note that the "prerequisite_task"
1042  * must be satisfied in addition to any of the other conditions.  In
1043  * other words, the task will be started when
1044  * <code>
1045  * (prerequisite-run)
1046  * && (delay-ready
1047  *     || any-rs-ready
1048  *     || any-ws-ready
1049  *     || (shutdown-active && run-on-shutdown) )
1050  * </code>
1051  *
1052  * @param sched scheduler to use
1053  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1054  *        which means that the task will only be run after we receive SIGTERM
1055  * @param rfd file descriptor we want to read (can be -1)
1056  * @param wfd file descriptors we want to write (can be -1)
1057  * @param task main function of the task
1058  * @param task_cls closure of task
1059  * @return unique task identifier for the job
1060  *         only valid until "task" is started!
1061  */
1062 GNUNET_SCHEDULER_TaskIdentifier
1063 add_without_sets (struct GNUNET_SCHEDULER_Handle * sched,
1064                   struct GNUNET_TIME_Relative delay,
1065                   int rfd,
1066                   int wfd,
1067                   GNUNET_SCHEDULER_Task task, void *task_cls)
1068 {
1069   struct Task *t;
1070 #if EXECINFO
1071   void *backtrace_array[MAX_TRACE_DEPTH];
1072 #endif
1073
1074   GNUNET_assert (NULL != task);
1075   t = GNUNET_malloc (sizeof (struct Task));
1076   t->callback = task;
1077   t->callback_cls = task_cls;
1078 #if EXECINFO
1079   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1080   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1081 #endif
1082   t->read_fd = rfd;
1083   t->write_fd = wfd;
1084   t->id = ++sched->last_id;
1085 #if PROFILE_DELAYS
1086   t->start_time = GNUNET_TIME_absolute_get ();
1087 #endif
1088   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1089   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1090   t->priority = check_priority (sched->current_priority);
1091   t->next = sched->pending;
1092   sched->pending = t;
1093   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1094                                           t->priority);
1095 #if DEBUG_TASKS
1096   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1097               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1098 #endif
1099 #if EXECINFO
1100   int i;
1101
1102   for (i=0;i<t->num_backtrace_strings;i++)
1103       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1104                   "Task %u trace %d: %s\n",
1105                   t->id,
1106                   i,
1107                   t->backtrace_strings[i]);
1108 #endif
1109   return t->id;
1110 }
1111
1112
1113
1114 /**
1115  * Schedule a new task to be run with a specified delay or when the
1116  * specified file descriptor is ready for reading.  The delay can be
1117  * used as a timeout on the socket being ready.  The task will be
1118  * scheduled for execution once either the delay has expired or the
1119  * socket operation is ready.  It will be run with the priority of
1120  * the calling task.
1121  *
1122  * @param sched scheduler to use
1123  * @param delay when should this operation time out? Use 
1124  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1125  * @param rfd read file-descriptor
1126  * @param task main function of the task
1127  * @param task_cls closure of task
1128  * @return unique task identifier for the job
1129  *         only valid until "task" is started!
1130  */
1131 GNUNET_SCHEDULER_TaskIdentifier
1132 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
1133                                struct GNUNET_TIME_Relative delay,
1134                                struct GNUNET_NETWORK_Handle * rfd,
1135                                GNUNET_SCHEDULER_Task task, void *task_cls)
1136 {
1137   return add_without_sets (sched,
1138                            delay,
1139                            GNUNET_NETWORK_get_fd (rfd),
1140                            -1,
1141                            task,
1142                            task_cls);
1143 }
1144
1145
1146 /**
1147  * Schedule a new task to be run with a specified delay or when the
1148  * specified file descriptor is ready for writing.  The delay can be
1149  * used as a timeout on the socket being ready.  The task will be
1150  * scheduled for execution once either the delay has expired or the
1151  * socket operation is ready.  It will be run with the priority of
1152  * the calling task.
1153  *
1154  * @param sched scheduler to use
1155  * @param delay when should this operation time out? Use 
1156  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1157  * @param wfd write file-descriptor
1158  * @param task main function of the task
1159  * @param task_cls closure of task
1160  * @return unique task identifier for the job
1161  *         only valid until "task" is started!
1162  */
1163 GNUNET_SCHEDULER_TaskIdentifier
1164 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
1165                                 struct GNUNET_TIME_Relative delay,
1166                                 struct GNUNET_NETWORK_Handle * wfd,
1167                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1168 {
1169   return add_without_sets (sched,
1170                            delay,
1171                            -1,
1172                            GNUNET_NETWORK_get_fd (wfd),
1173                            task,
1174                            task_cls);
1175 }
1176
1177
1178 /**
1179  * Schedule a new task to be run with a specified delay or when the
1180  * specified file descriptor is ready for reading.  The delay can be
1181  * used as a timeout on the socket being ready.  The task will be
1182  * scheduled for execution once either the delay has expired or the
1183  * socket operation is ready. It will be run with the priority of
1184  * the calling task.
1185  *
1186  * @param sched scheduler to use
1187  * @param delay when should this operation time out? Use 
1188  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1189  * @param rfd read file-descriptor
1190  * @param task main function of the task
1191  * @param task_cls closure of task
1192  * @return unique task identifier for the job
1193  *         only valid until "task" is started!
1194  */
1195 GNUNET_SCHEDULER_TaskIdentifier
1196 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1197                                 struct GNUNET_TIME_Relative delay,
1198                                 const struct GNUNET_DISK_FileHandle * rfd,
1199                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1200 {
1201 #if MINGW
1202   struct GNUNET_NETWORK_FDSet *rs;
1203   GNUNET_SCHEDULER_TaskIdentifier ret;
1204
1205   GNUNET_assert (rfd != NULL);
1206   rs = GNUNET_NETWORK_fdset_create ();
1207   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1208   ret = GNUNET_SCHEDULER_add_select (sched,
1209                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1210                                      GNUNET_SCHEDULER_NO_TASK, delay,
1211                                      rs, NULL, task, task_cls);
1212   GNUNET_NETWORK_fdset_destroy (rs);
1213   return ret;
1214 #else
1215   int fd;
1216
1217   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1218   return add_without_sets (sched,
1219                            delay,
1220                            fd,
1221                            -1,
1222                            task,
1223                            task_cls);
1224
1225 #endif
1226 }
1227
1228
1229 /**
1230  * Schedule a new task to be run with a specified delay or when the
1231  * specified file descriptor is ready for writing.  The delay can be
1232  * used as a timeout on the socket being ready.  The task will be
1233  * scheduled for execution once either the delay has expired or the
1234  * socket operation is ready. It will be run with the priority of
1235  * the calling task.
1236  *
1237  * @param sched scheduler to use
1238  * @param delay when should this operation time out? Use 
1239  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1240  * @param wfd write file-descriptor
1241  * @param task main function of the task
1242  * @param task_cls closure of task
1243  * @return unique task identifier for the job
1244  *         only valid until "task" is started!
1245  */
1246 GNUNET_SCHEDULER_TaskIdentifier
1247 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1248                                  struct GNUNET_TIME_Relative delay,
1249                                  const struct GNUNET_DISK_FileHandle * wfd,
1250                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1251 {
1252 #if MINGW
1253   struct GNUNET_NETWORK_FDSet *ws;
1254   GNUNET_SCHEDULER_TaskIdentifier ret;
1255
1256   GNUNET_assert (wfd != NULL);
1257   ws = GNUNET_NETWORK_fdset_create ();
1258   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1259   ret = GNUNET_SCHEDULER_add_select (sched,
1260                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1261                                      GNUNET_SCHEDULER_NO_TASK,
1262                                      delay, NULL, ws, task, task_cls);
1263   GNUNET_NETWORK_fdset_destroy (ws);
1264   return ret;
1265 #else
1266   int fd;
1267
1268   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1269   return add_without_sets (sched,
1270                            delay,
1271                            -1,
1272                            fd,
1273                            task,
1274                            task_cls);
1275
1276 #endif
1277 }
1278
1279
1280
1281 /**
1282  * Schedule a new task to be run with a specified delay or when any of
1283  * the specified file descriptor sets is ready.  The delay can be used
1284  * as a timeout on the socket(s) being ready.  The task will be
1285  * scheduled for execution once either the delay has expired or any of
1286  * the socket operations is ready.  This is the most general
1287  * function of the "add" family.  Note that the "prerequisite_task"
1288  * must be satisfied in addition to any of the other conditions.  In
1289  * other words, the task will be started when
1290  * <code>
1291  * (prerequisite-run)
1292  * && (delay-ready
1293  *     || any-rs-ready
1294  *     || any-ws-ready
1295  *     || (shutdown-active && run-on-shutdown) )
1296  * </code>
1297  *
1298  * @param sched scheduler to use
1299  * @param prio how important is this task?
1300  * @param prerequisite_task run this task after the task with the given
1301  *        task identifier completes (and any of our other
1302  *        conditions, such as delay, read or write-readiness
1303  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1304  *        on completion of other tasks.
1305  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1306  *        which means that the task will only be run after we receive SIGTERM
1307  * @param rs set of file descriptors we want to read (can be NULL)
1308  * @param ws set of file descriptors we want to write (can be NULL)
1309  * @param task main function of the task
1310  * @param task_cls closure of task
1311  * @return unique task identifier for the job
1312  *         only valid until "task" is started!
1313  */
1314 GNUNET_SCHEDULER_TaskIdentifier
1315 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1316                              enum GNUNET_SCHEDULER_Priority prio,
1317                              GNUNET_SCHEDULER_TaskIdentifier
1318                              prerequisite_task,
1319                              struct GNUNET_TIME_Relative delay,
1320                              const struct GNUNET_NETWORK_FDSet * rs,
1321                              const struct GNUNET_NETWORK_FDSet * ws,
1322                              GNUNET_SCHEDULER_Task task, void *task_cls)
1323 {
1324   struct Task *t;
1325 #if EXECINFO
1326   void *backtrace_array[MAX_TRACE_DEPTH];
1327 #endif
1328
1329   GNUNET_assert (NULL != task);
1330   t = GNUNET_malloc (sizeof (struct Task));
1331   t->callback = task;
1332   t->callback_cls = task_cls;
1333 #if EXECINFO
1334   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1335   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1336 #endif
1337   t->read_fd = -1;
1338   t->write_fd = -1;
1339   if (rs != NULL)
1340     {
1341       t->read_set = GNUNET_NETWORK_fdset_create ();
1342       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1343     }
1344   if (ws != NULL)
1345     {
1346       t->write_set = GNUNET_NETWORK_fdset_create ();
1347       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1348     }
1349   t->id = ++sched->last_id;
1350 #if PROFILE_DELAYS
1351   t->start_time = GNUNET_TIME_absolute_get ();
1352 #endif
1353   t->prereq_id = prerequisite_task;
1354   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1355   t->priority =
1356     check_priority ((prio ==
1357                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1358                     : prio);
1359   t->next = sched->pending; 
1360   sched->pending = t;
1361   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1362                                           t->priority);
1363 #if DEBUG_TASKS
1364   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1365               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1366 #endif
1367 #if EXECINFO
1368   int i;
1369
1370   for (i=0;i<t->num_backtrace_strings;i++)
1371       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1372                   "Task %u trace %d: %s\n",
1373                   t->id,
1374                   i,
1375                   t->backtrace_strings[i]);
1376 #endif
1377   return t->id;
1378 }
1379
1380 /* end of scheduler.c */