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