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