b15b578e2043e6bf33edabc4d385b5c7d5c7ae95
[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  * List of tasks waiting for an event.
167  */
168 static struct Task *pending;
169
170 /**
171  * List of tasks waiting ONLY for a timeout event.
172  * Sorted by timeout (earliest first).  Used so that
173  * we do not traverse the list of these tasks when
174  * building select sets (we just look at the head
175  * to determine the respective timeout ONCE).
176  */
177 static struct Task *pending_timeout;
178
179 /**
180  * Last inserted task waiting ONLY for a timeout event.
181  * Used to (heuristically) speed up insertion.
182  */
183 static struct Task *pending_timeout_last;
184
185 /**
186  * ID of the task that is running right now.
187  */
188 static struct Task *active_task;
189
190 /**
191  * List of tasks ready to run right now,
192  * grouped by importance.
193  */
194 static struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
195
196 /**
197  * Identity of the last task queued.  Incremented for each task to
198  * generate a unique task ID (it is virtually impossible to start
199  * more than 2^64 tasks during the lifetime of a process).
200  */
201 static GNUNET_SCHEDULER_TaskIdentifier last_id;
202
203 /**
204  * Highest number so that all tasks with smaller identifiers
205  * have already completed.  Also the lowest number of a task
206  * still waiting to be executed.
207  */
208 static GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
209
210 /**
211  * Number of tasks on the ready list.
212  */
213 static unsigned int ready_count;
214
215 /**
216  * How many tasks have we run so far?
217  */
218 static unsigned long long tasks_run;
219
220 /**
221  * Priority of the task running right now.  Only
222  * valid while a task is running.
223  */
224 static enum GNUNET_SCHEDULER_Priority current_priority;
225
226 /**
227  * Priority of the highest task added in the current select
228  * iteration.
229  */
230 static enum GNUNET_SCHEDULER_Priority max_priority_added;
231
232
233 /**
234  * Check that the given priority is legal (and return it).
235  *
236  * @param p priority value to check
237  * @return p on success, 0 on error
238  */
239 static enum GNUNET_SCHEDULER_Priority
240 check_priority (enum GNUNET_SCHEDULER_Priority p)
241 {
242   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
243     return p;
244   GNUNET_assert (0);
245   return 0;                     /* make compiler happy */
246 }
247
248
249 /**
250  * Is a task with this identifier still pending?  Also updates
251  * "lowest_pending_id" as a side-effect (for faster checks in the
252  * future), but only if the return value is "GNUNET_NO" (and
253  * the "lowest_pending_id" check failed).
254  *
255  * @param id which task are we checking for
256  * @return GNUNET_YES if so, GNUNET_NO if not
257  */
258 static int
259 is_pending (GNUNET_SCHEDULER_TaskIdentifier id)
260 {
261   struct Task *pos;
262   enum GNUNET_SCHEDULER_Priority p;
263   GNUNET_SCHEDULER_TaskIdentifier min;
264
265   if (id < lowest_pending_id)
266     return GNUNET_NO;
267   min = -1;                     /* maximum value */
268   pos = 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   pos = pending_timeout;
278   while (pos != NULL)
279     {
280       if (pos->id == id)
281         return GNUNET_YES;
282       if (pos->id < min)
283         min = pos->id;
284       pos = pos->next;
285     }
286   for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
287     {
288       pos = ready[p];
289       while (pos != NULL)
290         {
291           if (pos->id == id)
292             return GNUNET_YES;
293           if (pos->id < min)
294             min = pos->id;
295           pos = pos->next;
296         }
297     }
298   lowest_pending_id = min;
299   return GNUNET_NO;
300 }
301
302
303 /**
304  * Update all sets and timeout for select.
305  *
306  * @param rs read-set, set to all FDs we would like to read (updated)
307  * @param ws write-set, set to all FDs we would like to write (updated)
308  * @param timeout next timeout (updated)
309  */
310 static void
311 update_sets (struct GNUNET_NETWORK_FDSet *rs,
312              struct GNUNET_NETWORK_FDSet *ws,
313              struct GNUNET_TIME_Relative *timeout)
314 {
315   struct Task *pos;
316   struct GNUNET_TIME_Absolute now;
317   struct GNUNET_TIME_Relative to;
318
319   now = GNUNET_TIME_absolute_get ();
320   pos = pending_timeout;
321   if (pos != NULL) 
322     {
323       to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
324       if (timeout->rel_value > to.rel_value)
325         *timeout = to;
326       if (pos->reason != 0)
327         *timeout = GNUNET_TIME_UNIT_ZERO;
328     }
329   pos = pending;
330   while (pos != NULL)
331     {
332       if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
333           (GNUNET_YES == is_pending (pos->prereq_id)))
334         {
335           pos = pos->next;
336           continue;
337         }
338       if (pos->timeout.abs_value != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
339         {
340           to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
341           if (timeout->rel_value > to.rel_value)
342             *timeout = to;
343         }
344       if (pos->read_fd != -1)
345         GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
346       if (pos->write_fd != -1)
347         GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
348       if (pos->read_set != NULL)
349         GNUNET_NETWORK_fdset_add (rs, pos->read_set);
350       if (pos->write_set != NULL)
351         GNUNET_NETWORK_fdset_add (ws, pos->write_set);
352       if (pos->reason != 0)
353         *timeout = GNUNET_TIME_UNIT_ZERO;
354       pos = pos->next;
355     }
356 }
357
358
359 /**
360  * Check if the ready set overlaps with the set we want to have ready.
361  * If so, update the want set (set all FDs that are ready).  If not,
362  * return GNUNET_NO.
363  *
364  * @param ready set that is ready
365  * @param want set that we want to be ready
366  * @return GNUNET_YES if there was some overlap
367  */
368 static int
369 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
370               struct GNUNET_NETWORK_FDSet *want)
371 {
372   if ( (NULL == want) || (NULL == ready) )
373     return GNUNET_NO;
374   if (GNUNET_NETWORK_fdset_overlap (ready, want))
375     {
376       /* copy all over (yes, there maybe unrelated bits,
377          but this should not hurt well-written clients) */
378       GNUNET_NETWORK_fdset_copy (want, ready);
379       return GNUNET_YES;
380     }
381   return GNUNET_NO;
382 }
383
384
385 /**
386  * Check if the given task is eligible to run now.
387  * Also set the reason why it is eligible.
388  *
389  * @param task task to check if it is ready
390  * @param now the current time
391  * @param rs set of FDs ready for reading
392  * @param ws set of FDs ready for writing
393  * @return GNUNET_YES if we can run it, GNUNET_NO if not.
394  */
395 static int
396 is_ready (struct Task *task,
397           struct GNUNET_TIME_Absolute now,
398           const struct GNUNET_NETWORK_FDSet *rs,
399           const struct GNUNET_NETWORK_FDSet *ws)
400 {
401   enum GNUNET_SCHEDULER_Reason reason;
402
403   reason = task->reason;
404   if (now.abs_value >= task->timeout.abs_value)
405     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
406   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
407        ( ( (task->read_fd != -1) &&
408            (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd)) ) ||
409          (set_overlaps (rs, task->read_set) ) ) )
410     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
411   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
412       ( ( (task->write_fd != -1) &&
413           (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)) ) ||
414         (set_overlaps (ws, task->write_set) ) ) )
415     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
416   if (reason == 0)
417     return GNUNET_NO;           /* not ready */    
418   if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
419     {
420       if (GNUNET_YES == is_pending (task->prereq_id))
421         {
422           task->reason = reason;
423           return GNUNET_NO;       /* prereq waiting */
424         }
425       reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
426     }
427   task->reason = reason;
428   return GNUNET_YES;
429 }
430
431
432 /**
433  * Put a task that is ready for execution into the ready queue.
434  *
435  * @param task task ready for execution
436  */
437 static void
438 queue_ready_task (struct Task *task)
439 {
440   enum GNUNET_SCHEDULER_Priority p = task->priority;
441   if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
442     p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
443   task->next = ready[check_priority (p)];
444   ready[check_priority (p)] = task;
445   ready_count++;
446 }
447
448
449 /**
450  * Check which tasks are ready and move them
451  * to the respective ready queue.
452  *
453  * @param rs FDs ready for reading
454  * @param ws FDs ready for writing
455  */
456 static void
457 check_ready (const struct GNUNET_NETWORK_FDSet *rs,
458              const struct GNUNET_NETWORK_FDSet *ws)
459 {
460   struct Task *pos;
461   struct Task *prev;
462   struct Task *next;
463   struct GNUNET_TIME_Absolute now;
464
465   now = GNUNET_TIME_absolute_get ();
466   prev = NULL;
467   pos = pending_timeout;
468   while (pos != NULL)
469     {
470       next = pos->next;
471       if (now.abs_value >= pos->timeout.abs_value)
472         pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
473       if (0 == pos->reason)
474         break;
475       pending_timeout = next;
476       if (pending_timeout_last == pos)
477         pending_timeout_last = NULL;
478       queue_ready_task (pos);
479       pos = next;
480     }
481   pos = pending;
482   while (pos != NULL)
483     {
484 #if DEBUG_TASKS
485       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
486                   "Checking readiness of task: %llu / %p\n",
487                   pos->id, pos->callback_cls);
488 #endif
489       next = pos->next;
490       if (GNUNET_YES == is_ready (pos, now, rs, ws))
491         {
492           if (prev == NULL)
493             pending = next;
494           else
495             prev->next = next;
496           queue_ready_task (pos);
497           pos = next;
498           continue;
499         }
500       prev = pos;
501       pos = next;
502     }
503 }
504
505
506 /**
507  * Request the shutdown of a scheduler.  Marks all currently
508  * pending tasks as ready because of shutdown.  This will
509  * cause all tasks to run (as soon as possible, respecting
510  * priorities and prerequisite tasks).  Note that tasks
511  * scheduled AFTER this call may still be delayed arbitrarily.
512  */
513 void
514 GNUNET_SCHEDULER_shutdown ()
515 {
516   struct Task *pos;
517   int i;
518
519   pos = pending_timeout;
520   while (pos != NULL)
521     {
522       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
523       /* we don't move the task into the ready queue yet; check_ready
524          will do that later, possibly adding additional
525          readiness-factors */
526       pos = pos->next;
527     }
528   pos = pending;
529   while (pos != NULL)
530     {
531       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
532       /* we don't move the task into the ready queue yet; check_ready
533          will do that later, possibly adding additional
534          readiness-factors */
535       pos = pos->next;
536     }
537   for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
538     {
539       pos = ready[i];
540       while (pos != NULL)
541         {
542           pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
543           /* we don't move the task into the ready queue yet; check_ready
544              will do that later, possibly adding additional
545              readiness-factors */
546           pos = pos->next;
547         }
548     }  
549 }
550
551
552 /**
553  * Destroy a task (release associated resources)
554  *
555  * @param t task to destroy
556  */
557 static void
558 destroy_task (struct Task *t)
559 {
560   if (NULL != t->read_set)
561     GNUNET_NETWORK_fdset_destroy (t->read_set);
562   if (NULL != t->write_set)
563     GNUNET_NETWORK_fdset_destroy (t->write_set);
564 #if EXECINFO
565   GNUNET_free (t->backtrace_strings);
566 #endif
567   GNUNET_free (t);
568 }
569
570
571 /**
572  * Run at least one task in the highest-priority queue that is not
573  * empty.  Keep running tasks until we are either no longer running
574  * "URGENT" tasks or until we have at least one "pending" task (which
575  * may become ready, hence we should select on it).  Naturally, if
576  * there are no more ready tasks, we also return.  
577  *
578  * @param rs FDs ready for reading
579  * @param ws FDs ready for writing
580  */
581 static void
582 run_ready (struct GNUNET_NETWORK_FDSet *rs,
583            struct GNUNET_NETWORK_FDSet *ws)
584 {
585   enum GNUNET_SCHEDULER_Priority p;
586   struct Task *pos;
587   struct GNUNET_SCHEDULER_TaskContext tc;
588
589   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
590   do
591     {
592       if (ready_count == 0)
593         return;
594       GNUNET_assert (ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
595       /* yes, p>0 is correct, 0 is "KEEP" which should
596          always be an empty queue (see assertion)! */
597       for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
598         {
599           pos = ready[p];
600           if (pos != NULL)
601             break;
602         }
603       GNUNET_assert (pos != NULL);      /* ready_count wrong? */
604       ready[p] = pos->next;
605       ready_count--;
606       if (current_priority != pos->priority)
607         {
608           current_priority = pos->priority;
609           (void) GNUNET_OS_set_process_priority (GNUNET_OS_process_current (), pos->priority);
610         }
611       active_task = pos;
612 #if PROFILE_DELAYS
613       if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value >
614           DELAY_THRESHOLD.rel_value)
615         {
616           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
617                       "Task %llu took %llums to be scheduled\n",
618                       pos->id,
619                       (unsigned long long) GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value);
620         }
621 #endif
622       tc.reason = pos->reason;
623       tc.read_ready = (pos->read_set == NULL) ? rs : pos->read_set; 
624       if ( (pos->read_fd != -1) &&
625            (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) )
626         GNUNET_NETWORK_fdset_set_native (rs,
627                                          pos->read_fd);
628       tc.write_ready = (pos->write_set == NULL) ? ws : pos->write_set;
629       if ( (pos->write_fd != -1) &&
630            (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) )
631         GNUNET_NETWORK_fdset_set_native (ws,
632                                          pos->write_fd);
633       if ( ( (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0) &&
634            (pos->write_fd != -1) &&
635            (! GNUNET_NETWORK_fdset_test_native (ws,
636                                                 pos->write_fd))) 
637         abort (); // added to ready in previous select loop!
638 #if DEBUG_TASKS
639       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640                   "Running task: %llu / %p\n", pos->id, pos->callback_cls);
641 #endif
642       pos->callback (pos->callback_cls, &tc);
643 #if EXECINFO
644       int i;
645       for (i=0;i<pos->num_backtrace_strings;i++)
646         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
647                     "Task %llu trace %d: %s\n",
648                     pos->id,
649                     i,
650                     pos->backtrace_strings[i]);
651 #endif
652       active_task = NULL;
653       destroy_task (pos);
654       tasks_run++;
655     }
656   while ( (pending == NULL) || (p >= max_priority_added) );
657 }
658
659 /**
660  * Pipe used to communicate shutdown via signal.
661  */
662 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
663
664 /**
665  * Signal handler called for SIGPIPE.
666  */
667 #ifndef MINGW
668 static void
669 sighandler_pipe ()
670 {
671   return;
672 }
673 #endif
674 /**
675  * Signal handler called for signals that should cause us to shutdown.
676  */
677 static void
678 sighandler_shutdown ()
679 {
680   static char c;
681   int old_errno = errno; /* backup errno */
682
683   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
684                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE), &c,
685                           sizeof (c));
686   errno = old_errno;
687 }
688
689
690 /**
691  * Initialize and run scheduler.  This function will return when all
692  * tasks have completed.  On systems with signals, receiving a SIGTERM
693  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
694  * to be run after the active task is complete.  As a result, SIGTERM
695  * causes all active tasks to be scheduled with reason
696  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
697  * afterwards will execute normally!). Note that any particular signal
698  * will only shut down one scheduler; applications should always only
699  * create a single scheduler.
700  *
701  * @param task task to run immediately
702  * @param task_cls closure of task
703  */
704 void
705 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
706 {
707   struct GNUNET_NETWORK_FDSet *rs;
708   struct GNUNET_NETWORK_FDSet *ws;
709   struct GNUNET_TIME_Relative timeout;
710   int ret;
711   struct GNUNET_SIGNAL_Context *shc_int;
712   struct GNUNET_SIGNAL_Context *shc_term;
713 #ifndef MINGW
714   struct GNUNET_SIGNAL_Context *shc_quit;
715   struct GNUNET_SIGNAL_Context *shc_hup;
716   struct GNUNET_SIGNAL_Context *shc_pipe;
717 #endif
718   unsigned long long last_tr;
719   unsigned int busy_wait_warning;
720   const struct GNUNET_DISK_FileHandle *pr;
721   char c;
722
723   GNUNET_assert (active_task == NULL);
724   rs = GNUNET_NETWORK_fdset_create ();
725   ws = GNUNET_NETWORK_fdset_create ();
726   GNUNET_assert (shutdown_pipe_handle == NULL);
727   shutdown_pipe_handle =  GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
728   GNUNET_assert (shutdown_pipe_handle != NULL);
729   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_READ);
730   GNUNET_assert (pr != NULL);
731   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
732   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
733 #ifndef MINGW
734   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
735   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
736   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
737 #endif
738   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
739   GNUNET_SCHEDULER_add_continuation (task,
740                                      task_cls,
741                                      GNUNET_SCHEDULER_REASON_STARTUP);
742 #if ENABLE_WINDOWS_WORKAROUNDS
743   GNUNET_SCHEDULER_add_continuation (&GNUNET_OS_install_parent_control_handler,
744                                      NULL, GNUNET_SCHEDULER_REASON_STARTUP);
745 #endif
746   last_tr = 0;
747   busy_wait_warning = 0;
748   while ((pending != NULL) ||
749          (pending_timeout != NULL) ||
750          (ready_count > 0))
751     {
752       GNUNET_NETWORK_fdset_zero (rs);
753       GNUNET_NETWORK_fdset_zero (ws);
754       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
755       update_sets (rs, ws, &timeout);
756       GNUNET_NETWORK_fdset_handle_set (rs, pr);
757       if (ready_count > 0)
758         {
759           /* no blocking, more work already ready! */
760           timeout = GNUNET_TIME_UNIT_ZERO;
761         }
762       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
763       if (ret == GNUNET_SYSERR)
764         {
765           if (errno == EINTR)
766             continue;
767
768           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
769 #ifndef MINGW
770 #if USE_LSOF
771           char lsof[512];
772           snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
773           close (1);
774           dup2 (2, 1);
775           ret = system (lsof);            
776 #endif
777 #endif
778           abort ();
779           break;
780         }
781       if ((ret == 0) && (timeout.rel_value == 0) && (busy_wait_warning > 16))
782         {
783           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
784                       _("Looks like we're busy waiting...\n"));
785           sleep (1);            /* mitigate */
786         }
787       check_ready (rs, ws);
788       run_ready (rs, ws);
789       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
790         {
791           /* consume the signal */
792           GNUNET_DISK_file_read (pr, &c, sizeof (c));
793           /* mark all active tasks as ready due to shutdown */
794           GNUNET_SCHEDULER_shutdown ();
795         }
796       if (last_tr == tasks_run)
797         {
798           busy_wait_warning++;
799         }
800       else
801         {
802           last_tr = tasks_run;
803           busy_wait_warning = 0;
804         }
805     }
806   GNUNET_SIGNAL_handler_uninstall (shc_int);
807   GNUNET_SIGNAL_handler_uninstall (shc_term);
808 #ifndef MINGW
809   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
810   GNUNET_SIGNAL_handler_uninstall (shc_quit);
811   GNUNET_SIGNAL_handler_uninstall (shc_hup);
812 #endif
813   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
814   shutdown_pipe_handle = NULL;
815   GNUNET_NETWORK_fdset_destroy (rs);
816   GNUNET_NETWORK_fdset_destroy (ws);
817 }
818
819
820 /**
821  * Obtain the reason code for why the current task was
822  * started.  Will return the same value as 
823  * the GNUNET_SCHEDULER_TaskContext's reason field.
824  *
825  * @return reason(s) why the current task is run
826  */
827 enum GNUNET_SCHEDULER_Reason
828 GNUNET_SCHEDULER_get_reason ()
829 {
830   GNUNET_assert (active_task != NULL);
831   return active_task->reason;
832 }
833
834
835 /**
836  * Get information about the current load of this scheduler.  Use this
837  * function to determine if an elective task should be added or simply
838  * dropped (if the decision should be made based on the number of
839  * tasks ready to run).
840  *
841  * @param p priority level to look at
842  * @return number of tasks pending right now
843  */
844 unsigned int
845 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
846 {
847   struct Task *pos;
848   unsigned int ret;
849
850   GNUNET_assert (active_task != NULL);
851   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
852     return ready_count;
853   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
854     p = current_priority;
855   ret = 0;
856   pos = ready[check_priority (p)];
857   while (pos != NULL)
858     {
859       pos = pos->next;
860       ret++;
861     }
862   return ret;
863 }
864
865
866 /**
867  * Cancel the task with the specified identifier.
868  * The task must not yet have run.
869  *
870  * @param task id of the task to cancel
871  * @return original closure of the task
872  */
873 void *
874 GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_TaskIdentifier task)
875 {
876   struct Task *t;
877   struct Task *prev;
878   enum GNUNET_SCHEDULER_Priority p;
879   int to;
880   void *ret;
881
882   GNUNET_assert (active_task != NULL);
883   to = 0;
884   prev = NULL;
885   t = pending;
886   while (t != NULL)
887     {
888       if (t->id == task)
889         break;
890       prev = t;
891       t = t->next;
892     }
893   if (t == NULL)
894     {
895       prev = NULL;
896       to = 1;
897       t = pending_timeout;
898       while (t != NULL)
899         {
900           if (t->id == task)
901             break;
902           prev = t;
903           t = t->next;
904         }
905       if (pending_timeout_last == t)
906         pending_timeout_last = NULL;
907     }
908   p = 0;
909   while (t == NULL)
910     {
911       p++;
912       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
913       prev = NULL;
914       t = ready[p];
915       while (t != NULL)
916         {
917           if (t->id == task)
918             {
919               ready_count--;
920               break;
921             }
922           prev = t;
923           t = t->next;
924         }
925     }
926   if (prev == NULL)
927     {
928       if (p == 0)
929         {
930           if (to == 0)
931             {
932               pending = t->next;
933             }
934           else
935             {
936               pending_timeout = t->next;
937             }
938         }
939       else
940         {
941           ready[p] = t->next;
942         }
943     }
944   else
945     {
946       prev->next = t->next;
947     }
948   ret = t->callback_cls;
949 #if DEBUG_TASKS
950   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
951               "Canceling task: %llu / %p\n", task, t->callback_cls);
952 #endif
953   destroy_task (t);
954   return ret;
955 }
956
957
958 /**
959  * Continue the current execution with the given function.  This is
960  * similar to the other "add" functions except that there is no delay
961  * and the reason code can be specified.
962  *
963  * @param task main function of the task
964  * @param task_cls closure for 'main'
965  * @param reason reason for task invocation
966  */
967 void
968 GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_Task task,
969                                    void *task_cls,
970                                    enum GNUNET_SCHEDULER_Reason reason)
971 {
972   struct Task *t;
973 #if EXECINFO
974   void *backtrace_array[50];
975 #endif
976
977   GNUNET_assert ( (active_task != NULL) ||
978                   (reason == GNUNET_SCHEDULER_REASON_STARTUP) );
979   t = GNUNET_malloc (sizeof (struct Task));
980 #if EXECINFO
981   t->num_backtrace_strings = backtrace(backtrace_array, 50);
982   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
983 #endif
984   t->read_fd = -1;
985   t->write_fd = -1;
986   t->callback = task;
987   t->callback_cls = task_cls;
988   t->id = ++last_id;
989 #if PROFILE_DELAYS
990   t->start_time = GNUNET_TIME_absolute_get ();
991 #endif
992   t->reason = reason;
993   t->priority = current_priority;
994 #if DEBUG_TASKS
995   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
996               "Adding continuation task: %llu / %p\n",
997               t->id, t->callback_cls);
998 #endif
999   queue_ready_task (t);
1000 }
1001
1002
1003
1004 /**
1005  * Schedule a new task to be run after the specified prerequisite task
1006  * has completed. It will be run with the priority of the calling
1007  * task.
1008  *
1009  * @param prerequisite_task run this task after the task with the given
1010  *        task identifier completes (and any of our other
1011  *        conditions, such as delay, read or write-readiness
1012  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
1013  *        on completion of other tasks (this will cause the task to run as
1014  *        soon as possible).
1015  * @param task main function of the task
1016  * @param task_cls closure of task
1017  * @return unique task identifier for the job
1018  *         only valid until "task" is started!
1019  */
1020 GNUNET_SCHEDULER_TaskIdentifier
1021 GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
1022                             GNUNET_SCHEDULER_Task task, void *task_cls)
1023 {
1024   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1025                                       prerequisite_task,
1026                                       GNUNET_TIME_UNIT_ZERO,
1027                                       NULL, NULL, task, task_cls);
1028 }
1029
1030
1031 /**
1032  * Schedule a new task to be run with a specified priority.
1033  *
1034  * @param prio how important is the new task?
1035  * @param task main function of the task
1036  * @param task_cls closure of task
1037  * @return unique task identifier for the job
1038  *         only valid until "task" is started!
1039  */
1040 GNUNET_SCHEDULER_TaskIdentifier
1041 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1042                                     GNUNET_SCHEDULER_Task task,
1043                                     void *task_cls)
1044 {
1045   return GNUNET_SCHEDULER_add_select (prio,
1046                                       GNUNET_SCHEDULER_NO_TASK,
1047                                       GNUNET_TIME_UNIT_ZERO,
1048                                       NULL, NULL, task, task_cls);
1049 }
1050
1051
1052
1053 /**
1054  * Schedule a new task to be run with a specified delay.  The task
1055  * will be scheduled for execution once the delay has expired. It
1056  * will be run with the priority of the calling task.
1057  *
1058  * @param delay when should this operation time out? Use 
1059  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1060  * @param task main function of the task
1061  * @param task_cls closure of task
1062  * @return unique task identifier for the job
1063  *         only valid until "task" is started!
1064  */
1065 GNUNET_SCHEDULER_TaskIdentifier
1066 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1067                               GNUNET_SCHEDULER_Task task, void *task_cls)
1068 {
1069 #if 1
1070   /* new, optimized version */
1071   struct Task *t;
1072   struct Task *pos;
1073   struct Task *prev;
1074 #if EXECINFO
1075   void *backtrace_array[MAX_TRACE_DEPTH];
1076 #endif
1077
1078   GNUNET_assert (active_task != NULL);
1079   GNUNET_assert (NULL != task);
1080   t = GNUNET_malloc (sizeof (struct Task));
1081   t->callback = task;
1082   t->callback_cls = task_cls;
1083 #if EXECINFO
1084   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1085   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1086 #endif
1087   t->read_fd = -1;
1088   t->write_fd = -1;
1089   t->id = ++last_id;
1090 #if PROFILE_DELAYS
1091   t->start_time = GNUNET_TIME_absolute_get ();
1092 #endif
1093   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1094   t->priority = current_priority;
1095   /* try tail first (optimization in case we are
1096      appending to a long list of tasks with timeouts) */
1097   prev = pending_timeout_last;
1098   if (prev != NULL) 
1099     {
1100       if (prev->timeout.abs_value > t->timeout.abs_value)
1101         prev = NULL;
1102       else
1103         pos = prev->next; /* heuristic success! */
1104     }
1105   if (prev == NULL)
1106     {
1107       /* heuristic failed, do traversal of timeout list */
1108       pos = pending_timeout;
1109     }
1110   while ( (pos != NULL) &&
1111           ( (pos->timeout.abs_value <= t->timeout.abs_value) ||
1112             (pos->reason != 0) ) )
1113     {
1114       prev = pos;
1115       pos = pos->next;
1116     }
1117   if (prev == NULL)
1118     pending_timeout = t;
1119   else
1120     prev->next = t;
1121   t->next = pos;
1122   /* hyper-optimization... */
1123   pending_timeout_last = t;
1124
1125 #if DEBUG_TASKS
1126   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1127               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1128 #endif
1129 #if EXECINFO
1130   int i;
1131
1132   for (i=0;i<t->num_backtrace_strings;i++)
1133       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1134                   "Task %llu trace %d: %s\n",
1135                   t->id,
1136                   i,
1137                   t->backtrace_strings[i]);
1138 #endif
1139   return t->id;
1140
1141 #else
1142   /* unoptimized version */
1143   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1144                                       GNUNET_SCHEDULER_NO_TASK, delay,
1145                                       NULL, NULL, task, task_cls);
1146 #endif
1147 }
1148
1149
1150
1151 /**
1152  * Schedule a new task to be run as soon as possible. The task
1153  * will be run with the priority of the calling task.
1154  *
1155  * @param task main function of the task
1156  * @param task_cls closure of task
1157  * @return unique task identifier for the job
1158  *         only valid until "task" is started!
1159  */
1160 GNUNET_SCHEDULER_TaskIdentifier
1161 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task,
1162                                                   void *task_cls)
1163 {
1164   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1165                                       GNUNET_SCHEDULER_NO_TASK,
1166                                       GNUNET_TIME_UNIT_ZERO,
1167                                       NULL, NULL, task, task_cls);
1168 }
1169
1170
1171
1172
1173 /**
1174  * Schedule a new task to be run with a specified delay or when any of
1175  * the specified file descriptor sets is ready.  The delay can be used
1176  * as a timeout on the socket(s) being ready.  The task will be
1177  * scheduled for execution once either the delay has expired or any of
1178  * the socket operations is ready.  This is the most general
1179  * function of the "add" family.  Note that the "prerequisite_task"
1180  * must be satisfied in addition to any of the other conditions.  In
1181  * other words, the task will be started when
1182  * <code>
1183  * (prerequisite-run)
1184  * && (delay-ready
1185  *     || any-rs-ready
1186  *     || any-ws-ready
1187  *     || shutdown-active )
1188  * </code>
1189  *
1190  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1191  *        which means that the task will only be run after we receive SIGTERM
1192  * @param rfd file descriptor we want to read (can be -1)
1193  * @param wfd file descriptors we want to write (can be -1)
1194  * @param task main function of the task
1195  * @param task_cls closure of task
1196  * @return unique task identifier for the job
1197  *         only valid until "task" is started!
1198  */
1199 #ifndef MINGW
1200 GNUNET_SCHEDULER_TaskIdentifier
1201 add_without_sets (struct GNUNET_TIME_Relative delay,
1202                   int rfd,
1203                   int wfd,
1204                   GNUNET_SCHEDULER_Task task, void *task_cls)
1205 {
1206   struct Task *t;
1207 #if EXECINFO
1208   void *backtrace_array[MAX_TRACE_DEPTH];
1209 #endif
1210
1211   GNUNET_assert (active_task != NULL);
1212   GNUNET_assert (NULL != task);
1213   t = GNUNET_malloc (sizeof (struct Task));
1214   t->callback = task;
1215   t->callback_cls = task_cls;
1216 #if EXECINFO
1217   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1218   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1219 #endif
1220   t->read_fd = rfd;
1221   t->write_fd = wfd;
1222   t->id = ++last_id;
1223 #if PROFILE_DELAYS
1224   t->start_time = GNUNET_TIME_absolute_get ();
1225 #endif
1226   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1227   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1228   t->priority = check_priority (current_priority);
1229   t->next = pending;
1230   pending = t;
1231   max_priority_added = GNUNET_MAX (max_priority_added,
1232                                           t->priority);
1233 #if DEBUG_TASKS
1234   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1235               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1236 #endif
1237 #if EXECINFO
1238   int i;
1239
1240   for (i=0;i<t->num_backtrace_strings;i++)
1241       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1242                   "Task %llu trace %d: %s\n",
1243                   t->id,
1244                   i,
1245                   t->backtrace_strings[i]);
1246 #endif
1247   return t->id;
1248 }
1249 #endif
1250
1251
1252
1253 /**
1254  * Schedule a new task to be run with a specified delay or when the
1255  * specified file descriptor is ready for reading.  The delay can be
1256  * used as a timeout on the socket being ready.  The task will be
1257  * scheduled for execution once either the delay has expired or the
1258  * socket operation is ready.  It will be run with the priority of
1259  * the calling task.
1260  *
1261  * @param delay when should this operation time out? Use 
1262  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1263  * @param rfd read file-descriptor
1264  * @param task main function of the task
1265  * @param task_cls closure of task
1266  * @return unique task identifier for the job
1267  *         only valid until "task" is started!
1268  */
1269 GNUNET_SCHEDULER_TaskIdentifier
1270 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1271                                struct GNUNET_NETWORK_Handle * rfd,
1272                                GNUNET_SCHEDULER_Task task, void *task_cls)
1273 {
1274 #if MINGW
1275   struct GNUNET_NETWORK_FDSet *rs;
1276   GNUNET_SCHEDULER_TaskIdentifier ret;
1277
1278   GNUNET_assert (rfd != NULL);
1279   rs = GNUNET_NETWORK_fdset_create ();
1280   GNUNET_NETWORK_fdset_set (rs, rfd);
1281   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1282                                      GNUNET_SCHEDULER_NO_TASK, delay,
1283                                      rs, NULL, task, task_cls);
1284   GNUNET_NETWORK_fdset_destroy (rs);
1285   return ret;
1286 #else
1287   return add_without_sets (delay,
1288                            GNUNET_NETWORK_get_fd (rfd),
1289                            -1,
1290                            task,
1291                            task_cls);
1292 #endif
1293 }
1294
1295
1296 /**
1297  * Schedule a new task to be run with a specified delay or when the
1298  * specified file descriptor is ready for writing.  The delay can be
1299  * used as a timeout on the socket being ready.  The task will be
1300  * scheduled for execution once either the delay has expired or the
1301  * socket operation is ready.  It will be run with the priority of
1302  * the calling task.
1303  *
1304  * @param delay when should this operation time out? Use 
1305  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1306  * @param wfd write file-descriptor
1307  * @param task main function of the task
1308  * @param task_cls closure of task
1309  * @return unique task identifier for the job
1310  *         only valid until "task" is started!
1311  */
1312 GNUNET_SCHEDULER_TaskIdentifier
1313 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1314                                 struct GNUNET_NETWORK_Handle * wfd,
1315                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1316 {
1317 #if MINGW
1318   struct GNUNET_NETWORK_FDSet *ws;
1319   GNUNET_SCHEDULER_TaskIdentifier ret;
1320
1321   GNUNET_assert (wfd != NULL);
1322   ws = GNUNET_NETWORK_fdset_create ();
1323   GNUNET_NETWORK_fdset_set (ws, wfd);
1324   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1325                                      GNUNET_SCHEDULER_NO_TASK, delay,
1326                                      NULL, ws, task, task_cls);
1327   GNUNET_NETWORK_fdset_destroy (ws);
1328   return ret;
1329 #else
1330   return add_without_sets (delay,
1331                            -1,
1332                            GNUNET_NETWORK_get_fd (wfd),
1333                            task,
1334                            task_cls);
1335 #endif
1336 }
1337
1338
1339 /**
1340  * Schedule a new task to be run with a specified delay or when the
1341  * specified file descriptor is ready for reading.  The delay can be
1342  * used as a timeout on the socket being ready.  The task will be
1343  * scheduled for execution once either the delay has expired or the
1344  * socket operation is ready. It will be run with the priority of
1345  * the calling task.
1346  *
1347  * @param delay when should this operation time out? Use 
1348  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1349  * @param rfd read file-descriptor
1350  * @param task main function of the task
1351  * @param task_cls closure of task
1352  * @return unique task identifier for the job
1353  *         only valid until "task" is started!
1354  */
1355 GNUNET_SCHEDULER_TaskIdentifier
1356 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1357                                 const struct GNUNET_DISK_FileHandle * rfd,
1358                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1359 {
1360 #if MINGW
1361   struct GNUNET_NETWORK_FDSet *rs;
1362   GNUNET_SCHEDULER_TaskIdentifier ret;
1363
1364   GNUNET_assert (rfd != NULL);
1365   rs = GNUNET_NETWORK_fdset_create ();
1366   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1367   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1368                                      GNUNET_SCHEDULER_NO_TASK, delay,
1369                                      rs, NULL, task, task_cls);
1370   GNUNET_NETWORK_fdset_destroy (rs);
1371   return ret;
1372 #else
1373   int fd;
1374
1375   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1376   return add_without_sets (delay,
1377                            fd,
1378                            -1,
1379                            task,
1380                            task_cls);
1381
1382 #endif
1383 }
1384
1385
1386 /**
1387  * Schedule a new task to be run with a specified delay or when the
1388  * specified file descriptor is ready for writing.  The delay can be
1389  * used as a timeout on the socket being ready.  The task will be
1390  * scheduled for execution once either the delay has expired or the
1391  * socket operation is ready. It will be run with the priority of
1392  * the calling task.
1393  *
1394  * @param delay when should this operation time out? Use 
1395  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1396  * @param wfd write file-descriptor
1397  * @param task main function of the task
1398  * @param task_cls closure of task
1399  * @return unique task identifier for the job
1400  *         only valid until "task" is started!
1401  */
1402 GNUNET_SCHEDULER_TaskIdentifier
1403 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1404                                  const struct GNUNET_DISK_FileHandle * wfd,
1405                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1406 {
1407 #if MINGW
1408   struct GNUNET_NETWORK_FDSet *ws;
1409   GNUNET_SCHEDULER_TaskIdentifier ret;
1410
1411   GNUNET_assert (wfd != NULL);
1412   ws = GNUNET_NETWORK_fdset_create ();
1413   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1414   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1415                                      GNUNET_SCHEDULER_NO_TASK,
1416                                      delay, NULL, ws, task, task_cls);
1417   GNUNET_NETWORK_fdset_destroy (ws);
1418   return ret;
1419 #else
1420   int fd;
1421
1422   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1423   return add_without_sets (delay,
1424                            -1,
1425                            fd,
1426                            task,
1427                            task_cls);
1428
1429 #endif
1430 }
1431
1432
1433
1434 /**
1435  * Schedule a new task to be run with a specified delay or when any of
1436  * the specified file descriptor sets is ready.  The delay can be used
1437  * as a timeout on the socket(s) being ready.  The task will be
1438  * scheduled for execution once either the delay has expired or any of
1439  * the socket operations is ready.  This is the most general
1440  * function of the "add" family.  Note that the "prerequisite_task"
1441  * must be satisfied in addition to any of the other conditions.  In
1442  * other words, the task will be started when
1443  * <code>
1444  * (prerequisite-run)
1445  * && (delay-ready
1446  *     || any-rs-ready
1447  *     || any-ws-ready
1448  *     || (shutdown-active && run-on-shutdown) )
1449  * </code>
1450  *
1451  * @param prio how important is this task?
1452  * @param prerequisite_task run this task after the task with the given
1453  *        task identifier completes (and any of our other
1454  *        conditions, such as delay, read or write-readiness
1455  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1456  *        on completion of other tasks.
1457  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1458  *        which means that the task will only be run after we receive SIGTERM
1459  * @param rs set of file descriptors we want to read (can be NULL)
1460  * @param ws set of file descriptors we want to write (can be NULL)
1461  * @param task main function of the task
1462  * @param task_cls closure of task
1463  * @return unique task identifier for the job
1464  *         only valid until "task" is started!
1465  */
1466 GNUNET_SCHEDULER_TaskIdentifier
1467 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1468                              GNUNET_SCHEDULER_TaskIdentifier
1469                              prerequisite_task,
1470                              struct GNUNET_TIME_Relative delay,
1471                              const struct GNUNET_NETWORK_FDSet * rs,
1472                              const struct GNUNET_NETWORK_FDSet * ws,
1473                              GNUNET_SCHEDULER_Task task, void *task_cls)
1474 {
1475   struct Task *t;
1476 #if EXECINFO
1477   void *backtrace_array[MAX_TRACE_DEPTH];
1478 #endif
1479
1480   GNUNET_assert (active_task != NULL);
1481   GNUNET_assert (NULL != task);
1482   t = GNUNET_malloc (sizeof (struct Task));
1483   t->callback = task;
1484   t->callback_cls = task_cls;
1485 #if EXECINFO
1486   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1487   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1488 #endif
1489   t->read_fd = -1;
1490   t->write_fd = -1;
1491   if (rs != NULL)
1492     {
1493       t->read_set = GNUNET_NETWORK_fdset_create ();
1494       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1495     }
1496   if (ws != NULL)
1497     {
1498       t->write_set = GNUNET_NETWORK_fdset_create ();
1499       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1500     }
1501   t->id = ++last_id;
1502 #if PROFILE_DELAYS
1503   t->start_time = GNUNET_TIME_absolute_get ();
1504 #endif
1505   t->prereq_id = prerequisite_task;
1506   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1507   t->priority =
1508     check_priority ((prio ==
1509                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority
1510                     : prio);
1511   t->next = pending;
1512   pending = t;
1513   max_priority_added = GNUNET_MAX (max_priority_added,
1514                                           t->priority);
1515 #if DEBUG_TASKS
1516   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1517               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1518 #endif
1519 #if EXECINFO
1520   int i;
1521
1522   for (i=0;i<t->num_backtrace_strings;i++)
1523       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1524                   "Task %llu trace %d: %s\n",
1525                   t->id,
1526                   i,
1527                   t->backtrace_strings[i]);
1528 #endif
1529   return t->id;
1530 }
1531
1532 /* end of scheduler.c */