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