function coverage 100%
[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_pipe);
760   GNUNET_SIGNAL_handler_uninstall (shc_int);
761   GNUNET_SIGNAL_handler_uninstall (shc_term);
762 #ifndef MINGW
763   GNUNET_SIGNAL_handler_uninstall (shc_quit);
764   GNUNET_SIGNAL_handler_uninstall (shc_hup);
765 #endif
766   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
767   shutdown_pipe_handle = NULL;
768   GNUNET_NETWORK_fdset_destroy (rs);
769   GNUNET_NETWORK_fdset_destroy (ws);
770 }
771
772
773 /**
774  * Obtain the reason code for why the current task was
775  * started.  Will return the same value as 
776  * the GNUNET_SCHEDULER_TaskContext's reason field.
777  *
778  * @param sched scheduler to query
779  * @return reason(s) why the current task is run
780  */
781 enum GNUNET_SCHEDULER_Reason
782 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
783 {
784   return sched->active_task->reason;
785 }
786
787
788 /**
789  * Get information about the current load of this scheduler.  Use this
790  * function to determine if an elective task should be added or simply
791  * dropped (if the decision should be made based on the number of
792  * tasks ready to run).
793  *
794  * @param sched scheduler to query
795  * @param p priority level to look at
796  * @return number of tasks pending right now
797  */
798 unsigned int
799 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
800                            enum GNUNET_SCHEDULER_Priority p)
801 {
802   struct Task *pos;
803   unsigned int ret;
804
805   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
806     return sched->ready_count;
807   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
808     p = sched->current_priority;
809   ret = 0;
810   pos = sched->ready[p];
811   while (pos != NULL)
812     {
813       pos = pos->next;
814       ret++;
815     }
816   return ret;
817 }
818
819
820 /**
821  * Cancel the task with the specified identifier.
822  * The task must not yet have run.
823  *
824  * @param sched scheduler to use
825  * @param task id of the task to cancel
826  * @return original closure of the task
827  */
828 void *
829 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
830                          GNUNET_SCHEDULER_TaskIdentifier task)
831 {
832   struct Task *t;
833   struct Task *prev;
834   enum GNUNET_SCHEDULER_Priority p;
835   void *ret;
836
837   prev = NULL;
838   t = sched->pending;
839   while (t != NULL)
840     {
841       if (t->id == task)
842         break;
843       prev = t;
844       t = t->next;
845     }
846   p = 0;
847   while (t == NULL)
848     {
849       p++;
850       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
851       prev = NULL;
852       t = sched->ready[p];
853       while (t != NULL)
854         {
855           if (t->id == task)
856             {
857               sched->ready_count--;
858               break;
859             }
860           prev = t;
861           t = t->next;
862         }
863     }
864   if (prev == NULL)
865     {
866       if (p == 0)
867         sched->pending = t->next;
868       else
869         sched->ready[p] = t->next;
870     }
871   else
872     prev->next = t->next;
873   ret = t->callback_cls;
874 #if DEBUG_TASKS
875   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
876               "Canceling task: %llu / %p\n", task, t->callback_cls);
877 #endif
878   destroy_task (t);
879   return ret;
880 }
881
882
883 /**
884  * Continue the current execution with the given function.  This is
885  * similar to the other "add" functions except that there is no delay
886  * and the reason code can be specified.
887  *
888  * @param sched scheduler to use
889  * @param task main function of the task
890  * @param task_cls closure for 'main'
891  * @param reason reason for task invocation
892  */
893 void
894 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
895                                    GNUNET_SCHEDULER_Task task,
896                                    void *task_cls,
897                                    enum GNUNET_SCHEDULER_Reason reason)
898 {
899   struct Task *t;
900 #if EXECINFO
901   void *backtrace_array[50];
902 #endif
903   t = GNUNET_malloc (sizeof (struct Task));
904 #if EXECINFO
905   t->num_backtrace_strings = backtrace(backtrace_array, 50);
906   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
907 #endif
908   t->read_fd = -1;
909   t->write_fd = -1;
910   t->callback = task;
911   t->callback_cls = task_cls;
912   t->id = ++sched->last_id;
913 #if PROFILE_DELAYS
914   t->start_time = GNUNET_TIME_absolute_get ();
915 #endif
916   t->reason = reason;
917   t->priority = sched->current_priority;
918 #if DEBUG_TASKS
919   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920               "Adding continuation task: %llu / %p\n",
921               t->id, t->callback_cls);
922 #endif
923   queue_ready_task (sched, t);
924 }
925
926
927
928 /**
929  * Schedule a new task to be run after the specified prerequisite task
930  * has completed. It will be run with the priority of the calling
931  * task.
932  *
933  * @param sched scheduler to use
934  * @param prerequisite_task run this task after the task with the given
935  *        task identifier completes (and any of our other
936  *        conditions, such as delay, read or write-readiness
937  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
938  *        on completion of other tasks (this will cause the task to run as
939  *        soon as possible).
940  * @param task main function of the task
941  * @param task_cls closure of task
942  * @return unique task identifier for the job
943  *         only valid until "task" is started!
944  */
945 GNUNET_SCHEDULER_TaskIdentifier
946 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
947                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
948                             GNUNET_SCHEDULER_Task task, void *task_cls)
949 {
950   return GNUNET_SCHEDULER_add_select (sched,
951                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
952                                       prerequisite_task,
953                                       GNUNET_TIME_UNIT_ZERO,
954                                       NULL, NULL, task, task_cls);
955 }
956
957
958 /**
959  * Schedule a new task to be run with a specified priority.
960  *
961  * @param sched scheduler to use
962  * @param prio how important is the new task?
963  * @param task main function of the task
964  * @param task_cls closure of task
965  * @return unique task identifier for the job
966  *         only valid until "task" is started!
967  */
968 GNUNET_SCHEDULER_TaskIdentifier
969 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
970                                     enum GNUNET_SCHEDULER_Priority prio,
971                                     GNUNET_SCHEDULER_Task task,
972                                     void *task_cls)
973 {
974   return GNUNET_SCHEDULER_add_select (sched,
975                                       prio,
976                                       GNUNET_SCHEDULER_NO_TASK,
977                                       GNUNET_TIME_UNIT_ZERO,
978                                       NULL, NULL, task, task_cls);
979 }
980
981
982
983 /**
984  * Schedule a new task to be run with a specified delay.  The task
985  * will be scheduled for execution once the delay has expired. It
986  * will be run with the priority of the calling task.
987  *
988  * @param sched scheduler to use
989  * @param delay when should this operation time out? Use 
990  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
991  * @param task main function of the task
992  * @param task_cls closure of task
993  * @return unique task identifier for the job
994  *         only valid until "task" is started!
995  */
996 GNUNET_SCHEDULER_TaskIdentifier
997 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
998                               struct GNUNET_TIME_Relative delay,
999                               GNUNET_SCHEDULER_Task task, void *task_cls)
1000 {
1001   return GNUNET_SCHEDULER_add_select (sched,
1002                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1003                                       GNUNET_SCHEDULER_NO_TASK, delay,
1004                                       NULL, NULL, task, task_cls);
1005 }
1006
1007
1008
1009 /**
1010  * Schedule a new task to be run as soon as possible. The task
1011  * will be run with the priority of the calling task.
1012  *
1013  * @param sched scheduler to use
1014  * @param task main function of the task
1015  * @param task_cls closure of task
1016  * @return unique task identifier for the job
1017  *         only valid until "task" is started!
1018  */
1019 GNUNET_SCHEDULER_TaskIdentifier
1020 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
1021                           GNUNET_SCHEDULER_Task task,
1022                           void *task_cls)
1023 {
1024   return GNUNET_SCHEDULER_add_select (sched,
1025                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1026                                       GNUNET_SCHEDULER_NO_TASK,
1027                                       GNUNET_TIME_UNIT_ZERO,
1028                                       NULL, NULL, task, task_cls);
1029 }
1030
1031
1032
1033
1034 /**
1035  * Schedule a new task to be run with a specified delay or when any of
1036  * the specified file descriptor sets is ready.  The delay can be used
1037  * as a timeout on the socket(s) being ready.  The task will be
1038  * scheduled for execution once either the delay has expired or any of
1039  * the socket operations is ready.  This is the most general
1040  * function of the "add" family.  Note that the "prerequisite_task"
1041  * must be satisfied in addition to any of the other conditions.  In
1042  * other words, the task will be started when
1043  * <code>
1044  * (prerequisite-run)
1045  * && (delay-ready
1046  *     || any-rs-ready
1047  *     || any-ws-ready
1048  *     || (shutdown-active && run-on-shutdown) )
1049  * </code>
1050  *
1051  * @param sched scheduler to use
1052  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1053  *        which means that the task will only be run after we receive SIGTERM
1054  * @param rfd file descriptor we want to read (can be -1)
1055  * @param wfd file descriptors we want to write (can be -1)
1056  * @param task main function of the task
1057  * @param task_cls closure of task
1058  * @return unique task identifier for the job
1059  *         only valid until "task" is started!
1060  */
1061 GNUNET_SCHEDULER_TaskIdentifier
1062 add_without_sets (struct GNUNET_SCHEDULER_Handle * sched,
1063                   struct GNUNET_TIME_Relative delay,
1064                   int rfd,
1065                   int wfd,
1066                   GNUNET_SCHEDULER_Task task, void *task_cls)
1067 {
1068   struct Task *t;
1069 #if EXECINFO
1070   void *backtrace_array[MAX_TRACE_DEPTH];
1071 #endif
1072
1073   GNUNET_assert (NULL != task);
1074   t = GNUNET_malloc (sizeof (struct Task));
1075   t->callback = task;
1076   t->callback_cls = task_cls;
1077 #if EXECINFO
1078   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1079   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1080 #endif
1081   t->read_fd = rfd;
1082   t->write_fd = wfd;
1083   t->id = ++sched->last_id;
1084 #if PROFILE_DELAYS
1085   t->start_time = GNUNET_TIME_absolute_get ();
1086 #endif
1087   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1088   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1089   t->priority = check_priority (sched->current_priority);
1090   t->next = sched->pending;
1091   sched->pending = t;
1092   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1093                                           t->priority);
1094 #if DEBUG_TASKS
1095   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1097 #endif
1098 #if EXECINFO
1099   int i;
1100
1101   for (i=0;i<t->num_backtrace_strings;i++)
1102       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1103                   "Task %u trace %d: %s\n",
1104                   t->id,
1105                   i,
1106                   t->backtrace_strings[i]);
1107 #endif
1108   return t->id;
1109 }
1110
1111
1112
1113 /**
1114  * Schedule a new task to be run with a specified delay or when the
1115  * specified file descriptor is ready for reading.  The delay can be
1116  * used as a timeout on the socket being ready.  The task will be
1117  * scheduled for execution once either the delay has expired or the
1118  * socket operation is ready.  It will be run with the priority of
1119  * the calling task.
1120  *
1121  * @param sched scheduler to use
1122  * @param delay when should this operation time out? Use 
1123  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1124  * @param rfd read file-descriptor
1125  * @param task main function of the task
1126  * @param task_cls closure of task
1127  * @return unique task identifier for the job
1128  *         only valid until "task" is started!
1129  */
1130 GNUNET_SCHEDULER_TaskIdentifier
1131 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
1132                                struct GNUNET_TIME_Relative delay,
1133                                struct GNUNET_NETWORK_Handle * rfd,
1134                                GNUNET_SCHEDULER_Task task, void *task_cls)
1135 {
1136   return add_without_sets (sched,
1137                            delay,
1138                            GNUNET_NETWORK_get_fd (rfd),
1139                            -1,
1140                            task,
1141                            task_cls);
1142 }
1143
1144
1145 /**
1146  * Schedule a new task to be run with a specified delay or when the
1147  * specified file descriptor is ready for writing.  The delay can be
1148  * used as a timeout on the socket being ready.  The task will be
1149  * scheduled for execution once either the delay has expired or the
1150  * socket operation is ready.  It will be run with the priority of
1151  * the calling task.
1152  *
1153  * @param sched scheduler to use
1154  * @param delay when should this operation time out? Use 
1155  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1156  * @param wfd write file-descriptor
1157  * @param task main function of the task
1158  * @param task_cls closure of task
1159  * @return unique task identifier for the job
1160  *         only valid until "task" is started!
1161  */
1162 GNUNET_SCHEDULER_TaskIdentifier
1163 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
1164                                 struct GNUNET_TIME_Relative delay,
1165                                 struct GNUNET_NETWORK_Handle * wfd,
1166                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1167 {
1168   return add_without_sets (sched,
1169                            delay,
1170                            -1,
1171                            GNUNET_NETWORK_get_fd (wfd),
1172                            task,
1173                            task_cls);
1174 }
1175
1176
1177 /**
1178  * Schedule a new task to be run with a specified delay or when the
1179  * specified file descriptor is ready for reading.  The delay can be
1180  * used as a timeout on the socket being ready.  The task will be
1181  * scheduled for execution once either the delay has expired or the
1182  * socket operation is ready. It will be run with the priority of
1183  * the calling task.
1184  *
1185  * @param sched scheduler to use
1186  * @param delay when should this operation time out? Use 
1187  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1188  * @param rfd read file-descriptor
1189  * @param task main function of the task
1190  * @param task_cls closure of task
1191  * @return unique task identifier for the job
1192  *         only valid until "task" is started!
1193  */
1194 GNUNET_SCHEDULER_TaskIdentifier
1195 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1196                                 struct GNUNET_TIME_Relative delay,
1197                                 const struct GNUNET_DISK_FileHandle * rfd,
1198                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1199 {
1200 #if MINGW
1201   struct GNUNET_NETWORK_FDSet *rs;
1202   GNUNET_SCHEDULER_TaskIdentifier ret;
1203
1204   GNUNET_assert (rfd != NULL);
1205   rs = GNUNET_NETWORK_fdset_create ();
1206   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1207   ret = GNUNET_SCHEDULER_add_select (sched,
1208                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1209                                      GNUNET_SCHEDULER_NO_TASK, delay,
1210                                      rs, NULL, task, task_cls);
1211   GNUNET_NETWORK_fdset_destroy (rs);
1212   return ret;
1213 #else
1214   int fd;
1215
1216   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1217   return add_without_sets (sched,
1218                            delay,
1219                            fd,
1220                            -1,
1221                            task,
1222                            task_cls);
1223
1224 #endif
1225 }
1226
1227
1228 /**
1229  * Schedule a new task to be run with a specified delay or when the
1230  * specified file descriptor is ready for writing.  The delay can be
1231  * used as a timeout on the socket being ready.  The task will be
1232  * scheduled for execution once either the delay has expired or the
1233  * socket operation is ready. It will be run with the priority of
1234  * the calling task.
1235  *
1236  * @param sched scheduler to use
1237  * @param delay when should this operation time out? Use 
1238  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1239  * @param wfd write file-descriptor
1240  * @param task main function of the task
1241  * @param task_cls closure of task
1242  * @return unique task identifier for the job
1243  *         only valid until "task" is started!
1244  */
1245 GNUNET_SCHEDULER_TaskIdentifier
1246 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1247                                  struct GNUNET_TIME_Relative delay,
1248                                  const struct GNUNET_DISK_FileHandle * wfd,
1249                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1250 {
1251 #if MINGW
1252   struct GNUNET_NETWORK_FDSet *ws;
1253   GNUNET_SCHEDULER_TaskIdentifier ret;
1254
1255   GNUNET_assert (wfd != NULL);
1256   ws = GNUNET_NETWORK_fdset_create ();
1257   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1258   ret = GNUNET_SCHEDULER_add_select (sched,
1259                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1260                                      GNUNET_SCHEDULER_NO_TASK,
1261                                      delay, NULL, ws, task, task_cls);
1262   GNUNET_NETWORK_fdset_destroy (ws);
1263   return ret;
1264 #else
1265   int fd;
1266
1267   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1268   return add_without_sets (sched,
1269                            delay,
1270                            -1,
1271                            fd,
1272                            task,
1273                            task_cls);
1274
1275 #endif
1276 }
1277
1278
1279
1280 /**
1281  * Schedule a new task to be run with a specified delay or when any of
1282  * the specified file descriptor sets is ready.  The delay can be used
1283  * as a timeout on the socket(s) being ready.  The task will be
1284  * scheduled for execution once either the delay has expired or any of
1285  * the socket operations is ready.  This is the most general
1286  * function of the "add" family.  Note that the "prerequisite_task"
1287  * must be satisfied in addition to any of the other conditions.  In
1288  * other words, the task will be started when
1289  * <code>
1290  * (prerequisite-run)
1291  * && (delay-ready
1292  *     || any-rs-ready
1293  *     || any-ws-ready
1294  *     || (shutdown-active && run-on-shutdown) )
1295  * </code>
1296  *
1297  * @param sched scheduler to use
1298  * @param prio how important is this task?
1299  * @param prerequisite_task run this task after the task with the given
1300  *        task identifier completes (and any of our other
1301  *        conditions, such as delay, read or write-readiness
1302  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1303  *        on completion of other tasks.
1304  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1305  *        which means that the task will only be run after we receive SIGTERM
1306  * @param rs set of file descriptors we want to read (can be NULL)
1307  * @param ws set of file descriptors we want to write (can be NULL)
1308  * @param task main function of the task
1309  * @param task_cls closure of task
1310  * @return unique task identifier for the job
1311  *         only valid until "task" is started!
1312  */
1313 GNUNET_SCHEDULER_TaskIdentifier
1314 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1315                              enum GNUNET_SCHEDULER_Priority prio,
1316                              GNUNET_SCHEDULER_TaskIdentifier
1317                              prerequisite_task,
1318                              struct GNUNET_TIME_Relative delay,
1319                              const struct GNUNET_NETWORK_FDSet * rs,
1320                              const struct GNUNET_NETWORK_FDSet * ws,
1321                              GNUNET_SCHEDULER_Task task, void *task_cls)
1322 {
1323   struct Task *t;
1324 #if EXECINFO
1325   void *backtrace_array[MAX_TRACE_DEPTH];
1326 #endif
1327
1328   GNUNET_assert (NULL != task);
1329   t = GNUNET_malloc (sizeof (struct Task));
1330   t->callback = task;
1331   t->callback_cls = task_cls;
1332 #if EXECINFO
1333   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1334   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1335 #endif
1336   t->read_fd = -1;
1337   t->write_fd = -1;
1338   if (rs != NULL)
1339     {
1340       t->read_set = GNUNET_NETWORK_fdset_create ();
1341       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1342     }
1343   if (ws != NULL)
1344     {
1345       t->write_set = GNUNET_NETWORK_fdset_create ();
1346       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1347     }
1348   t->id = ++sched->last_id;
1349 #if PROFILE_DELAYS
1350   t->start_time = GNUNET_TIME_absolute_get ();
1351 #endif
1352   t->prereq_id = prerequisite_task;
1353   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1354   t->priority =
1355     check_priority ((prio ==
1356                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1357                     : prio);
1358   t->next = sched->pending; 
1359   sched->pending = t;
1360   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1361                                           t->priority);
1362 #if DEBUG_TASKS
1363   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1364               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1365 #endif
1366 #if EXECINFO
1367   int i;
1368
1369   for (i=0;i<t->num_backtrace_strings;i++)
1370       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1371                   "Task %u trace %d: %s\n",
1372                   t->id,
1373                   i,
1374                   t->backtrace_strings[i]);
1375 #endif
1376   return t->id;
1377 }
1378
1379 /* end of scheduler.c */