- refactor kx sending, unify under send_kx
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (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 = task->priority = 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_assert (0);          // 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_DEBUG,
593            "Task %p trace %u: %s\n",
594            pos,
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,
784                                           ws,
785                                           NULL,
786                                           timeout);
787     else
788       ret = scheduler_select (scheduler_select_cls,
789                               rs,
790                               ws,
791                               NULL,
792                               timeout);
793     if (ret == GNUNET_SYSERR)
794     {
795       if (errno == EINTR)
796         continue;
797
798       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
799 #ifndef MINGW
800 #if USE_LSOF
801       char lsof[512];
802
803       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
804       (void) close (1);
805       (void) dup2 (2, 1);
806       if (0 != system (lsof))
807         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
808                       "system");
809 #endif
810 #endif
811 #if DEBUG_FDS
812       struct GNUNET_SCHEDULER_Task *t;
813
814       for (t = pending_head; NULL != t; t = t->next)
815       {
816         if (-1 != t->read_fd)
817         {
818           int flags = fcntl (t->read_fd, F_GETFD);
819           if ((flags == -1) && (errno == EBADF))
820             {
821               LOG (GNUNET_ERROR_TYPE_ERROR,
822                    "Got invalid file descriptor %d!\n",
823                    t->read_fd);
824 #if EXECINFO
825               unsigned int i;
826
827               for (i = 0; i < t->num_backtrace_strings; i++)
828                 LOG (GNUNET_ERROR_TYPE_ERROR,
829                      "Trace: %s\n",
830                      t->backtrace_strings[i]);
831 #endif
832             }
833         }
834         if (-1 != t->write_fd)
835           {
836             int flags = fcntl (t->write_fd, F_GETFD);
837             if ((flags == -1) && (errno == EBADF))
838               {
839                 LOG (GNUNET_ERROR_TYPE_ERROR,
840                      "Got invalid file descriptor %d!\n",
841                      t->write_fd);
842 #if EXECINFO
843                 unsigned int i;
844
845                 for (i = 0; i < t->num_backtrace_strings; i++)
846                   LOG (GNUNET_ERROR_TYPE_DEBUG,
847                        "Trace: %s\n",
848                        t->backtrace_strings[i]);
849 #endif
850               }
851           }
852       }
853 #endif
854       GNUNET_assert (0);
855       break;
856     }
857
858     if ( (0 == ret) &&
859          (0 == timeout.rel_value_us) &&
860          (busy_wait_warning > 16) )
861     {
862       LOG (GNUNET_ERROR_TYPE_WARNING,
863            "Looks like we're busy waiting...\n");
864       short_wait (100);                /* mitigate */
865     }
866     check_ready (rs, ws);
867     run_ready (rs, ws);
868     if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
869     {
870       /* consume the signal */
871       GNUNET_DISK_file_read (pr, &c, sizeof (c));
872       /* mark all active tasks as ready due to shutdown */
873       GNUNET_SCHEDULER_shutdown ();
874     }
875     if (last_tr == tasks_run)
876     {
877       short_wait (1);
878       busy_wait_warning++;
879     }
880     else
881     {
882       last_tr = tasks_run;
883       busy_wait_warning = 0;
884     }
885   }
886   GNUNET_SIGNAL_handler_uninstall (shc_int);
887   GNUNET_SIGNAL_handler_uninstall (shc_term);
888 #if (SIGTERM != GNUNET_TERM_SIG)
889   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
890 #endif
891 #ifndef MINGW
892   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
893   GNUNET_SIGNAL_handler_uninstall (shc_quit);
894   GNUNET_SIGNAL_handler_uninstall (shc_hup);
895 #endif
896   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
897   shutdown_pipe_handle = NULL;
898   GNUNET_NETWORK_fdset_destroy (rs);
899   GNUNET_NETWORK_fdset_destroy (ws);
900 }
901
902
903 /**
904  * Obtain the reason code for why the current task was
905  * started.  Will return the same value as
906  * the `struct GNUNET_SCHEDULER_TaskContext`'s reason field.
907  *
908  * @return reason(s) why the current task is run
909  */
910 enum GNUNET_SCHEDULER_Reason
911 GNUNET_SCHEDULER_get_reason ()
912 {
913   GNUNET_assert (NULL != active_task);
914   return active_task->reason;
915 }
916
917
918 /**
919  * Get information about the current load of this scheduler.  Use this
920  * function to determine if an elective task should be added or simply
921  * dropped (if the decision should be made based on the number of
922  * tasks ready to run).
923  *
924  * @param p priority level to look at
925  * @return number of tasks pending right now
926  */
927 unsigned int
928 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
929 {
930   struct GNUNET_SCHEDULER_Task *pos;
931   unsigned int ret;
932
933   GNUNET_assert (NULL != active_task);
934   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
935     return ready_count;
936   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
937     p = current_priority;
938   ret = 0;
939   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
940     ret++;
941   return ret;
942 }
943
944
945 /**
946  * Cancel the task with the specified identifier.
947  * The task must not yet have run.
948  *
949  * @param task id of the task to cancel
950  * @return original closure of the task
951  */
952 void *
953 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
954 {
955   enum GNUNET_SCHEDULER_Priority p;
956   void *ret;
957
958   GNUNET_assert (NULL != active_task);
959   if (! task->in_ready_list)
960   {
961     if ( (-1 == task->read_fd) &&
962          (-1 == task->write_fd) &&
963          (NULL == task->read_set) &&
964          (NULL == task->write_set) )
965     {
966       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
967                                    pending_timeout_tail,
968                                    task);
969       if (task == pending_timeout_last)
970         pending_timeout_last = NULL;
971     }
972     else
973     {
974       GNUNET_CONTAINER_DLL_remove (pending_head,
975                                    pending_tail,
976                                    task);
977     }
978   }
979   else
980   {
981     p = check_priority (task->priority);
982     GNUNET_CONTAINER_DLL_remove (ready_head[p],
983                                  ready_tail[p],
984                                  task);
985     ready_count--;
986   }
987   ret = task->callback_cls;
988   LOG (GNUNET_ERROR_TYPE_DEBUG,
989        "Canceling task %p\n",
990        task);
991   destroy_task (task);
992   return ret;
993 }
994
995
996 /**
997  * Continue the current execution with the given function.  This is
998  * similar to the other "add" functions except that there is no delay
999  * and the reason code can be specified.
1000  *
1001  * @param task main function of the task
1002  * @param task_cls closure for @a task
1003  * @param reason reason for task invocation
1004  * @param priority priority to use for the task
1005  */
1006 void
1007 GNUNET_SCHEDULER_add_continuation_with_priority (GNUNET_SCHEDULER_TaskCallback task,
1008                                                  void *task_cls,
1009                                                  enum GNUNET_SCHEDULER_Reason reason,
1010                                                  enum GNUNET_SCHEDULER_Priority priority)
1011 {
1012   struct GNUNET_SCHEDULER_Task *t;
1013
1014 #if EXECINFO
1015   void *backtrace_array[50];
1016 #endif
1017
1018   GNUNET_assert (NULL != task);
1019   GNUNET_assert ((NULL != active_task) ||
1020                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1021   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1022 #if EXECINFO
1023   t->num_backtrace_strings = backtrace (backtrace_array, 50);
1024   t->backtrace_strings =
1025       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1026 #endif
1027   t->read_fd = -1;
1028   t->write_fd = -1;
1029   t->callback = task;
1030   t->callback_cls = task_cls;
1031 #if PROFILE_DELAYS
1032   t->start_time = GNUNET_TIME_absolute_get ();
1033 #endif
1034   t->reason = reason;
1035   t->priority = priority;
1036   t->lifeness = current_lifeness;
1037   LOG (GNUNET_ERROR_TYPE_DEBUG,
1038        "Adding continuation task %p\n",
1039        t);
1040   queue_ready_task (t);
1041 }
1042
1043
1044 /**
1045  * Continue the current execution with the given function.  This is
1046  * similar to the other "add" functions except that there is no delay
1047  * and the reason code can be specified.
1048  *
1049  * @param task main function of the task
1050  * @param task_cls closure for @a task
1051  * @param reason reason for task invocation
1052  */
1053 void
1054 GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_TaskCallback task, void *task_cls,
1055                                    enum GNUNET_SCHEDULER_Reason reason)
1056 {
1057   GNUNET_SCHEDULER_add_continuation_with_priority (task, task_cls,
1058                                                    reason,
1059                                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT);
1060 }
1061
1062
1063 /**
1064  * Schedule a new task to be run with a specified delay.  The task
1065  * will be scheduled for execution once the delay has expired.
1066  *
1067  * @param delay when should this operation time out? Use
1068  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1069  * @param priority priority to use for the task
1070  * @param task main function of the task
1071  * @param task_cls closure of @a task
1072  * @return unique task identifier for the job
1073  *         only valid until @a task is started!
1074  */
1075 struct GNUNET_SCHEDULER_Task *
1076 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1077                                             enum GNUNET_SCHEDULER_Priority priority,
1078                                             GNUNET_SCHEDULER_TaskCallback task,
1079                                             void *task_cls)
1080 {
1081   struct GNUNET_SCHEDULER_Task *t;
1082   struct GNUNET_SCHEDULER_Task *pos;
1083   struct GNUNET_SCHEDULER_Task *prev;
1084
1085 #if EXECINFO
1086   void *backtrace_array[MAX_TRACE_DEPTH];
1087 #endif
1088
1089   GNUNET_assert (NULL != active_task);
1090   GNUNET_assert (NULL != task);
1091   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1092   t->callback = task;
1093   t->callback_cls = task_cls;
1094 #if EXECINFO
1095   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1096   t->backtrace_strings =
1097       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1098 #endif
1099   t->read_fd = -1;
1100   t->write_fd = -1;
1101 #if PROFILE_DELAYS
1102   t->start_time = GNUNET_TIME_absolute_get ();
1103 #endif
1104   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1105   t->priority = priority;
1106   t->lifeness = current_lifeness;
1107   /* try tail first (optimization in case we are
1108    * appending to a long list of tasks with timeouts) */
1109   if (0 == delay.rel_value_us)
1110   {
1111     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1112                                  pending_timeout_tail,
1113                                  t);
1114   }
1115   else
1116   {
1117     /* first move from heuristic start backwards to before start time */
1118     prev = pending_timeout_last;
1119     while ( (NULL != prev) &&
1120             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1121       prev = prev->prev;
1122     /* now, move from heuristic start (or head of list) forward to insertion point */
1123     if (NULL == prev)
1124       pos = pending_timeout_head;
1125     else
1126       pos = prev->next;
1127     while ( (NULL != pos) &&
1128             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1129               (0 != pos->reason) ) )
1130     {
1131       prev = pos;
1132       pos = pos->next;
1133     }
1134     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1135                                        pending_timeout_tail,
1136                                        prev,
1137                                        t);
1138     /* finally, update heuristic insertion point to last insertion... */
1139     pending_timeout_last = t;
1140   }
1141
1142   LOG (GNUNET_ERROR_TYPE_DEBUG,
1143        "Adding task: %p\n",
1144        t);
1145 #if EXECINFO
1146   unsigned int i;
1147
1148   for (i = 0; i < t->num_backtrace_strings; i++)
1149     LOG (GNUNET_ERROR_TYPE_DEBUG,
1150          "Task %p trace %d: %s\n",
1151          t,
1152          i,
1153          t->backtrace_strings[i]);
1154 #endif
1155   return t;
1156 }
1157
1158
1159 /**
1160  * Schedule a new task to be run with a specified priority.
1161  *
1162  * @param prio how important is the new task?
1163  * @param task main function of the task
1164  * @param task_cls closure of @a task
1165  * @return unique task identifier for the job
1166  *         only valid until @a task is started!
1167  */
1168 struct GNUNET_SCHEDULER_Task *
1169 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1170                                     GNUNET_SCHEDULER_TaskCallback task,
1171                                     void *task_cls)
1172 {
1173   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1174                                                      prio,
1175                                                      task,
1176                                                      task_cls);
1177 }
1178
1179
1180 /**
1181  * Schedule a new task to be run with a specified delay.  The task
1182  * will be scheduled for execution once the delay has expired. It
1183  * will be run with the DEFAULT priority.
1184  *
1185  * @param delay when should this operation time out? Use
1186  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1187  * @param task main function of the task
1188  * @param task_cls closure of task
1189  * @return unique task identifier for the job
1190  *         only valid until "task" is started!
1191  */
1192 struct GNUNET_SCHEDULER_Task *
1193 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1194                               GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1195 {
1196   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1197                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1198                                                      task, task_cls);
1199 }
1200
1201
1202 /**
1203  * Schedule a new task to be run as soon as possible.  Note that this
1204  * does not guarantee that this will be the next task that is being
1205  * run, as other tasks with higher priority (or that are already ready
1206  * to run) might get to run first.  Just as with delays, clients must
1207  * not rely on any particular order of execution between tasks
1208  * scheduled concurrently.
1209  *
1210  * The task will be run with the DEFAULT priority.
1211  *
1212  * @param task main function of the task
1213  * @param task_cls closure of @a task
1214  * @return unique task identifier for the job
1215  *         only valid until "task" is started!
1216  */
1217 struct GNUNET_SCHEDULER_Task *
1218 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1219 {
1220   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO, task, task_cls);
1221 }
1222
1223
1224 /**
1225  * Schedule a new task to be run as soon as possible with the
1226  * (transitive) ignore-shutdown flag either explicitly set or
1227  * explicitly enabled.  This task (and all tasks created from it,
1228  * other than by another call to this function) will either count or
1229  * not count for the "lifeness" of the process.  This API is only
1230  * useful in a few special cases.
1231  *
1232  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1233  * @param task main function of the task
1234  * @param task_cls closure of @a task
1235  * @return unique task identifier for the job
1236  *         only valid until @a task is started!
1237  */
1238 struct GNUNET_SCHEDULER_Task *
1239 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1240                                         GNUNET_SCHEDULER_TaskCallback task,
1241                                         void *task_cls)
1242 {
1243   struct GNUNET_SCHEDULER_Task *ret;
1244
1245   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1246   ret->lifeness = lifeness;
1247   return ret;
1248 }
1249
1250
1251 /**
1252  * Schedule a new task to be run with a specified delay or when any of
1253  * the specified file descriptor sets is ready.  The delay can be used
1254  * as a timeout on the socket(s) being ready.  The task will be
1255  * scheduled for execution once either the delay has expired or any of
1256  * the socket operations is ready.  This is the most general
1257  * function of the "add" family.  Note that the "prerequisite_task"
1258  * must be satisfied in addition to any of the other conditions.  In
1259  * other words, the task will be started when
1260  * <code>
1261  * (prerequisite-run)
1262  * && (delay-ready
1263  *     || any-rs-ready
1264  *     || any-ws-ready
1265  *     || shutdown-active )
1266  * </code>
1267  *
1268  * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1269  *        which means that the task will only be run after we receive SIGTERM
1270  * @param priority priority to use
1271  * @param rfd file descriptor we want to read (can be -1)
1272  * @param wfd file descriptors we want to write (can be -1)
1273  * @param task main function of the task
1274  * @param task_cls closure of @a task
1275  * @return unique task identifier for the job
1276  *         only valid until @a task is started!
1277  */
1278 #ifndef MINGW
1279 static struct GNUNET_SCHEDULER_Task *
1280 add_without_sets (struct GNUNET_TIME_Relative delay,
1281                   enum GNUNET_SCHEDULER_Priority priority,
1282                   int rfd,
1283                   int wfd,
1284                   GNUNET_SCHEDULER_TaskCallback task,
1285                   void *task_cls)
1286 {
1287   struct GNUNET_SCHEDULER_Task *t;
1288
1289 #if EXECINFO
1290   void *backtrace_array[MAX_TRACE_DEPTH];
1291 #endif
1292
1293   GNUNET_assert (NULL != active_task);
1294   GNUNET_assert (NULL != task);
1295   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1296   t->callback = task;
1297   t->callback_cls = task_cls;
1298 #if EXECINFO
1299   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1300   t->backtrace_strings =
1301       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1302 #endif
1303 #if DEBUG_FDS
1304   if (-1 != rfd)
1305   {
1306     int flags = fcntl (rfd, F_GETFD);
1307
1308     if ((flags == -1) && (errno == EBADF))
1309     {
1310       LOG (GNUNET_ERROR_TYPE_ERROR,
1311            "Got invalid file descriptor %d!\n",
1312            rfd);
1313 #if EXECINFO
1314       unsigned int i;
1315
1316       for (i = 0; i < t->num_backtrace_strings; i++)
1317         LOG (GNUNET_ERROR_TYPE_ERROR,
1318              "Trace: %s\n",
1319              t->backtrace_strings[i]);
1320 #endif
1321       GNUNET_assert (0);
1322     }
1323   }
1324   if (-1 != wfd)
1325   {
1326     int flags = fcntl (wfd, F_GETFD);
1327
1328     if (flags == -1 && errno == EBADF)
1329     {
1330       LOG (GNUNET_ERROR_TYPE_ERROR,
1331            "Got invalid file descriptor %d!\n",
1332            wfd);
1333 #if EXECINFO
1334       unsigned int i;
1335
1336       for (i = 0; i < t->num_backtrace_strings; i++)
1337         LOG (GNUNET_ERROR_TYPE_DEBUG,
1338              "Trace: %s\n",
1339              t->backtrace_strings[i]);
1340 #endif
1341       GNUNET_assert (0);
1342     }
1343   }
1344 #endif
1345   t->read_fd = rfd;
1346   GNUNET_assert (wfd >= -1);
1347   t->write_fd = wfd;
1348 #if PROFILE_DELAYS
1349   t->start_time = GNUNET_TIME_absolute_get ();
1350 #endif
1351   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1352   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1353   t->lifeness = current_lifeness;
1354   GNUNET_CONTAINER_DLL_insert (pending_head,
1355                                pending_tail,
1356                                t);
1357   max_priority_added = GNUNET_MAX (max_priority_added,
1358                                    t->priority);
1359   LOG (GNUNET_ERROR_TYPE_DEBUG,
1360        "Adding task %p\n",
1361        t);
1362 #if EXECINFO
1363   unsigned int i;
1364
1365   for (i = 0; i < t->num_backtrace_strings; i++)
1366     LOG (GNUNET_ERROR_TYPE_DEBUG,
1367          "Task %p trace %d: %s\n",
1368          t,
1369          i,
1370          t->backtrace_strings[i]);
1371 #endif
1372   return t;
1373 }
1374 #endif
1375
1376
1377 /**
1378  * Schedule a new task to be run with a specified delay or when the
1379  * specified file descriptor is ready for reading.  The delay can be
1380  * used as a timeout on the socket being ready.  The task will be
1381  * scheduled for execution once either the delay has expired or the
1382  * socket operation is ready.  It will be run with the DEFAULT priority.
1383  *
1384  * @param delay when should this operation time out? Use
1385  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1386  * @param rfd read file-descriptor
1387  * @param task main function of the task
1388  * @param task_cls closure of @a task
1389  * @return unique task identifier for the job
1390  *         only valid until @a task is started!
1391  */
1392 struct GNUNET_SCHEDULER_Task *
1393 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1394                                struct GNUNET_NETWORK_Handle *rfd,
1395                                GNUNET_SCHEDULER_TaskCallback task,
1396                                void *task_cls)
1397 {
1398   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1399                                                       GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1400                                                       rfd, task, task_cls);
1401 }
1402
1403
1404 /**
1405  * Schedule a new task to be run with a specified priority and to be
1406  * run after the specified delay or when the specified file descriptor
1407  * is ready for reading.  The delay can be used as a timeout on the
1408  * socket being ready.  The task will be scheduled for execution once
1409  * either the delay has expired or the socket operation is ready.  It
1410  * will be run with the DEFAULT priority.
1411  *
1412  * @param delay when should this operation time out? Use
1413  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1414  * @param priority priority to use for the task
1415  * @param rfd read file-descriptor
1416  * @param task main function of the task
1417  * @param task_cls closure of @a task
1418  * @return unique task identifier for the job
1419  *         only valid until @a task is started!
1420  */
1421 struct GNUNET_SCHEDULER_Task *
1422 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1423                                              enum GNUNET_SCHEDULER_Priority priority,
1424                                              struct GNUNET_NETWORK_Handle *rfd,
1425                                              GNUNET_SCHEDULER_TaskCallback task,
1426                                              void *task_cls)
1427 {
1428   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1429                                                  rfd,
1430                                                  GNUNET_YES,
1431                                                  GNUNET_NO,
1432                                                  task, task_cls);
1433 }
1434
1435
1436 /**
1437  * Schedule a new task to be run with a specified delay or when the
1438  * specified file descriptor is ready for writing.  The delay can be
1439  * used as a timeout on the socket being ready.  The task will be
1440  * scheduled for execution once either the delay has expired or the
1441  * socket operation is ready.  It will be run with the priority of
1442  * the calling task.
1443  *
1444  * @param delay when should this operation time out? Use
1445  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1446  * @param wfd write file-descriptor
1447  * @param task main function of the task
1448  * @param task_cls closure of @a task
1449  * @return unique task identifier for the job
1450  *         only valid until @a task is started!
1451  */
1452 struct GNUNET_SCHEDULER_Task *
1453 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1454                                 struct GNUNET_NETWORK_Handle *wfd,
1455                                 GNUNET_SCHEDULER_TaskCallback task,
1456                                 void *task_cls)
1457 {
1458   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1459                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1460                                                  wfd,
1461                                                  GNUNET_NO, GNUNET_YES,
1462                                                  task, task_cls);
1463 }
1464
1465 /**
1466  * Schedule a new task to be run with a specified delay or when the
1467  * specified file descriptor is ready.  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.
1471  *
1472  * @param delay when should this operation time out? Use
1473  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1474  * @param priority priority of the task
1475  * @param fd file-descriptor
1476  * @param on_read whether to poll the file-descriptor for readability
1477  * @param on_write whether to poll the file-descriptor for writability
1478  * @param task main function of the task
1479  * @param task_cls closure of task
1480  * @return unique task identifier for the job
1481  *         only valid until "task" is started!
1482  */
1483 struct GNUNET_SCHEDULER_Task *
1484 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1485                                          enum GNUNET_SCHEDULER_Priority priority,
1486                                          struct GNUNET_NETWORK_Handle *fd,
1487                                          int on_read,
1488                                          int on_write,
1489                                          GNUNET_SCHEDULER_TaskCallback task,
1490                                          void *task_cls)
1491 {
1492 #if MINGW
1493   struct GNUNET_NETWORK_FDSet *s;
1494   struct GNUNET_SCHEDULER_Task * ret;
1495
1496   GNUNET_assert (NULL != fd);
1497   s = GNUNET_NETWORK_fdset_create ();
1498   GNUNET_NETWORK_fdset_set (s, fd);
1499   ret = GNUNET_SCHEDULER_add_select (
1500       priority, delay,
1501       on_read  ? s : NULL,
1502       on_write ? s : NULL,
1503       task, task_cls);
1504   GNUNET_NETWORK_fdset_destroy (s);
1505   return ret;
1506 #else
1507   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1508   return add_without_sets (delay, priority,
1509                            on_read  ? GNUNET_NETWORK_get_fd (fd) : -1,
1510                            on_write ? GNUNET_NETWORK_get_fd (fd) : -1,
1511                            task, task_cls);
1512 #endif
1513 }
1514
1515
1516 /**
1517  * Schedule a new task to be run with a specified delay or when the
1518  * specified file descriptor is ready for reading.  The delay can be
1519  * used as a timeout on the socket being ready.  The task will be
1520  * scheduled for execution once either the delay has expired or the
1521  * socket operation is ready. It will be run with the DEFAULT priority.
1522  *
1523  * @param delay when should this operation time out? Use
1524  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1525  * @param rfd read file-descriptor
1526  * @param task main function of the task
1527  * @param task_cls closure of @a task
1528  * @return unique task identifier for the job
1529  *         only valid until @a task is started!
1530  */
1531 struct GNUNET_SCHEDULER_Task *
1532 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1533                                 const struct GNUNET_DISK_FileHandle *rfd,
1534                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1535 {
1536   return GNUNET_SCHEDULER_add_file_with_priority (
1537       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1538       rfd, GNUNET_YES, GNUNET_NO,
1539       task, task_cls);
1540 }
1541
1542
1543 /**
1544  * Schedule a new task to be run with a specified delay or when the
1545  * specified file descriptor is ready for writing.  The delay can be
1546  * used as a timeout on the socket being ready.  The task will be
1547  * scheduled for execution once either the delay has expired or the
1548  * socket operation is ready. It will be run with the DEFAULT priority.
1549  *
1550  * @param delay when should this operation time out? Use
1551  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1552  * @param wfd write file-descriptor
1553  * @param task main function of the task
1554  * @param task_cls closure of @a task
1555  * @return unique task identifier for the job
1556  *         only valid until @a task is started!
1557  */
1558 struct GNUNET_SCHEDULER_Task *
1559 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1560                                  const struct GNUNET_DISK_FileHandle *wfd,
1561                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1562 {
1563   return GNUNET_SCHEDULER_add_file_with_priority (
1564       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1565       wfd, GNUNET_NO, GNUNET_YES,
1566       task, task_cls);
1567 }
1568
1569
1570 /**
1571  * Schedule a new task to be run with a specified delay or when the
1572  * specified file descriptor is ready.  The delay can be
1573  * used as a timeout on the socket being ready.  The task will be
1574  * scheduled for execution once either the delay has expired or the
1575  * socket operation is ready.
1576  *
1577  * @param delay when should this operation time out? Use
1578  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1579  * @param priority priority of the task
1580  * @param fd file-descriptor
1581  * @param on_read whether to poll the file-descriptor for readability
1582  * @param on_write whether to poll the file-descriptor for writability
1583  * @param task main function of the task
1584  * @param task_cls closure of @a task
1585  * @return unique task identifier for the job
1586  *         only valid until @a task is started!
1587  */
1588 struct GNUNET_SCHEDULER_Task *
1589 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1590                                          enum GNUNET_SCHEDULER_Priority priority,
1591                                          const struct GNUNET_DISK_FileHandle *fd,
1592                                          int on_read, int on_write,
1593                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1594 {
1595 #if MINGW
1596   struct GNUNET_NETWORK_FDSet *s;
1597   struct GNUNET_SCHEDULER_Task * ret;
1598
1599   GNUNET_assert (NULL != fd);
1600   s = GNUNET_NETWORK_fdset_create ();
1601   GNUNET_NETWORK_fdset_handle_set (s, fd);
1602   ret = GNUNET_SCHEDULER_add_select (
1603       priority, delay,
1604       on_read  ? s : NULL,
1605       on_write ? s : NULL,
1606       task, task_cls);
1607   GNUNET_NETWORK_fdset_destroy (s);
1608   return ret;
1609 #else
1610   int real_fd;
1611
1612   GNUNET_DISK_internal_file_handle_ (fd, &real_fd, sizeof (int));
1613   GNUNET_assert (real_fd >= 0);
1614   return add_without_sets (
1615       delay, priority,
1616       on_read  ? real_fd : -1,
1617       on_write ? real_fd : -1,
1618       task, task_cls);
1619 #endif
1620 }
1621
1622
1623 /**
1624  * Schedule a new task to be run with a specified delay or when any of
1625  * the specified file descriptor sets is ready.  The delay can be used
1626  * as a timeout on the socket(s) being ready.  The task will be
1627  * scheduled for execution once either the delay has expired or any of
1628  * the socket operations is ready.  This is the most general
1629  * function of the "add" family.  Note that the "prerequisite_task"
1630  * must be satisfied in addition to any of the other conditions.  In
1631  * other words, the task will be started when
1632  * <code>
1633  * (prerequisite-run)
1634  * && (delay-ready
1635  *     || any-rs-ready
1636  *     || any-ws-ready
1637  *     || (shutdown-active && run-on-shutdown) )
1638  * </code>
1639  *
1640  * @param prio how important is this task?
1641  * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1642  *        which means that the task will only be run after we receive SIGTERM
1643  * @param rs set of file descriptors we want to read (can be NULL)
1644  * @param ws set of file descriptors we want to write (can be NULL)
1645  * @param task main function of the task
1646  * @param task_cls closure of @a task
1647  * @return unique task identifier for the job
1648  *         only valid until @a task is started!
1649  */
1650 struct GNUNET_SCHEDULER_Task *
1651 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1652                              struct GNUNET_TIME_Relative delay,
1653                              const struct GNUNET_NETWORK_FDSet *rs,
1654                              const struct GNUNET_NETWORK_FDSet *ws,
1655                              GNUNET_SCHEDULER_TaskCallback task,
1656                              void *task_cls)
1657 {
1658   struct GNUNET_SCHEDULER_Task *t;
1659 #if EXECINFO
1660   void *backtrace_array[MAX_TRACE_DEPTH];
1661 #endif
1662
1663   if ( (NULL == rs) &&
1664        (NULL == ws) )
1665     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1666                                                        prio,
1667                                                        task,
1668                                                        task_cls);
1669   GNUNET_assert (NULL != active_task);
1670   GNUNET_assert (NULL != task);
1671   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1672   t->callback = task;
1673   t->callback_cls = task_cls;
1674 #if EXECINFO
1675   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1676   t->backtrace_strings =
1677       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1678 #endif
1679   t->read_fd = -1;
1680   t->write_fd = -1;
1681   if (NULL != rs)
1682   {
1683     t->read_set = GNUNET_NETWORK_fdset_create ();
1684     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1685   }
1686   if (NULL != ws)
1687   {
1688     t->write_set = GNUNET_NETWORK_fdset_create ();
1689     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1690   }
1691 #if PROFILE_DELAYS
1692   t->start_time = GNUNET_TIME_absolute_get ();
1693 #endif
1694   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1695   t->priority =
1696       check_priority ((prio ==
1697                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1698                       prio);
1699   t->lifeness = current_lifeness;
1700   GNUNET_CONTAINER_DLL_insert (pending_head,
1701                                pending_tail,
1702                                t);
1703   max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
1704   LOG (GNUNET_ERROR_TYPE_DEBUG,
1705        "Adding task %p\n",
1706        t);
1707 #if EXECINFO
1708   int i;
1709
1710   for (i = 0; i < t->num_backtrace_strings; i++)
1711     LOG (GNUNET_ERROR_TYPE_DEBUG,
1712          "Task p trace %d: %s\n",
1713          t,
1714          i,
1715          t->backtrace_strings[i]);
1716 #endif
1717   return t;
1718 }
1719
1720 /* end of scheduler.c */