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