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