remove debug logic
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2009-2017 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file util/scheduler.c
22  * @brief schedule computations using continuation passing style
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "disk.h"
28
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
30
31 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
32
33
34 #if HAVE_EXECINFO_H
35 #include "execinfo.h"
36
37 /**
38  * Use lsof to generate file descriptor reports on select error?
39  * (turn off for stable releases).
40  */
41 #define USE_LSOF GNUNET_NO
42
43 /**
44  * Obtain trace information for all scheduler calls that schedule tasks.
45  */
46 #define EXECINFO GNUNET_NO
47
48 /**
49  * Check each file descriptor before adding
50  */
51 #define DEBUG_FDS GNUNET_NO
52
53 /**
54  * Depth of the traces collected via EXECINFO.
55  */
56 #define MAX_TRACE_DEPTH 50
57 #endif
58
59 /**
60  * Should we figure out which tasks are delayed for a while
61  * before they are run? (Consider using in combination with EXECINFO).
62  */
63 #define PROFILE_DELAYS GNUNET_NO
64
65 /**
66  * Task that were in the queue for longer than this are reported if
67  * PROFILE_DELAYS is active.
68  */
69 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
70
71
72 /**
73  * Argument to be passed from the driver to
74  * #GNUNET_SCHEDULER_run_from_driver().  Contains the
75  * scheduler's internal state.
76  */
77 struct GNUNET_SCHEDULER_Handle
78 {
79   /**
80    * Passed here to avoid constantly allocating/deallocating
81    * this element, but generally we want to get rid of this.
82    * @deprecated
83    */
84   struct GNUNET_NETWORK_FDSet *rs;
85
86   /**
87    * Passed here to avoid constantly allocating/deallocating
88    * this element, but generally we want to get rid of this.
89    * @deprecated
90    */
91   struct GNUNET_NETWORK_FDSet *ws;
92
93   /**
94    * Driver we used for the event loop.
95    */
96   const struct GNUNET_SCHEDULER_Driver *driver;
97
98 };
99
100
101 /**
102  * Entry in list of pending tasks.
103  */
104 struct GNUNET_SCHEDULER_Task
105 {
106   /**
107    * This is a linked list.
108    */
109   struct GNUNET_SCHEDULER_Task *next;
110
111   /**
112    * This is a linked list.
113    */
114   struct GNUNET_SCHEDULER_Task *prev;
115
116   /**
117    * Function to run when ready.
118    */
119   GNUNET_SCHEDULER_TaskCallback callback;
120
121   /**
122    * Closure for the @e callback.
123    */
124   void *callback_cls;
125
126   /**
127    * Handle to the scheduler's state.
128    */
129   const struct GNUNET_SCHEDULER_Handle *sh;
130
131   /**
132    * Set of file descriptors this task is waiting
133    * for for reading.  Once ready, this is updated
134    * to reflect the set of file descriptors ready
135    * for operation.
136    */
137   struct GNUNET_NETWORK_FDSet *read_set;
138
139   /**
140    * Set of file descriptors this task is waiting for for writing.
141    * Once ready, this is updated to reflect the set of file
142    * descriptors ready for operation.
143    */
144   struct GNUNET_NETWORK_FDSet *write_set;
145
146   /**
147    * Information about which FDs are ready for this task (and why).
148    */
149   const struct GNUNET_SCHEDULER_FdInfo *fds;
150
151   /**
152    * Storage location used for @e fds if we want to avoid
153    * a separate malloc() call in the common case that this
154    * task is only about a single FD.
155    */
156   struct GNUNET_SCHEDULER_FdInfo fdx;
157
158   /**
159    * Absolute timeout value for the task, or
160    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
161    */
162   struct GNUNET_TIME_Absolute timeout;
163
164 #if PROFILE_DELAYS
165   /**
166    * When was the task scheduled?
167    */
168   struct GNUNET_TIME_Absolute start_time;
169 #endif
170
171   /**
172    * Size of the @e fds array.
173    */
174   unsigned int fds_len;
175
176   /**
177    * Why is the task ready?  Set after task is added to ready queue.
178    * Initially set to zero.  All reasons that have already been
179    * satisfied (i.e.  read or write ready) will be set over time.
180    */
181   enum GNUNET_SCHEDULER_Reason reason;
182
183   /**
184    * Task priority.
185    */
186   enum GNUNET_SCHEDULER_Priority priority;
187
188   /**
189    * Set if we only wait for reading from a single FD, otherwise -1.
190    */
191   int read_fd;
192
193   /**
194    * Set if we only wait for writing to a single FD, otherwise -1.
195    */
196   int write_fd;
197
198   /**
199    * Should the existence of this task in the queue be counted as
200    * reason to not shutdown the scheduler?
201    */
202   int lifeness;
203
204   /**
205    * Is this task run on shutdown?
206    */
207   int on_shutdown;
208
209   /**
210    * Is this task in the ready list?
211    */
212   int in_ready_list;
213
214 #if EXECINFO
215   /**
216    * Array of strings which make up a backtrace from the point when this
217    * task was scheduled (essentially, who scheduled the task?)
218    */
219   char **backtrace_strings;
220
221   /**
222    * Size of the backtrace_strings array
223    */
224   int num_backtrace_strings;
225 #endif
226
227
228 };
229
230
231 /**
232  * Head of list of tasks waiting for an event.
233  */
234 static struct GNUNET_SCHEDULER_Task *pending_head;
235
236 /**
237  * Tail of list of tasks waiting for an event.
238  */
239 static struct GNUNET_SCHEDULER_Task *pending_tail;
240
241 /**
242  * Head of list of tasks waiting for shutdown.
243  */
244 static struct GNUNET_SCHEDULER_Task *shutdown_head;
245
246 /**
247  * Tail of list of tasks waiting for shutdown.
248  */
249 static struct GNUNET_SCHEDULER_Task *shutdown_tail;
250
251 /**
252  * List of tasks waiting ONLY for a timeout event.
253  * Sorted by timeout (earliest first).  Used so that
254  * we do not traverse the list of these tasks when
255  * building select sets (we just look at the head
256  * to determine the respective timeout ONCE).
257  */
258 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
259
260 /**
261  * List of tasks waiting ONLY for a timeout event.
262  * Sorted by timeout (earliest first).  Used so that
263  * we do not traverse the list of these tasks when
264  * building select sets (we just look at the head
265  * to determine the respective timeout ONCE).
266  */
267 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
268
269 /**
270  * Last inserted task waiting ONLY for a timeout event.
271  * Used to (heuristically) speed up insertion.
272  */
273 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
274
275 /**
276  * ID of the task that is running right now.
277  */
278 static struct GNUNET_SCHEDULER_Task *active_task;
279
280 /**
281  * Head of list of tasks ready to run right now, grouped by importance.
282  */
283 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
284
285 /**
286  * Tail of list of tasks ready to run right now, grouped by importance.
287  */
288 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
289
290 /**
291  * Number of tasks on the ready list.
292  */
293 static unsigned int ready_count;
294
295 /**
296  * How many tasks have we run so far?
297  */
298 static unsigned long long tasks_run;
299
300 /**
301  * Priority of the task running right now.  Only
302  * valid while a task is running.
303  */
304 static enum GNUNET_SCHEDULER_Priority current_priority;
305
306 /**
307  * Priority of the highest task added in the current select
308  * iteration.
309  */
310 static enum GNUNET_SCHEDULER_Priority max_priority_added;
311
312 /**
313  * Value of the 'lifeness' flag for the current task.
314  */
315 static int current_lifeness;
316
317 /**
318  * Function to use as a select() in the scheduler.
319  * If NULL, we use GNUNET_NETWORK_socket_select().
320  */
321 static GNUNET_SCHEDULER_select scheduler_select;
322
323 /**
324  * Task context of the current task.
325  */
326 static struct GNUNET_SCHEDULER_TaskContext tc;
327
328 /**
329  * Closure for #scheduler_select.
330  */
331 static void *scheduler_select_cls;
332
333
334 /**
335  * Sets the select function to use in the scheduler (scheduler_select).
336  *
337  * @param new_select new select function to use
338  * @param new_select_cls closure for @a new_select
339  * @return previously used select function, NULL for default
340  */
341 void
342 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
343                              void *new_select_cls)
344 {
345   scheduler_select = new_select;
346   scheduler_select_cls = new_select_cls;
347 }
348
349
350 /**
351  * Check that the given priority is legal (and return it).
352  *
353  * @param p priority value to check
354  * @return p on success, 0 on error
355  */
356 static enum GNUNET_SCHEDULER_Priority
357 check_priority (enum GNUNET_SCHEDULER_Priority p)
358 {
359   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
360     return p;
361   GNUNET_assert (0);
362   return 0;                     /* make compiler happy */
363 }
364
365
366 /**
367  * Update all sets and timeout for select.
368  *
369  * @param rs read-set, set to all FDs we would like to read (updated)
370  * @param ws write-set, set to all FDs we would like to write (updated)
371  * @param timeout next timeout (updated)
372  */
373 static void
374 update_sets (struct GNUNET_NETWORK_FDSet *rs,
375              struct GNUNET_NETWORK_FDSet *ws,
376              struct GNUNET_TIME_Relative *timeout)
377 {
378   struct GNUNET_SCHEDULER_Task *pos;
379   struct GNUNET_TIME_Absolute now;
380   struct GNUNET_TIME_Relative to;
381
382   now = GNUNET_TIME_absolute_get ();
383   pos = pending_timeout_head;
384   if (NULL != pos)
385   {
386     to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
387     if (timeout->rel_value_us > to.rel_value_us)
388       *timeout = to;
389     if (0 != pos->reason)
390       *timeout = GNUNET_TIME_UNIT_ZERO;
391   }
392   for (pos = pending_head; NULL != pos; pos = pos->next)
393   {
394     if (pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
395     {
396       to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
397       if (timeout->rel_value_us > to.rel_value_us)
398         *timeout = to;
399     }
400     if (-1 != pos->read_fd)
401       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
402     if (-1 != pos->write_fd)
403       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
404     if (NULL != pos->read_set)
405       GNUNET_NETWORK_fdset_add (rs, pos->read_set);
406     if (NULL != pos->write_set)
407       GNUNET_NETWORK_fdset_add (ws, pos->write_set);
408     if (0 != pos->reason)
409       *timeout = GNUNET_TIME_UNIT_ZERO;
410   }
411 }
412
413
414 /**
415  * Check if the ready set overlaps with the set we want to have ready.
416  * If so, update the want set (set all FDs that are ready).  If not,
417  * return #GNUNET_NO.
418  *
419  * @param ready set that is ready
420  * @param want set that we want to be ready
421  * @return #GNUNET_YES if there was some overlap
422  */
423 static int
424 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
425               struct GNUNET_NETWORK_FDSet *want)
426 {
427   if ((NULL == want) || (NULL == ready))
428     return GNUNET_NO;
429   if (GNUNET_NETWORK_fdset_overlap (ready, want))
430   {
431     /* copy all over (yes, there maybe unrelated bits,
432      * but this should not hurt well-written clients) */
433     GNUNET_NETWORK_fdset_copy (want, ready);
434     return GNUNET_YES;
435   }
436   return GNUNET_NO;
437 }
438
439
440 /**
441  * Check if the given task is eligible to run now.
442  * Also set the reason why it is eligible.
443  *
444  * @param task task to check if it is ready
445  * @param now the current time
446  * @param rs set of FDs ready for reading
447  * @param ws set of FDs ready for writing
448  * @return #GNUNET_YES if we can run it, #GNUNET_NO if not.
449  */
450 static int
451 is_ready (struct GNUNET_SCHEDULER_Task *task,
452           struct GNUNET_TIME_Absolute now,
453           const struct GNUNET_NETWORK_FDSet *rs,
454           const struct GNUNET_NETWORK_FDSet *ws)
455 {
456   enum GNUNET_SCHEDULER_Reason reason;
457
458   reason = task->reason;
459   if (now.abs_value_us >= task->timeout.abs_value_us)
460     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
461   if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
462       (((task->read_fd != -1) &&
463         (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd))) ||
464        (set_overlaps (rs, task->read_set))))
465     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
466   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
467       (((task->write_fd != -1) &&
468         (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)))
469        || (set_overlaps (ws, task->write_set))))
470     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
471   if (0 == reason)
472     return GNUNET_NO;           /* not ready */
473   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
474   task->reason = reason;
475   return GNUNET_YES;
476 }
477
478
479 /**
480  * Put a task that is ready for execution into the ready queue.
481  *
482  * @param task task ready for execution
483  */
484 static void
485 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
486 {
487   enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
488
489   GNUNET_CONTAINER_DLL_insert (ready_head[p],
490                                ready_tail[p],
491                                task);
492   task->in_ready_list = GNUNET_YES;
493   ready_count++;
494 }
495
496
497 /**
498  * Check which tasks are ready and move them
499  * to the respective ready queue.
500  *
501  * @param rs FDs ready for reading
502  * @param ws FDs ready for writing
503  */
504 static void
505 check_ready (const struct GNUNET_NETWORK_FDSet *rs,
506              const struct GNUNET_NETWORK_FDSet *ws)
507 {
508   struct GNUNET_SCHEDULER_Task *pos;
509   struct GNUNET_SCHEDULER_Task *next;
510   struct GNUNET_TIME_Absolute now;
511
512   now = GNUNET_TIME_absolute_get ();
513   while (NULL != (pos = pending_timeout_head))
514   {
515     if (now.abs_value_us >= pos->timeout.abs_value_us)
516       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
517     if (0 == pos->reason)
518       break;
519     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
520                                  pending_timeout_tail,
521                                  pos);
522     if (pending_timeout_last == pos)
523       pending_timeout_last = NULL;
524     queue_ready_task (pos);
525   }
526   pos = pending_head;
527   while (NULL != pos)
528   {
529     next = pos->next;
530     if (GNUNET_YES == is_ready (pos, now, rs, ws))
531     {
532       GNUNET_CONTAINER_DLL_remove (pending_head,
533                                    pending_tail,
534                                    pos);
535       queue_ready_task (pos);
536     }
537     pos = next;
538   }
539 }
540
541
542 /**
543  * Request the shutdown of a scheduler.  Marks all tasks
544  * awaiting shutdown as ready. Note that tasks
545  * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
546  * will be delayed until the next shutdown signal.
547  */
548 void
549 GNUNET_SCHEDULER_shutdown ()
550 {
551   struct GNUNET_SCHEDULER_Task *pos;
552
553   while (NULL != (pos = shutdown_head))
554   {
555     GNUNET_CONTAINER_DLL_remove (shutdown_head,
556                                  shutdown_tail,
557                                  pos);
558     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
559     queue_ready_task (pos);
560   }
561 }
562
563
564 /**
565  * Destroy a task (release associated resources)
566  *
567  * @param t task to destroy
568  */
569 static void
570 destroy_task (struct GNUNET_SCHEDULER_Task *t)
571 {
572   if (NULL != t->read_set)
573     GNUNET_NETWORK_fdset_destroy (t->read_set);
574   if (NULL != t->write_set)
575     GNUNET_NETWORK_fdset_destroy (t->write_set);
576 #if EXECINFO
577   GNUNET_free (t->backtrace_strings);
578 #endif
579   GNUNET_free (t);
580 }
581
582
583 /**
584  * Output stack trace of task @a t.
585  *
586  * @param t task to dump stack trace of
587  */
588 static void
589 dump_backtrace (struct GNUNET_SCHEDULER_Task *t)
590 {
591 #if EXECINFO
592   unsigned int i;
593
594   for (i = 0; i < t->num_backtrace_strings; i++)
595     LOG (GNUNET_ERROR_TYPE_DEBUG,
596          "Task %p trace %u: %s\n",
597          t,
598          i,
599          t->backtrace_strings[i]);
600 #endif
601 }
602
603
604 /**
605  * Run at least one task in the highest-priority queue that is not
606  * empty.  Keep running tasks until we are either no longer running
607  * "URGENT" tasks or until we have at least one "pending" task (which
608  * may become ready, hence we should select on it).  Naturally, if
609  * there are no more ready tasks, we also return.
610  *
611  * @param rs FDs ready for reading
612  * @param ws FDs ready for writing
613  */
614 static void
615 run_ready (struct GNUNET_NETWORK_FDSet *rs,
616            struct GNUNET_NETWORK_FDSet *ws)
617 {
618   enum GNUNET_SCHEDULER_Priority p;
619   struct GNUNET_SCHEDULER_Task *pos;
620
621   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
622   do
623   {
624     if (0 == ready_count)
625       return;
626     GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
627     /* yes, p>0 is correct, 0 is "KEEP" which should
628      * always be an empty queue (see assertion)! */
629     for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
630     {
631       pos = ready_head[p];
632       if (NULL != pos)
633         break;
634     }
635     GNUNET_assert (NULL != pos);        /* ready_count wrong? */
636     GNUNET_CONTAINER_DLL_remove (ready_head[p],
637                                  ready_tail[p],
638                                  pos);
639     ready_count--;
640     current_priority = pos->priority;
641     current_lifeness = pos->lifeness;
642     active_task = pos;
643 #if PROFILE_DELAYS
644     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
645         DELAY_THRESHOLD.rel_value_us)
646     {
647       LOG (GNUNET_ERROR_TYPE_DEBUG,
648            "Task %p took %s to be scheduled\n",
649            pos,
650            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
651                                                    GNUNET_YES));
652     }
653 #endif
654     tc.reason = pos->reason;
655     tc.read_ready = (NULL == pos->read_set) ? rs : pos->read_set;
656     if ((-1 != pos->read_fd) &&
657         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)))
658       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
659     tc.write_ready = (NULL == pos->write_set) ? ws : pos->write_set;
660     if ((-1 != pos->write_fd) &&
661         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
662       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
663     if ((0 != (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
664         (-1 != pos->write_fd) &&
665         (!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
666       GNUNET_assert (0);          // added to ready in previous select loop!
667     LOG (GNUNET_ERROR_TYPE_DEBUG,
668          "Running task: %p\n",
669          pos);
670     pos->callback (pos->callback_cls);
671     dump_backtrace (pos);
672     active_task = NULL;
673     destroy_task (pos);
674     tasks_run++;
675   }
676   while ((NULL == pending_head) || (p >= max_priority_added));
677 }
678
679
680 /**
681  * Pipe used to communicate shutdown via signal.
682  */
683 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
684
685 /**
686  * Process ID of this process at the time we installed the various
687  * signal handlers.
688  */
689 static pid_t my_pid;
690
691 /**
692  * Signal handler called for SIGPIPE.
693  */
694 #ifndef MINGW
695 static void
696 sighandler_pipe ()
697 {
698   return;
699 }
700 #endif
701
702
703 /**
704  * Wait for a short time.
705  * Sleeps for @a ms ms (as that should be long enough for virtually all
706  * modern systems to context switch and allow another process to do
707  * some 'real' work).
708  *
709  * @param ms how many ms to wait
710  */
711 static void
712 short_wait (unsigned int ms)
713 {
714   struct GNUNET_TIME_Relative timeout;
715
716   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
717   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
718 }
719
720
721 /**
722  * Signal handler called for signals that should cause us to shutdown.
723  */
724 static void
725 sighandler_shutdown ()
726 {
727   static char c;
728   int old_errno = errno;        /* backup errno */
729
730   if (getpid () != my_pid)
731     exit (1);                   /* we have fork'ed since the signal handler was created,
732                                  * ignore the signal, see https://gnunet.org/vfork discussion */
733   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
734                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
735                           &c, sizeof (c));
736   errno = old_errno;
737 }
738
739
740 /**
741  * Check if the system is still alive. Trigger shutdown if we
742  * have tasks, but none of them give us lifeness.
743  *
744  * @return #GNUNET_OK to continue the main loop,
745  *         #GNUNET_NO to exit
746  */
747 static int
748 check_lifeness ()
749 {
750   struct GNUNET_SCHEDULER_Task *t;
751
752   if (ready_count > 0)
753     return GNUNET_OK;
754   for (t = pending_head; NULL != t; t = t->next)
755     if (t->lifeness == GNUNET_YES)
756       return GNUNET_OK;
757   for (t = shutdown_head; NULL != t; t = t->next)
758     if (t->lifeness == GNUNET_YES)
759       return GNUNET_OK;
760   for (t = pending_timeout_head; NULL != t; t = t->next)
761     if (t->lifeness == GNUNET_YES)
762       return GNUNET_OK;
763   if (NULL != shutdown_head)
764   {
765     GNUNET_SCHEDULER_shutdown ();
766     return GNUNET_OK;
767   }
768   return GNUNET_NO;
769 }
770
771
772 /**
773  * Initialize and run scheduler.  This function will return when all
774  * tasks have completed.  On systems with signals, receiving a SIGTERM
775  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
776  * to be run after the active task is complete.  As a result, SIGTERM
777  * causes all active tasks to be scheduled with reason
778  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
779  * afterwards will execute normally!). Note that any particular signal
780  * will only shut down one scheduler; applications should always only
781  * create a single scheduler.
782  *
783  * @param task task to run immediately
784  * @param task_cls closure of @a task
785  */
786 void
787 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
788                       void *task_cls)
789 {
790   struct GNUNET_NETWORK_FDSet *rs;
791   struct GNUNET_NETWORK_FDSet *ws;
792   struct GNUNET_TIME_Relative timeout;
793   int ret;
794   struct GNUNET_SIGNAL_Context *shc_int;
795   struct GNUNET_SIGNAL_Context *shc_term;
796 #if (SIGTERM != GNUNET_TERM_SIG)
797   struct GNUNET_SIGNAL_Context *shc_gterm;
798 #endif
799
800 #ifndef MINGW
801   struct GNUNET_SIGNAL_Context *shc_quit;
802   struct GNUNET_SIGNAL_Context *shc_hup;
803   struct GNUNET_SIGNAL_Context *shc_pipe;
804 #endif
805   unsigned long long last_tr;
806   unsigned int busy_wait_warning;
807   const struct GNUNET_DISK_FileHandle *pr;
808   char c;
809
810   GNUNET_assert (NULL == active_task);
811   rs = GNUNET_NETWORK_fdset_create ();
812   ws = GNUNET_NETWORK_fdset_create ();
813   GNUNET_assert (NULL == shutdown_pipe_handle);
814   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
815                                            GNUNET_NO,
816                                            GNUNET_NO,
817                                            GNUNET_NO);
818   GNUNET_assert (NULL != shutdown_pipe_handle);
819   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
820                                 GNUNET_DISK_PIPE_END_READ);
821   GNUNET_assert (NULL != pr);
822   my_pid = getpid ();
823   LOG (GNUNET_ERROR_TYPE_DEBUG,
824        "Registering signal handlers\n");
825   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
826                                            &sighandler_shutdown);
827   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
828                                             &sighandler_shutdown);
829 #if (SIGTERM != GNUNET_TERM_SIG)
830   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
831                                              &sighandler_shutdown);
832 #endif
833 #ifndef MINGW
834   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
835                                             &sighandler_pipe);
836   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
837                                             &sighandler_shutdown);
838   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
839                                            &sighandler_shutdown);
840 #endif
841   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
842   current_lifeness = GNUNET_YES;
843   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
844                                                  task_cls,
845                                                  GNUNET_SCHEDULER_REASON_STARTUP,
846                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
847   active_task = (void *) (long) -1;     /* force passing of sanity check */
848   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
849                                           &GNUNET_OS_install_parent_control_handler,
850                                           NULL);
851   active_task = NULL;
852   last_tr = 0;
853   busy_wait_warning = 0;
854   while (GNUNET_OK == check_lifeness ())
855   {
856     GNUNET_NETWORK_fdset_zero (rs);
857     GNUNET_NETWORK_fdset_zero (ws);
858     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
859     update_sets (rs, ws, &timeout);
860     GNUNET_NETWORK_fdset_handle_set (rs, pr);
861     if (ready_count > 0)
862     {
863       /* no blocking, more work already ready! */
864       timeout = GNUNET_TIME_UNIT_ZERO;
865     }
866     if (NULL == scheduler_select)
867       ret = GNUNET_NETWORK_socket_select (rs,
868                                           ws,
869                                           NULL,
870                                           timeout);
871     else
872       ret = scheduler_select (scheduler_select_cls,
873                               rs,
874                               ws,
875                               NULL,
876                               timeout);
877     if (ret == GNUNET_SYSERR)
878     {
879       if (errno == EINTR)
880         continue;
881
882       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
883 #ifndef MINGW
884 #if USE_LSOF
885       char lsof[512];
886
887       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
888       (void) close (1);
889       (void) dup2 (2, 1);
890       if (0 != system (lsof))
891         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
892                       "system");
893 #endif
894 #endif
895 #if DEBUG_FDS
896       struct GNUNET_SCHEDULER_Task *t;
897
898       for (t = pending_head; NULL != t; t = t->next)
899       {
900         if (-1 != t->read_fd)
901         {
902           int flags = fcntl (t->read_fd, F_GETFD);
903           if ((flags == -1) && (errno == EBADF))
904             {
905               LOG (GNUNET_ERROR_TYPE_ERROR,
906                    "Got invalid file descriptor %d!\n",
907                    t->read_fd);
908               dump_backtrace (t);
909             }
910         }
911         if (-1 != t->write_fd)
912           {
913             int flags = fcntl (t->write_fd, F_GETFD);
914             if ((flags == -1) && (errno == EBADF))
915               {
916                 LOG (GNUNET_ERROR_TYPE_ERROR,
917                      "Got invalid file descriptor %d!\n",
918                      t->write_fd);
919                 dump_backtrace (t);
920               }
921           }
922       }
923 #endif
924       GNUNET_assert (0);
925       break;
926     }
927
928     if ( (0 == ret) &&
929          (0 == timeout.rel_value_us) &&
930          (busy_wait_warning > 16) )
931     {
932       LOG (GNUNET_ERROR_TYPE_WARNING,
933            "Looks like we're busy waiting...\n");
934       short_wait (100);                /* mitigate */
935     }
936     check_ready (rs, ws);
937     run_ready (rs, ws);
938     if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
939     {
940       /* consume the signal */
941       GNUNET_DISK_file_read (pr, &c, sizeof (c));
942       /* mark all active tasks as ready due to shutdown */
943       GNUNET_SCHEDULER_shutdown ();
944     }
945     if (last_tr == tasks_run)
946     {
947       short_wait (1);
948       busy_wait_warning++;
949     }
950     else
951     {
952       last_tr = tasks_run;
953       busy_wait_warning = 0;
954     }
955   }
956   GNUNET_SIGNAL_handler_uninstall (shc_int);
957   GNUNET_SIGNAL_handler_uninstall (shc_term);
958 #if (SIGTERM != GNUNET_TERM_SIG)
959   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
960 #endif
961 #ifndef MINGW
962   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
963   GNUNET_SIGNAL_handler_uninstall (shc_quit);
964   GNUNET_SIGNAL_handler_uninstall (shc_hup);
965 #endif
966   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
967   shutdown_pipe_handle = NULL;
968   GNUNET_NETWORK_fdset_destroy (rs);
969   GNUNET_NETWORK_fdset_destroy (ws);
970 }
971
972
973 /**
974  * Obtain the task context, giving the reason why the current task was
975  * started.
976  *
977  * @return current tasks' scheduler context
978  */
979 const struct GNUNET_SCHEDULER_TaskContext *
980 GNUNET_SCHEDULER_get_task_context ()
981 {
982   GNUNET_assert (NULL != active_task);
983   return &tc;
984 }
985
986
987 /**
988  * Get information about the current load of this scheduler.  Use this
989  * function to determine if an elective task should be added or simply
990  * dropped (if the decision should be made based on the number of
991  * tasks ready to run).
992  *
993  * @param p priority level to look at
994  * @return number of tasks pending right now
995  */
996 unsigned int
997 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
998 {
999   struct GNUNET_SCHEDULER_Task *pos;
1000   unsigned int ret;
1001
1002   GNUNET_assert (NULL != active_task);
1003   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
1004     return ready_count;
1005   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
1006     p = current_priority;
1007   ret = 0;
1008   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
1009     ret++;
1010   return ret;
1011 }
1012
1013
1014 /**
1015  * Cancel the task with the specified identifier.
1016  * The task must not yet have run.
1017  *
1018  * @param task id of the task to cancel
1019  * @return original closure of the task
1020  */
1021 void *
1022 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
1023 {
1024   enum GNUNET_SCHEDULER_Priority p;
1025   void *ret;
1026
1027   GNUNET_assert ( (NULL != active_task) ||
1028                   (GNUNET_NO == task->lifeness) );
1029   if (! task->in_ready_list)
1030   {
1031     if ( (-1 == task->read_fd) &&
1032          (-1 == task->write_fd) &&
1033          (NULL == task->read_set) &&
1034          (NULL == task->write_set) )
1035     {
1036       if (GNUNET_YES == task->on_shutdown)
1037         GNUNET_CONTAINER_DLL_remove (shutdown_head,
1038                                      shutdown_tail,
1039                                      task);
1040       else
1041         GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1042                                      pending_timeout_tail,
1043                                      task);
1044       if (task == pending_timeout_last)
1045         pending_timeout_last = NULL;
1046     }
1047     else
1048     {
1049       GNUNET_CONTAINER_DLL_remove (pending_head,
1050                                    pending_tail,
1051                                    task);
1052     }
1053   }
1054   else
1055   {
1056     p = check_priority (task->priority);
1057     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1058                                  ready_tail[p],
1059                                  task);
1060     ready_count--;
1061   }
1062   ret = task->callback_cls;
1063   LOG (GNUNET_ERROR_TYPE_DEBUG,
1064        "Canceling task %p\n",
1065        task);
1066   destroy_task (task);
1067   return ret;
1068 }
1069
1070
1071 /**
1072  * Initialize backtrace data for task @a t
1073  *
1074  * @param t task to initialize
1075  */
1076 static void
1077 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
1078 {
1079 #if EXECINFO
1080   void *backtrace_array[MAX_TRACE_DEPTH];
1081
1082   t->num_backtrace_strings
1083     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1084   t->backtrace_strings =
1085       backtrace_symbols (backtrace_array,
1086                          t->num_backtrace_strings);
1087   dump_backtrace (t);
1088 #endif
1089 }
1090
1091
1092 /**
1093  * Continue the current execution with the given function.  This is
1094  * similar to the other "add" functions except that there is no delay
1095  * and the reason code can be specified.
1096  *
1097  * @param task main function of the task
1098  * @param task_cls closure for @a task
1099  * @param reason reason for task invocation
1100  * @param priority priority to use for the task
1101  */
1102 void
1103 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1104                                                void *task_cls,
1105                                                enum GNUNET_SCHEDULER_Reason reason,
1106                                                enum GNUNET_SCHEDULER_Priority priority)
1107 {
1108   struct GNUNET_SCHEDULER_Task *t;
1109
1110   GNUNET_assert (NULL != task);
1111   GNUNET_assert ((NULL != active_task) ||
1112                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1113   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1114   t->read_fd = -1;
1115   t->write_fd = -1;
1116   t->callback = task;
1117   t->callback_cls = task_cls;
1118 #if PROFILE_DELAYS
1119   t->start_time = GNUNET_TIME_absolute_get ();
1120 #endif
1121   t->reason = reason;
1122   t->priority = priority;
1123   t->lifeness = current_lifeness;
1124   LOG (GNUNET_ERROR_TYPE_DEBUG,
1125        "Adding continuation task %p\n",
1126        t);
1127   init_backtrace (t);
1128   queue_ready_task (t);
1129 }
1130
1131
1132 /**
1133  * Schedule a new task to be run at the specified time.  The task
1134  * will be scheduled for execution at time @a at.
1135  *
1136  * @param at time when the operation should run
1137  * @param priority priority to use for the task
1138  * @param task main function of the task
1139  * @param task_cls closure of @a task
1140  * @return unique task identifier for the job
1141  *         only valid until @a task is started!
1142  */
1143 struct GNUNET_SCHEDULER_Task *
1144 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1145                                        enum GNUNET_SCHEDULER_Priority priority,
1146                                        GNUNET_SCHEDULER_TaskCallback task,
1147                                        void *task_cls)
1148 {
1149   struct GNUNET_SCHEDULER_Task *t;
1150   struct GNUNET_SCHEDULER_Task *pos;
1151   struct GNUNET_SCHEDULER_Task *prev;
1152
1153   GNUNET_assert (NULL != active_task);
1154   GNUNET_assert (NULL != task);
1155   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1156   t->callback = task;
1157   t->callback_cls = task_cls;
1158   t->read_fd = -1;
1159   t->write_fd = -1;
1160 #if PROFILE_DELAYS
1161   t->start_time = GNUNET_TIME_absolute_get ();
1162 #endif
1163   t->timeout = at;
1164   t->priority = priority;
1165   t->lifeness = current_lifeness;
1166   /* try tail first (optimization in case we are
1167    * appending to a long list of tasks with timeouts) */
1168   if ( (NULL == pending_timeout_head) ||
1169        (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
1170   {
1171     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1172                                  pending_timeout_tail,
1173                                  t);
1174   }
1175   else
1176   {
1177     /* first move from heuristic start backwards to before start time */
1178     prev = pending_timeout_last;
1179     while ( (NULL != prev) &&
1180             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1181       prev = prev->prev;
1182     /* now, move from heuristic start (or head of list) forward to insertion point */
1183     if (NULL == prev)
1184       pos = pending_timeout_head;
1185     else
1186       pos = prev->next;
1187     while ( (NULL != pos) &&
1188             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1189               (0 != pos->reason) ) )
1190     {
1191       prev = pos;
1192       pos = pos->next;
1193     }
1194     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1195                                        pending_timeout_tail,
1196                                        prev,
1197                                        t);
1198   }
1199   /* finally, update heuristic insertion point to last insertion... */
1200   pending_timeout_last = t;
1201
1202   LOG (GNUNET_ERROR_TYPE_DEBUG,
1203        "Adding task: %p\n",
1204        t);
1205   init_backtrace (t);
1206   return t;
1207 }
1208
1209
1210 /**
1211  * Schedule a new task to be run with a specified delay.  The task
1212  * will be scheduled for execution once the delay has expired.
1213  *
1214  * @param delay when should this operation time out?
1215  * @param priority priority to use for the task
1216  * @param task main function of the task
1217  * @param task_cls closure of @a task
1218  * @return unique task identifier for the job
1219  *         only valid until @a task is started!
1220  */
1221 struct GNUNET_SCHEDULER_Task *
1222 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1223                                             enum GNUNET_SCHEDULER_Priority priority,
1224                                             GNUNET_SCHEDULER_TaskCallback task,
1225                                             void *task_cls)
1226 {
1227   return GNUNET_SCHEDULER_add_at_with_priority (GNUNET_TIME_relative_to_absolute (delay),
1228                                                 priority,
1229                                                 task,
1230                                                 task_cls);
1231 }
1232
1233
1234 /**
1235  * Schedule a new task to be run with a specified priority.
1236  *
1237  * @param prio how important is the new task?
1238  * @param task main function of the task
1239  * @param task_cls closure of @a task
1240  * @return unique task identifier for the job
1241  *         only valid until @a task is started!
1242  */
1243 struct GNUNET_SCHEDULER_Task *
1244 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1245                                     GNUNET_SCHEDULER_TaskCallback task,
1246                                     void *task_cls)
1247 {
1248   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1249                                                      prio,
1250                                                      task,
1251                                                      task_cls);
1252 }
1253
1254
1255 /**
1256  * Schedule a new task to be run at the specified time.  The task
1257  * will be scheduled for execution once specified time has been
1258  * reached. It will be run with the DEFAULT priority.
1259  *
1260  * @param at time at which this operation should run
1261  * @param task main function of the task
1262  * @param task_cls closure of @a task
1263  * @return unique task identifier for the job
1264  *         only valid until @a task is started!
1265  */
1266 struct GNUNET_SCHEDULER_Task *
1267 GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1268                          GNUNET_SCHEDULER_TaskCallback task,
1269                          void *task_cls)
1270 {
1271   return GNUNET_SCHEDULER_add_at_with_priority (at,
1272                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1273                                                 task,
1274                                                 task_cls);
1275 }
1276
1277
1278 /**
1279  * Schedule a new task to be run with a specified delay.  The task
1280  * will be scheduled for execution once the delay has expired. It
1281  * will be run with the DEFAULT priority.
1282  *
1283  * @param delay when should this operation time out?
1284  * @param task main function of the task
1285  * @param task_cls closure of @a task
1286  * @return unique task identifier for the job
1287  *         only valid until @a task is started!
1288  */
1289 struct GNUNET_SCHEDULER_Task *
1290 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1291                               GNUNET_SCHEDULER_TaskCallback task,
1292                               void *task_cls)
1293 {
1294   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1295                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1296                                                      task,
1297                                                      task_cls);
1298 }
1299
1300
1301 /**
1302  * Schedule a new task to be run as soon as possible.  Note that this
1303  * does not guarantee that this will be the next task that is being
1304  * run, as other tasks with higher priority (or that are already ready
1305  * to run) might get to run first.  Just as with delays, clients must
1306  * not rely on any particular order of execution between tasks
1307  * scheduled concurrently.
1308  *
1309  * The task will be run with the DEFAULT priority.
1310  *
1311  * @param task main function of the task
1312  * @param task_cls closure of @a task
1313  * @return unique task identifier for the job
1314  *         only valid until @a task is started!
1315  */
1316 struct GNUNET_SCHEDULER_Task *
1317 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1318                           void *task_cls)
1319 {
1320   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
1321                                        task,
1322                                        task_cls);
1323 }
1324
1325
1326 /**
1327  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1328  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1329  * invoked.
1330  *
1331  * @param task main function of the task
1332  * @param task_cls closure of @a task
1333  * @return unique task identifier for the job
1334  *         only valid until @a task is started!
1335  */
1336 struct GNUNET_SCHEDULER_Task *
1337 GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1338                                void *task_cls)
1339 {
1340   struct GNUNET_SCHEDULER_Task *t;
1341
1342   GNUNET_assert (NULL != active_task);
1343   GNUNET_assert (NULL != task);
1344   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1345   t->callback = task;
1346   t->callback_cls = task_cls;
1347   t->read_fd = -1;
1348   t->write_fd = -1;
1349 #if PROFILE_DELAYS
1350   t->start_time = GNUNET_TIME_absolute_get ();
1351 #endif
1352   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1353   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1354   t->on_shutdown = GNUNET_YES;
1355   t->lifeness = GNUNET_YES;
1356   GNUNET_CONTAINER_DLL_insert (shutdown_head,
1357                                shutdown_tail,
1358                                t);
1359   LOG (GNUNET_ERROR_TYPE_DEBUG,
1360        "Adding task: %p\n",
1361        t);
1362   init_backtrace (t);
1363   return t;
1364 }
1365
1366
1367 /**
1368  * Schedule a new task to be run as soon as possible with the
1369  * (transitive) ignore-shutdown flag either explicitly set or
1370  * explicitly enabled.  This task (and all tasks created from it,
1371  * other than by another call to this function) will either count or
1372  * not count for the "lifeness" of the process.  This API is only
1373  * useful in a few special cases.
1374  *
1375  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1376  * @param task main function of the task
1377  * @param task_cls closure of @a task
1378  * @return unique task identifier for the job
1379  *         only valid until @a task is started!
1380  */
1381 struct GNUNET_SCHEDULER_Task *
1382 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1383                                         GNUNET_SCHEDULER_TaskCallback task,
1384                                         void *task_cls)
1385 {
1386   struct GNUNET_SCHEDULER_Task *ret;
1387
1388   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1389   ret->lifeness = lifeness;
1390   return ret;
1391 }
1392
1393
1394 /**
1395  * Schedule a new task to be run with a specified delay or when any of
1396  * the specified file descriptor sets is ready.  The delay can be used
1397  * as a timeout on the socket(s) being ready.  The task will be
1398  * scheduled for execution once either the delay has expired or any of
1399  * the socket operations is ready.  This is the most general
1400  * function of the "add" family.  Note that the "prerequisite_task"
1401  * must be satisfied in addition to any of the other conditions.  In
1402  * other words, the task will be started when
1403  * <code>
1404  * (prerequisite-run)
1405  * && (delay-ready
1406  *     || any-rs-ready
1407  *     || any-ws-ready)
1408  * </code>
1409  *
1410  * @param delay how long should we wait?
1411  * @param priority priority to use
1412  * @param rfd file descriptor we want to read (can be -1)
1413  * @param wfd file descriptors we want to write (can be -1)
1414  * @param task main function of the task
1415  * @param task_cls closure of @a task
1416  * @return unique task identifier for the job
1417  *         only valid until @a task is started!
1418  */
1419 #ifndef MINGW
1420 static struct GNUNET_SCHEDULER_Task *
1421 add_without_sets (struct GNUNET_TIME_Relative delay,
1422                   enum GNUNET_SCHEDULER_Priority priority,
1423                   int rfd,
1424                   int wfd,
1425                   GNUNET_SCHEDULER_TaskCallback task,
1426                   void *task_cls)
1427 {
1428   struct GNUNET_SCHEDULER_Task *t;
1429
1430   GNUNET_assert (NULL != active_task);
1431   GNUNET_assert (NULL != task);
1432   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1433   t->callback = task;
1434   t->callback_cls = task_cls;
1435 #if DEBUG_FDS
1436   if (-1 != rfd)
1437   {
1438     int flags = fcntl (rfd, F_GETFD);
1439
1440     if ((flags == -1) && (errno == EBADF))
1441     {
1442       LOG (GNUNET_ERROR_TYPE_ERROR,
1443            "Got invalid file descriptor %d!\n",
1444            rfd);
1445       init_backtrace (t);
1446       GNUNET_assert (0);
1447     }
1448   }
1449   if (-1 != wfd)
1450   {
1451     int flags = fcntl (wfd, F_GETFD);
1452
1453     if (flags == -1 && errno == EBADF)
1454     {
1455       LOG (GNUNET_ERROR_TYPE_ERROR,
1456            "Got invalid file descriptor %d!\n",
1457            wfd);
1458       init_backtrace (t);
1459       GNUNET_assert (0);
1460     }
1461   }
1462 #endif
1463   t->read_fd = rfd;
1464   GNUNET_assert (wfd >= -1);
1465   t->write_fd = wfd;
1466 #if PROFILE_DELAYS
1467   t->start_time = GNUNET_TIME_absolute_get ();
1468 #endif
1469   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1470   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1471   t->lifeness = current_lifeness;
1472   GNUNET_CONTAINER_DLL_insert (pending_head,
1473                                pending_tail,
1474                                t);
1475   max_priority_added = GNUNET_MAX (max_priority_added,
1476                                    t->priority);
1477   LOG (GNUNET_ERROR_TYPE_DEBUG,
1478        "Adding task %p\n",
1479        t);
1480   init_backtrace (t);
1481   return t;
1482 }
1483 #endif
1484
1485
1486 /**
1487  * Schedule a new task to be run with a specified delay or when the
1488  * specified file descriptor is ready for reading.  The delay can be
1489  * used as a timeout on the socket being ready.  The task will be
1490  * scheduled for execution once either the delay has expired or the
1491  * socket operation is ready.  It will be run with the DEFAULT priority.
1492  *
1493  * @param delay when should this operation time out?
1494  * @param rfd read file-descriptor
1495  * @param task main function of the task
1496  * @param task_cls closure of @a task
1497  * @return unique task identifier for the job
1498  *         only valid until @a task is started!
1499  */
1500 struct GNUNET_SCHEDULER_Task *
1501 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1502                                struct GNUNET_NETWORK_Handle *rfd,
1503                                GNUNET_SCHEDULER_TaskCallback task,
1504                                void *task_cls)
1505 {
1506   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1507                                                       GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1508                                                       rfd, task, task_cls);
1509 }
1510
1511
1512 /**
1513  * Schedule a new task to be run with a specified priority and to be
1514  * run after the specified delay or when the specified file descriptor
1515  * is ready for reading.  The delay can be used as a timeout on the
1516  * socket being ready.  The task will be scheduled for execution once
1517  * either the delay has expired or the socket operation is ready.  It
1518  * will be run with the DEFAULT priority.
1519  *
1520  * @param delay when should this operation time out?
1521  * @param priority priority to use for the task
1522  * @param rfd read file-descriptor
1523  * @param task main function of the task
1524  * @param task_cls closure of @a task
1525  * @return unique task identifier for the job
1526  *         only valid until @a task is started!
1527  */
1528 struct GNUNET_SCHEDULER_Task *
1529 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1530                                              enum GNUNET_SCHEDULER_Priority priority,
1531                                              struct GNUNET_NETWORK_Handle *rfd,
1532                                              GNUNET_SCHEDULER_TaskCallback task,
1533                                              void *task_cls)
1534 {
1535   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1536                                                  rfd,
1537                                                  GNUNET_YES,
1538                                                  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 priority of
1549  * the calling task.
1550  *
1551  * @param delay when should this operation time out?
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_net (struct GNUNET_TIME_Relative delay,
1560                                 struct GNUNET_NETWORK_Handle *wfd,
1561                                 GNUNET_SCHEDULER_TaskCallback task,
1562                                 void *task_cls)
1563 {
1564   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1565                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1566                                                  wfd,
1567                                                  GNUNET_NO, GNUNET_YES,
1568                                                  task, task_cls);
1569 }
1570
1571 /**
1572  * Schedule a new task to be run with a specified delay or when the
1573  * specified file descriptor is ready.  The delay can be
1574  * used as a timeout on the socket being ready.  The task will be
1575  * scheduled for execution once either the delay has expired or the
1576  * socket operation is ready.
1577  *
1578  * @param delay when should this operation time out?
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 task
1585  * @return unique task identifier for the job
1586  *         only valid until "task" is started!
1587  */
1588 struct GNUNET_SCHEDULER_Task *
1589 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1590                                          enum GNUNET_SCHEDULER_Priority priority,
1591                                          struct GNUNET_NETWORK_Handle *fd,
1592                                          int on_read,
1593                                          int on_write,
1594                                          GNUNET_SCHEDULER_TaskCallback task,
1595                                          void *task_cls)
1596 {
1597 #if MINGW
1598   struct GNUNET_NETWORK_FDSet *s;
1599   struct GNUNET_SCHEDULER_Task * ret;
1600
1601   GNUNET_assert (NULL != fd);
1602   s = GNUNET_NETWORK_fdset_create ();
1603   GNUNET_NETWORK_fdset_set (s, fd);
1604   ret = GNUNET_SCHEDULER_add_select (
1605       priority, delay,
1606       on_read  ? s : NULL,
1607       on_write ? s : NULL,
1608       task, task_cls);
1609   GNUNET_NETWORK_fdset_destroy (s);
1610   return ret;
1611 #else
1612   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1613   return add_without_sets (delay, priority,
1614                            on_read  ? GNUNET_NETWORK_get_fd (fd) : -1,
1615                            on_write ? GNUNET_NETWORK_get_fd (fd) : -1,
1616                            task, task_cls);
1617 #endif
1618 }
1619
1620
1621 /**
1622  * Schedule a new task to be run with a specified delay or when the
1623  * specified file descriptor is ready for reading.  The delay can be
1624  * used as a timeout on the socket being ready.  The task will be
1625  * scheduled for execution once either the delay has expired or the
1626  * socket operation is ready. It will be run with the DEFAULT priority.
1627  *
1628  * @param delay when should this operation time out?
1629  * @param rfd read file-descriptor
1630  * @param task main function of the task
1631  * @param task_cls closure of @a task
1632  * @return unique task identifier for the job
1633  *         only valid until @a task is started!
1634  */
1635 struct GNUNET_SCHEDULER_Task *
1636 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1637                                 const struct GNUNET_DISK_FileHandle *rfd,
1638                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1639 {
1640   return GNUNET_SCHEDULER_add_file_with_priority (
1641       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1642       rfd, GNUNET_YES, GNUNET_NO,
1643       task, task_cls);
1644 }
1645
1646
1647 /**
1648  * Schedule a new task to be run with a specified delay or when the
1649  * specified file descriptor is ready for writing.  The delay can be
1650  * used as a timeout on the socket being ready.  The task will be
1651  * scheduled for execution once either the delay has expired or the
1652  * socket operation is ready. It will be run with the DEFAULT priority.
1653  *
1654  * @param delay when should this operation time out?
1655  * @param wfd write file-descriptor
1656  * @param task main function of the task
1657  * @param task_cls closure of @a task
1658  * @return unique task identifier for the job
1659  *         only valid until @a task is started!
1660  */
1661 struct GNUNET_SCHEDULER_Task *
1662 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1663                                  const struct GNUNET_DISK_FileHandle *wfd,
1664                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1665 {
1666   return GNUNET_SCHEDULER_add_file_with_priority (
1667       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1668       wfd, GNUNET_NO, GNUNET_YES,
1669       task, task_cls);
1670 }
1671
1672
1673 /**
1674  * Schedule a new task to be run with a specified delay or when the
1675  * specified file descriptor is ready.  The delay can be
1676  * used as a timeout on the socket being ready.  The task will be
1677  * scheduled for execution once either the delay has expired or the
1678  * socket operation is ready.
1679  *
1680  * @param delay when should this operation time out?
1681  * @param priority priority of the task
1682  * @param fd file-descriptor
1683  * @param on_read whether to poll the file-descriptor for readability
1684  * @param on_write whether to poll the file-descriptor for writability
1685  * @param task main function of the task
1686  * @param task_cls closure of @a task
1687  * @return unique task identifier for the job
1688  *         only valid until @a task is started!
1689  */
1690 struct GNUNET_SCHEDULER_Task *
1691 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1692                                          enum GNUNET_SCHEDULER_Priority priority,
1693                                          const struct GNUNET_DISK_FileHandle *fd,
1694                                          int on_read, int on_write,
1695                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1696 {
1697 #if MINGW
1698   struct GNUNET_NETWORK_FDSet *s;
1699   struct GNUNET_SCHEDULER_Task * ret;
1700
1701   GNUNET_assert (NULL != fd);
1702   s = GNUNET_NETWORK_fdset_create ();
1703   GNUNET_NETWORK_fdset_handle_set (s, fd);
1704   ret = GNUNET_SCHEDULER_add_select (
1705       priority, delay,
1706       on_read  ? s : NULL,
1707       on_write ? s : NULL,
1708       task, task_cls);
1709   GNUNET_NETWORK_fdset_destroy (s);
1710   return ret;
1711 #else
1712   int real_fd;
1713
1714   GNUNET_DISK_internal_file_handle_ (fd, &real_fd, sizeof (int));
1715   GNUNET_assert (real_fd >= 0);
1716   return add_without_sets (
1717       delay, priority,
1718       on_read  ? real_fd : -1,
1719       on_write ? real_fd : -1,
1720       task, task_cls);
1721 #endif
1722 }
1723
1724
1725 /**
1726  * Schedule a new task to be run with a specified delay or when any of
1727  * the specified file descriptor sets is ready.  The delay can be used
1728  * as a timeout on the socket(s) being ready.  The task will be
1729  * scheduled for execution once either the delay has expired or any of
1730  * the socket operations is ready.  This is the most general
1731  * function of the "add" family.  Note that the "prerequisite_task"
1732  * must be satisfied in addition to any of the other conditions.  In
1733  * other words, the task will be started when
1734  * <code>
1735  * (prerequisite-run)
1736  * && (delay-ready
1737  *     || any-rs-ready
1738  *     || any-ws-ready) )
1739  * </code>
1740  *
1741  * @param prio how important is this task?
1742  * @param delay how long should we wait?
1743  * @param rs set of file descriptors we want to read (can be NULL)
1744  * @param ws set of file descriptors we want to write (can be NULL)
1745  * @param task main function of the task
1746  * @param task_cls closure of @a task
1747  * @return unique task identifier for the job
1748  *         only valid until @a task is started!
1749  */
1750 struct GNUNET_SCHEDULER_Task *
1751 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1752                              struct GNUNET_TIME_Relative delay,
1753                              const struct GNUNET_NETWORK_FDSet *rs,
1754                              const struct GNUNET_NETWORK_FDSet *ws,
1755                              GNUNET_SCHEDULER_TaskCallback task,
1756                              void *task_cls)
1757 {
1758   struct GNUNET_SCHEDULER_Task *t;
1759
1760   if ( (NULL == rs) &&
1761        (NULL == ws) )
1762     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1763                                                        prio,
1764                                                        task,
1765                                                        task_cls);
1766   GNUNET_assert (NULL != active_task);
1767   GNUNET_assert (NULL != task);
1768   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1769   t->callback = task;
1770   t->callback_cls = task_cls;
1771   t->read_fd = -1;
1772   t->write_fd = -1;
1773   if (NULL != rs)
1774   {
1775     t->read_set = GNUNET_NETWORK_fdset_create ();
1776     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1777   }
1778   if (NULL != ws)
1779   {
1780     t->write_set = GNUNET_NETWORK_fdset_create ();
1781     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1782   }
1783 #if PROFILE_DELAYS
1784   t->start_time = GNUNET_TIME_absolute_get ();
1785 #endif
1786   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1787   t->priority =
1788       check_priority ((prio ==
1789                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1790                       prio);
1791   t->lifeness = current_lifeness;
1792   GNUNET_CONTAINER_DLL_insert (pending_head,
1793                                pending_tail,
1794                                t);
1795   max_priority_added = GNUNET_MAX (max_priority_added,
1796                                    t->priority);
1797   LOG (GNUNET_ERROR_TYPE_DEBUG,
1798        "Adding task %p\n",
1799        t);
1800   init_backtrace (t);
1801   return t;
1802 }
1803
1804
1805 /**
1806  * Function used by event-loop implementations to signal the scheduler
1807  * that a particular @a task is ready due to an event of type @a et.
1808  *
1809  * This function will then queue the task to notify the application
1810  * that the task is ready (with the respective priority).
1811  *
1812  * @param task the task that is ready, NULL for wake up calls
1813  * @param et information about why the task is ready
1814  */
1815 void
1816 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1817                              enum GNUNET_SCHEDULER_EventType et)
1818 {
1819   enum GNUNET_SCHEDULER_Reason reason;
1820   struct GNUNET_TIME_Absolute now;
1821
1822   now = GNUNET_TIME_absolute_get ();
1823   reason = task->reason;
1824   if (now.abs_value_us >= task->timeout.abs_value_us)
1825     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1826   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1827        (0 != (GNUNET_SCHEDULER_ET_IN & et)) )
1828     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1829   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1830        (0 != (GNUNET_SCHEDULER_ET_OUT & et)) )
1831     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1832   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1833   task->reason = reason;
1834   task->fds = &task->fdx;
1835   task->fdx.et = et;
1836   task->fds_len = 1;
1837   queue_ready_task (task);
1838 }
1839
1840
1841 /**
1842  * Function called by the driver to tell the scheduler to run some of
1843  * the tasks that are ready.  This function may return even though
1844  * there are tasks left to run just to give other tasks a chance as
1845  * well.  If we return #GNUNET_YES, the driver should call this
1846  * function again as soon as possible, while if we return #GNUNET_NO
1847  * it must block until the operating system has more work as the
1848  * scheduler has no more work to do right now.
1849  *
1850  * @param sh scheduler handle that was given to the `loop`
1851  * @return #GNUNET_OK if there are more tasks that are ready,
1852  *          and thus we would like to run more (yield to avoid
1853  *          blocking other activities for too long)
1854  *         #GNUNET_NO if we are done running tasks (yield to block)
1855  *         #GNUNET_SYSERR on error
1856  */
1857 int
1858 GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh)
1859 {
1860   enum GNUNET_SCHEDULER_Priority p;
1861   struct GNUNET_SCHEDULER_Task *pos;
1862   struct GNUNET_TIME_Absolute now;
1863
1864   /* check for tasks that reached the timeout! */
1865   now = GNUNET_TIME_absolute_get ();
1866   while (NULL != (pos = pending_timeout_head))
1867   {
1868     if (now.abs_value_us >= pos->timeout.abs_value_us)
1869       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1870     if (0 == pos->reason)
1871       break;
1872     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1873                                  pending_timeout_tail,
1874                                  pos);
1875     if (pending_timeout_last == pos)
1876       pending_timeout_last = NULL;
1877     queue_ready_task (pos);
1878   }
1879
1880   if (0 == ready_count)
1881     return GNUNET_NO;
1882
1883   /* find out which task priority level we are going to
1884      process this time */
1885   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
1886   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
1887   /* yes, p>0 is correct, 0 is "KEEP" which should
1888    * always be an empty queue (see assertion)! */
1889   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
1890   {
1891     pos = ready_head[p];
1892     if (NULL != pos)
1893       break;
1894   }
1895   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
1896
1897   /* process all tasks at this priority level, then yield */
1898   while (NULL != (pos = ready_head[p]))
1899   {
1900     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1901                                  ready_tail[p],
1902                                  pos);
1903     ready_count--;
1904     current_priority = pos->priority;
1905     current_lifeness = pos->lifeness;
1906     active_task = pos;
1907 #if PROFILE_DELAYS
1908     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
1909         DELAY_THRESHOLD.rel_value_us)
1910     {
1911       LOG (GNUNET_ERROR_TYPE_DEBUG,
1912            "Task %p took %s to be scheduled\n",
1913            pos,
1914            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
1915                                                    GNUNET_YES));
1916     }
1917 #endif
1918     tc.reason = pos->reason;
1919     GNUNET_NETWORK_fdset_zero (sh->rs);
1920     GNUNET_NETWORK_fdset_zero (sh->ws);
1921     tc.fds_len = pos->fds_len;
1922     tc.fds = pos->fds;
1923     tc.read_ready = (NULL == pos->read_set) ? sh->rs : pos->read_set;
1924     if ( (-1 != pos->read_fd) &&
1925          (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) )
1926       GNUNET_NETWORK_fdset_set_native (sh->rs,
1927                                        pos->read_fd);
1928     tc.write_ready = (NULL == pos->write_set) ? sh->ws : pos->write_set;
1929     if ((-1 != pos->write_fd) &&
1930         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
1931       GNUNET_NETWORK_fdset_set_native (sh->ws,
1932                                        pos->write_fd);
1933     LOG (GNUNET_ERROR_TYPE_DEBUG,
1934          "Running task: %p\n",
1935          pos);
1936     pos->callback (pos->callback_cls);
1937     active_task = NULL;
1938     dump_backtrace (pos);
1939     destroy_task (pos);
1940     tasks_run++;
1941   }
1942   if (0 == ready_count)
1943     return GNUNET_NO;
1944   return GNUNET_OK;
1945 }
1946
1947
1948 /**
1949  * Initialize and run scheduler.  This function will return when all
1950  * tasks have completed.  On systems with signals, receiving a SIGTERM
1951  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown
1952  * to be run after the active task is complete.  As a result, SIGTERM
1953  * causes all shutdown tasks to be scheduled with reason
1954  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
1955  * afterwards will execute normally!).  Note that any particular
1956  * signal will only shut down one scheduler; applications should
1957  * always only create a single scheduler.
1958  *
1959  * @param driver drive to use for the event loop
1960  * @param task task to run first (and immediately)
1961  * @param task_cls closure of @a task
1962  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1963  */
1964 int
1965 GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver,
1966                                   GNUNET_SCHEDULER_TaskCallback task,
1967                                   void *task_cls)
1968 {
1969   int ret;
1970   struct GNUNET_SIGNAL_Context *shc_int;
1971   struct GNUNET_SIGNAL_Context *shc_term;
1972 #if (SIGTERM != GNUNET_TERM_SIG)
1973   struct GNUNET_SIGNAL_Context *shc_gterm;
1974 #endif
1975 #ifndef MINGW
1976   struct GNUNET_SIGNAL_Context *shc_quit;
1977   struct GNUNET_SIGNAL_Context *shc_hup;
1978   struct GNUNET_SIGNAL_Context *shc_pipe;
1979 #endif
1980   struct GNUNET_SCHEDULER_Task tsk;
1981   const struct GNUNET_DISK_FileHandle *pr;
1982   struct GNUNET_SCHEDULER_Handle sh;
1983
1984   /* general set-up */
1985   GNUNET_assert (NULL == active_task);
1986   GNUNET_assert (NULL == shutdown_pipe_handle);
1987   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
1988                                            GNUNET_NO,
1989                                            GNUNET_NO,
1990                                            GNUNET_NO);
1991   GNUNET_assert (NULL != shutdown_pipe_handle);
1992   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
1993                                 GNUNET_DISK_PIPE_END_READ);
1994   GNUNET_assert (NULL != pr);
1995   my_pid = getpid ();
1996
1997   /* install signal handlers */
1998   LOG (GNUNET_ERROR_TYPE_DEBUG,
1999        "Registering signal handlers\n");
2000   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2001                                            &sighandler_shutdown);
2002   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2003                                             &sighandler_shutdown);
2004 #if (SIGTERM != GNUNET_TERM_SIG)
2005   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2006                                              &sighandler_shutdown);
2007 #endif
2008 #ifndef MINGW
2009   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2010                                             &sighandler_pipe);
2011   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2012                                             &sighandler_shutdown);
2013   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2014                                            &sighandler_shutdown);
2015 #endif
2016
2017   /* Setup initial tasks */
2018   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2019   current_lifeness = GNUNET_YES;
2020   memset (&tsk,
2021           0,
2022           sizeof (tsk));
2023   active_task = &tsk;
2024   tsk.sh = &sh;
2025   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
2026                                                  task_cls,
2027                                                  GNUNET_SCHEDULER_REASON_STARTUP,
2028                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
2029   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
2030                                           &GNUNET_OS_install_parent_control_handler,
2031                                           NULL);
2032   active_task = NULL;
2033   driver->set_wakeup (driver->cls,
2034                       GNUNET_TIME_absolute_get ());
2035
2036   /* begin main event loop */
2037   sh.rs = GNUNET_NETWORK_fdset_create ();
2038   sh.ws = GNUNET_NETWORK_fdset_create ();
2039   sh.driver = driver;
2040   ret = driver->loop (driver->cls,
2041                       &sh);
2042   GNUNET_NETWORK_fdset_destroy (sh.rs);
2043   GNUNET_NETWORK_fdset_destroy (sh.ws);
2044
2045   /* uninstall signal handlers */
2046   GNUNET_SIGNAL_handler_uninstall (shc_int);
2047   GNUNET_SIGNAL_handler_uninstall (shc_term);
2048 #if (SIGTERM != GNUNET_TERM_SIG)
2049   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
2050 #endif
2051 #ifndef MINGW
2052   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
2053   GNUNET_SIGNAL_handler_uninstall (shc_quit);
2054   GNUNET_SIGNAL_handler_uninstall (shc_hup);
2055 #endif
2056   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2057   shutdown_pipe_handle = NULL;
2058   return ret;
2059 }
2060
2061
2062 /**
2063  * Obtain the driver for using select() as the event loop.
2064  *
2065  * @return NULL on error
2066  */
2067 const struct GNUNET_SCHEDULER_Driver *
2068 GNUNET_SCHEDULER_driver_select ()
2069 {
2070   GNUNET_break (0); // not implemented
2071   return NULL;
2072 }
2073
2074
2075 /* end of scheduler.c */