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