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