f379bce42df827aba9ed2284dc4ea80da9c27067
[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 it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your 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       Affero General Public License for more details.
14
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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 // DEBUG
29 #include <inttypes.h>
30
31 #define LOG(kind, ...) GNUNET_log_from(kind, "util-scheduler", __VA_ARGS__)
32
33 #define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror(kind, "util-scheduler", syscall)
34
35
36 #if HAVE_EXECINFO_H
37 #include "execinfo.h"
38
39 /**
40  * Use lsof to generate file descriptor reports on select error?
41  * (turn off for stable releases).
42  */
43 #define USE_LSOF GNUNET_NO
44
45 /**
46  * Obtain trace information for all scheduler calls that schedule tasks.
47  */
48 #define EXECINFO GNUNET_NO
49
50 /**
51  * Check each file descriptor before adding
52  */
53 #define DEBUG_FDS GNUNET_NO
54
55 /**
56  * Depth of the traces collected via EXECINFO.
57  */
58 #define MAX_TRACE_DEPTH 50
59 #endif
60
61 /**
62  * Should we figure out which tasks are delayed for a while
63  * before they are run? (Consider using in combination with EXECINFO).
64  */
65 #define PROFILE_DELAYS GNUNET_NO
66
67 /**
68  * Task that were in the queue for longer than this are reported if
69  * PROFILE_DELAYS is active.
70  */
71 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
72
73
74 /**
75  * Argument to be passed from the driver to
76  * #GNUNET_SCHEDULER_do_work().  Contains the
77  * scheduler's internal state.
78  */
79 struct GNUNET_SCHEDULER_Handle {
80   /**
81    * Passed here to avoid constantly allocating/deallocating
82    * this element, but generally we want to get rid of this.
83    * @deprecated
84    */
85   struct GNUNET_NETWORK_FDSet *rs;
86
87   /**
88    * Passed here to avoid constantly allocating/deallocating
89    * this element, but generally we want to get rid of this.
90    * @deprecated
91    */
92   struct GNUNET_NETWORK_FDSet *ws;
93
94   /**
95    * context of the SIGINT handler
96    */
97   struct GNUNET_SIGNAL_Context *shc_int;
98
99   /**
100    * context of the SIGTERM handler
101    */
102   struct GNUNET_SIGNAL_Context *shc_term;
103
104 #if (SIGTERM != GNUNET_TERM_SIG)
105   /**
106    * context of the TERM_SIG handler
107    */
108   struct GNUNET_SIGNAL_Context *shc_gterm;
109 #endif
110
111   /**
112    * context of the SIGQUIT handler
113    */
114   struct GNUNET_SIGNAL_Context *shc_quit;
115
116   /**
117    * context of the SIGHUP handler
118    */
119   struct GNUNET_SIGNAL_Context *shc_hup;
120
121   /**
122    * context of hte SIGPIPE handler
123    */
124   struct GNUNET_SIGNAL_Context *shc_pipe;
125 };
126
127
128 /**
129  * Entry in list of pending tasks.
130  */
131 struct GNUNET_SCHEDULER_Task {
132   /**
133    * This is a linked list.
134    */
135   struct GNUNET_SCHEDULER_Task *next;
136
137   /**
138    * This is a linked list.
139    */
140   struct GNUNET_SCHEDULER_Task *prev;
141
142   /**
143    * Function to run when ready.
144    */
145   GNUNET_SCHEDULER_TaskCallback callback;
146
147   /**
148    * Closure for the @e callback.
149    */
150   void *callback_cls;
151
152   /**
153    * Information about which FDs are ready for this task (and why).
154    */
155   struct GNUNET_SCHEDULER_FdInfo *fds;
156
157   /**
158    * Storage location used for @e fds if we want to avoid
159    * a separate malloc() call in the common case that this
160    * task is only about a single FD.
161    */
162   struct GNUNET_SCHEDULER_FdInfo fdx;
163
164   /**
165    * Size of the @e fds array.
166    */
167   unsigned int fds_len;
168
169   /**
170    * Do we own the network and file handles referenced by the FdInfo
171    * structs in the fds array. This will only be GNUNET_YES if the
172    * task was created by the #GNUNET_SCHEDULER_add_select function.
173    */
174   int own_handles;
175
176   /**
177    * Absolute timeout value for the task, or
178    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
179    */
180   struct GNUNET_TIME_Absolute timeout;
181
182 #if PROFILE_DELAYS
183   /**
184    * When was the task scheduled?
185    */
186   struct GNUNET_TIME_Absolute start_time;
187 #endif
188
189   /**
190    * Why is the task ready?  Set after task is added to ready queue.
191    * Initially set to zero.  All reasons that have already been
192    * satisfied (i.e.  read or write ready) will be set over time.
193    */
194   enum GNUNET_SCHEDULER_Reason reason;
195
196   /**
197    * Task priority.
198    */
199   enum GNUNET_SCHEDULER_Priority priority;
200
201   /**
202    * Set if we only wait for reading from a single FD, otherwise -1.
203    */
204   int read_fd;
205
206   /**
207    * Set if we only wait for writing to a single FD, otherwise -1.
208    */
209   int write_fd;
210
211   /**
212    * Should the existence of this task in the queue be counted as
213    * reason to not shutdown the scheduler?
214    */
215   int lifeness;
216
217   /**
218    * Is this task run on shutdown?
219    */
220   int on_shutdown;
221
222   /**
223    * Is this task in the ready list?
224    */
225   int in_ready_list;
226
227 #if EXECINFO
228   /**
229    * Array of strings which make up a backtrace from the point when this
230    * task was scheduled (essentially, who scheduled the task?)
231    */
232   char **backtrace_strings;
233
234   /**
235    * Size of the backtrace_strings array
236    */
237   int num_backtrace_strings;
238 #endif
239
240   /**
241    * Asynchronous scope of the task that scheduled this scope,
242    */
243   struct GNUNET_AsyncScopeSave scope;
244 };
245
246
247 /**
248  * A struct representing an event the select driver is waiting for
249  */
250 struct Scheduled {
251   struct Scheduled *prev;
252
253   struct Scheduled *next;
254
255   /**
256    * the task, the event is related to
257    */
258   struct GNUNET_SCHEDULER_Task *task;
259
260   /**
261    * information about the network socket / file descriptor where
262    * the event is expected to occur
263    */
264   struct GNUNET_SCHEDULER_FdInfo *fdi;
265
266   /**
267    * the event types (multiple event types can be ORed) the select
268    * driver is expected to wait for
269    */
270   enum GNUNET_SCHEDULER_EventType et;
271 };
272
273
274 /**
275  * Driver context used by GNUNET_SCHEDULER_run
276  */
277 struct DriverContext {
278   /**
279    * the head of a DLL containing information about the events the
280    * select driver is waiting for
281    */
282   struct Scheduled *scheduled_head;
283
284   /**
285    * the tail of a DLL containing information about the events the
286    * select driver is waiting for
287    */
288   struct Scheduled *scheduled_tail;
289
290   /**
291    * the time when the select driver will wake up again (after
292    * calling select)
293    */
294   struct GNUNET_TIME_Absolute timeout;
295 };
296
297
298 /**
299  * The driver used for the event loop. Will be handed over to
300  * the scheduler in #GNUNET_SCHEDULER_do_work(), persisted
301  * there in this variable for later use in functions like
302  * #GNUNET_SCHEDULER_add_select(), #add_without_sets() and
303  * #GNUNET_SCHEDULER_cancel().
304  */
305 static const struct GNUNET_SCHEDULER_Driver *scheduler_driver;
306
307 /**
308  * Head of list of tasks waiting for an event.
309  */
310 static struct GNUNET_SCHEDULER_Task *pending_head;
311
312 /**
313  * Tail of list of tasks waiting for an event.
314  */
315 static struct GNUNET_SCHEDULER_Task *pending_tail;
316
317 /**
318  * Head of list of tasks waiting for shutdown.
319  */
320 static struct GNUNET_SCHEDULER_Task *shutdown_head;
321
322 /**
323  * Tail of list of tasks waiting for shutdown.
324  */
325 static struct GNUNET_SCHEDULER_Task *shutdown_tail;
326
327 /**
328  * List of tasks waiting ONLY for a timeout event.
329  * Sorted by timeout (earliest first).  Used so that
330  * we do not traverse the list of these tasks when
331  * building select sets (we just look at the head
332  * to determine the respective timeout ONCE).
333  */
334 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
335
336 /**
337  * List of tasks waiting ONLY for a timeout event.
338  * Sorted by timeout (earliest first).  Used so that
339  * we do not traverse the list of these tasks when
340  * building select sets (we just look at the head
341  * to determine the respective timeout ONCE).
342  */
343 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
344
345 /**
346  * Last inserted task waiting ONLY for a timeout event.
347  * Used to (heuristically) speed up insertion.
348  */
349 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
350
351 /**
352  * ID of the task that is running right now.
353  */
354 static struct GNUNET_SCHEDULER_Task *active_task;
355
356 /**
357  * Head of list of tasks ready to run right now, grouped by importance.
358  */
359 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
360
361 /**
362  * Tail of list of tasks ready to run right now, grouped by importance.
363  */
364 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
365
366 /**
367  * Task for installing parent control handlers (it might happen that the
368  * scheduler is shutdown before this task is executed, so
369  * GNUNET_SCHEDULER_shutdown must cancel it in that case)
370  */
371 static struct GNUNET_SCHEDULER_Task *install_parent_control_task;
372
373 /**
374  * Task for reading from a pipe that signal handlers will use to initiate
375  * shutdown
376  */
377 static struct GNUNET_SCHEDULER_Task *shutdown_pipe_task;
378
379 /**
380  * Number of tasks on the ready list.
381  */
382 static unsigned int ready_count;
383
384 /**
385  * Priority of the task running right now.  Only
386  * valid while a task is running.
387  */
388 static enum GNUNET_SCHEDULER_Priority current_priority;
389
390 /**
391  * Priority of the highest task added in the current select
392  * iteration.
393  */
394 static enum GNUNET_SCHEDULER_Priority max_priority_added;
395
396 /**
397  * Value of the 'lifeness' flag for the current task.
398  */
399 static int current_lifeness;
400
401 /**
402  * Function to use as a select() in the scheduler.
403  * If NULL, we use GNUNET_NETWORK_socket_select().
404  */
405 static GNUNET_SCHEDULER_select scheduler_select;
406
407 /**
408  * Task context of the current task.
409  */
410 static struct GNUNET_SCHEDULER_TaskContext tc;
411
412 /**
413  * Closure for #scheduler_select.
414  */
415 static void *scheduler_select_cls;
416
417
418 /**
419  * Sets the select function to use in the scheduler (scheduler_select).
420  *
421  * @param new_select new select function to use
422  * @param new_select_cls closure for @a new_select
423  * @return previously used select function, NULL for default
424  */
425 void
426 GNUNET_SCHEDULER_set_select(GNUNET_SCHEDULER_select new_select,
427                             void *new_select_cls)
428 {
429   scheduler_select = new_select;
430   scheduler_select_cls = new_select_cls;
431 }
432
433
434 /**
435  * Check that the given priority is legal (and return it).
436  *
437  * @param p priority value to check
438  * @return p on success, 0 on error
439  */
440 static enum GNUNET_SCHEDULER_Priority
441 check_priority(enum GNUNET_SCHEDULER_Priority p)
442 {
443   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
444     return p;
445   GNUNET_assert(0);
446   return 0;                     /* make compiler happy */
447 }
448
449
450 /**
451  * chooses the nearest timeout from all pending tasks, to be used
452  * to tell the driver the next wakeup time (using its set_wakeup
453  * callback)
454  */
455 struct GNUNET_TIME_Absolute
456 get_timeout()
457 {
458   struct GNUNET_SCHEDULER_Task *pos;
459   struct GNUNET_TIME_Absolute now;
460   struct GNUNET_TIME_Absolute timeout;
461
462   pos = pending_timeout_head;
463   now = GNUNET_TIME_absolute_get();
464   timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
465   if (NULL != pos)
466     {
467       if (0 != pos->reason)
468         {
469           return now;
470         }
471       else
472         {
473           timeout = pos->timeout;
474         }
475     }
476   for (pos = pending_head; NULL != pos; pos = pos->next)
477     {
478       if (0 != pos->reason)
479         {
480           return now;
481         }
482       else if ((pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
483                (timeout.abs_value_us > pos->timeout.abs_value_us))
484         {
485           timeout = pos->timeout;
486         }
487     }
488   return timeout;
489 }
490
491
492 /**
493  * Put a task that is ready for execution into the ready queue.
494  *
495  * @param task task ready for execution
496  */
497 static void
498 queue_ready_task(struct GNUNET_SCHEDULER_Task *task)
499 {
500   enum GNUNET_SCHEDULER_Priority p = check_priority(task->priority);
501
502   GNUNET_CONTAINER_DLL_insert(ready_head[p],
503                               ready_tail[p],
504                               task);
505   task->in_ready_list = GNUNET_YES;
506   ready_count++;
507 }
508
509
510 /**
511  * Request the shutdown of a scheduler.  Marks all tasks
512  * awaiting shutdown as ready. Note that tasks
513  * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
514  * will be delayed until the next shutdown signal.
515  */
516 void
517 GNUNET_SCHEDULER_shutdown()
518 {
519   struct GNUNET_SCHEDULER_Task *pos;
520
521   LOG(GNUNET_ERROR_TYPE_DEBUG,
522       "GNUNET_SCHEDULER_shutdown\n");
523   if (NULL != install_parent_control_task)
524     {
525       GNUNET_SCHEDULER_cancel(install_parent_control_task);
526       install_parent_control_task = NULL;
527     }
528   if (NULL != shutdown_pipe_task)
529     {
530       GNUNET_SCHEDULER_cancel(shutdown_pipe_task);
531       shutdown_pipe_task = NULL;
532     }
533   while (NULL != (pos = shutdown_head))
534     {
535       GNUNET_CONTAINER_DLL_remove(shutdown_head,
536                                   shutdown_tail,
537                                   pos);
538       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
539       queue_ready_task(pos);
540     }
541 }
542
543
544 /**
545  * Output stack trace of task @a t.
546  *
547  * @param t task to dump stack trace of
548  */
549 static void
550 dump_backtrace(struct GNUNET_SCHEDULER_Task *t)
551 {
552 #if EXECINFO
553   for (unsigned int i = 0; i < t->num_backtrace_strings; i++)
554     LOG(GNUNET_ERROR_TYPE_WARNING,
555         "Task %p trace %u: %s\n",
556         t,
557         i,
558         t->backtrace_strings[i]);
559 #else
560   (void)t;
561 #endif
562 }
563
564
565 /**
566  * Destroy a task (release associated resources)
567  *
568  * @param t task to destroy
569  */
570 static void
571 destroy_task(struct GNUNET_SCHEDULER_Task *t)
572 {
573   unsigned int i;
574
575   LOG(GNUNET_ERROR_TYPE_DEBUG,
576       "destroying task %p\n",
577       t);
578
579   if (GNUNET_YES == t->own_handles)
580     {
581       for (i = 0; i != t->fds_len; ++i)
582         {
583           const struct GNUNET_NETWORK_Handle *fd = t->fds[i].fd;
584           const struct GNUNET_DISK_FileHandle *fh = t->fds[i].fh;
585           if (fd)
586             {
587               GNUNET_NETWORK_socket_free_memory_only_((struct GNUNET_NETWORK_Handle *)fd);
588             }
589           if (fh)
590             {
591               // FIXME: on WIN32 this is not enough! A function
592               // GNUNET_DISK_file_free_memory_only would be nice
593               GNUNET_free((void *)fh);
594             }
595         }
596     }
597   if (t->fds_len > 1)
598     {
599       GNUNET_array_grow(t->fds, t->fds_len, 0);
600     }
601 #if EXECINFO
602   GNUNET_free(t->backtrace_strings);
603 #endif
604   GNUNET_free(t);
605 }
606
607
608 /**
609  * Pipe used to communicate shutdown via signal.
610  */
611 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
612
613 /**
614  * Process ID of this process at the time we installed the various
615  * signal handlers.
616  */
617 static pid_t my_pid;
618
619 /**
620  * Signal handler called for SIGPIPE.
621  */
622 static void
623 sighandler_pipe()
624 {
625   return;
626 }
627
628
629 ///**
630 // * Wait for a short time.
631 // * Sleeps for @a ms ms (as that should be long enough for virtually all
632 // * modern systems to context switch and allow another process to do
633 // * some 'real' work).
634 // *
635 // * @param ms how many ms to wait
636 // */
637 //static void
638 //short_wait (unsigned int ms)
639 //{
640 //  struct GNUNET_TIME_Relative timeout;
641 //
642 //  timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
643 //  (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
644 //}
645
646
647 /**
648  * Signal handler called for signals that should cause us to shutdown.
649  */
650 static void
651 sighandler_shutdown()
652 {
653   static char c;
654   int old_errno = errno;        /* backup errno */
655
656   if (getpid() != my_pid)
657     _exit(1);                    /* we have fork'ed since the signal handler was created,
658                                   * ignore the signal, see https://gnunet.org/vfork discussion */
659   GNUNET_DISK_file_write(GNUNET_DISK_pipe_handle
660                            (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
661                          &c, sizeof(c));
662   errno = old_errno;
663 }
664
665
666 static void
667 shutdown_if_no_lifeness()
668 {
669   struct GNUNET_SCHEDULER_Task *t;
670
671   if (ready_count > 0)
672     return;
673   for (t = pending_head; NULL != t; t = t->next)
674     if (GNUNET_YES == t->lifeness)
675       return;
676   for (t = shutdown_head; NULL != t; t = t->next)
677     if (GNUNET_YES == t->lifeness)
678       return;
679   for (t = pending_timeout_head; NULL != t; t = t->next)
680     if (GNUNET_YES == t->lifeness)
681       return;
682   /* No lifeness! */
683   GNUNET_SCHEDULER_shutdown();
684 }
685
686
687 static int
688 select_loop(struct GNUNET_SCHEDULER_Handle *sh,
689             struct DriverContext *context);
690
691
692 /**
693  * Initialize and run scheduler.  This function will return when all
694  * tasks have completed.  On systems with signals, receiving a SIGTERM
695  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
696  * to be run after the active task is complete.  As a result, SIGTERM
697  * causes all active tasks to be scheduled with reason
698  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
699  * afterwards will execute normally!). Note that any particular signal
700  * will only shut down one scheduler; applications should always only
701  * create a single scheduler.
702  *
703  * @param task task to run immediately
704  * @param task_cls closure of @a task
705  */
706 void
707 GNUNET_SCHEDULER_run(GNUNET_SCHEDULER_TaskCallback task,
708                      void *task_cls)
709 {
710   struct GNUNET_SCHEDULER_Handle *sh;
711   struct GNUNET_SCHEDULER_Driver *driver;
712   struct DriverContext context = { .scheduled_head = NULL,
713                                    .scheduled_tail = NULL,
714                                    .timeout = GNUNET_TIME_absolute_get() };
715
716   driver = GNUNET_SCHEDULER_driver_select();
717   driver->cls = &context;
718   sh = GNUNET_SCHEDULER_driver_init(driver);
719   GNUNET_SCHEDULER_add_with_reason_and_priority(task,
720                                                 task_cls,
721                                                 GNUNET_SCHEDULER_REASON_STARTUP,
722                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT);
723   select_loop(sh,
724               &context);
725   GNUNET_SCHEDULER_driver_done(sh);
726   GNUNET_free(driver);
727 }
728
729
730 /**
731  * Obtain the task context, giving the reason why the current task was
732  * started.
733  *
734  * @return current tasks' scheduler context
735  */
736 const struct GNUNET_SCHEDULER_TaskContext *
737 GNUNET_SCHEDULER_get_task_context()
738 {
739   GNUNET_assert(NULL != active_task);
740   return &tc;
741 }
742
743
744 /**
745  * Get information about the current load of this scheduler.  Use this
746  * function to determine if an elective task should be added or simply
747  * dropped (if the decision should be made based on the number of
748  * tasks ready to run).
749  *
750  * @param p priority level to look at
751  * @return number of tasks pending right now
752  */
753 unsigned int
754 GNUNET_SCHEDULER_get_load(enum GNUNET_SCHEDULER_Priority p)
755 {
756   struct GNUNET_SCHEDULER_Task *pos;
757   unsigned int ret;
758
759   GNUNET_assert(NULL != active_task);
760   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
761     return ready_count;
762   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
763     p = current_priority;
764   ret = 0;
765   for (pos = ready_head[check_priority(p)]; NULL != pos; pos = pos->next)
766     ret++;
767   return ret;
768 }
769
770
771 void
772 init_fd_info(struct GNUNET_SCHEDULER_Task *t,
773              const struct GNUNET_NETWORK_Handle *const *read_nh,
774              unsigned int read_nh_len,
775              const struct GNUNET_NETWORK_Handle *const *write_nh,
776              unsigned int write_nh_len,
777              const struct GNUNET_DISK_FileHandle *const *read_fh,
778              unsigned int read_fh_len,
779              const struct GNUNET_DISK_FileHandle *const *write_fh,
780              unsigned int write_fh_len)
781 {
782   // FIXME: if we have exactly two network handles / exactly two file handles
783   // and they are equal, we can make one FdInfo with both
784   // GNUNET_SCHEDULER_ET_IN and GNUNET_SCHEDULER_ET_OUT set.
785   struct GNUNET_SCHEDULER_FdInfo *fdi;
786
787   t->fds_len = read_nh_len + write_nh_len + read_fh_len + write_fh_len;
788   if (1 == t->fds_len)
789     {
790       fdi = &t->fdx;
791       t->fds = fdi;
792       if (1 == read_nh_len)
793         {
794           GNUNET_assert(NULL != read_nh);
795           GNUNET_assert(NULL != *read_nh);
796           fdi->fd = *read_nh;
797           fdi->et = GNUNET_SCHEDULER_ET_IN;
798           fdi->sock = GNUNET_NETWORK_get_fd(*read_nh);
799           t->read_fd = fdi->sock;
800           t->write_fd = -1;
801         }
802       else if (1 == write_nh_len)
803         {
804           GNUNET_assert(NULL != write_nh);
805           GNUNET_assert(NULL != *write_nh);
806           fdi->fd = *write_nh;
807           fdi->et = GNUNET_SCHEDULER_ET_OUT;
808           fdi->sock = GNUNET_NETWORK_get_fd(*write_nh);
809           t->read_fd = -1;
810           t->write_fd = fdi->sock;
811         }
812       else if (1 == read_fh_len)
813         {
814           GNUNET_assert(NULL != read_fh);
815           GNUNET_assert(NULL != *read_fh);
816           fdi->fh = *read_fh;
817           fdi->et = GNUNET_SCHEDULER_ET_IN;
818           fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
819           t->read_fd = fdi->sock;
820           t->write_fd = -1;
821         }
822       else
823         {
824           GNUNET_assert(NULL != write_fh);
825           GNUNET_assert(NULL != *write_fh);
826           fdi->fh = *write_fh;
827           fdi->et = GNUNET_SCHEDULER_ET_OUT;
828           fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
829           t->read_fd = -1;
830           t->write_fd = fdi->sock;
831         }
832     }
833   else
834     {
835       fdi = GNUNET_new_array(t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
836       t->fds = fdi;
837       t->read_fd = -1;
838       t->write_fd = -1;
839       unsigned int i;
840       for (i = 0; i != read_nh_len; ++i)
841         {
842           fdi->fd = read_nh[i];
843           GNUNET_assert(NULL != fdi->fd);
844           fdi->et = GNUNET_SCHEDULER_ET_IN;
845           fdi->sock = GNUNET_NETWORK_get_fd(read_nh[i]);
846           ++fdi;
847         }
848       for (i = 0; i != write_nh_len; ++i)
849         {
850           fdi->fd = write_nh[i];
851           GNUNET_assert(NULL != fdi->fd);
852           fdi->et = GNUNET_SCHEDULER_ET_OUT;
853           fdi->sock = GNUNET_NETWORK_get_fd(write_nh[i]);
854           ++fdi;
855         }
856       for (i = 0; i != read_fh_len; ++i)
857         {
858           fdi->fh = read_fh[i];
859           GNUNET_assert(NULL != fdi->fh);
860           fdi->et = GNUNET_SCHEDULER_ET_IN;
861           fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
862           ++fdi;
863         }
864       for (i = 0; i != write_fh_len; ++i)
865         {
866           fdi->fh = write_fh[i];
867           GNUNET_assert(NULL != fdi->fh);
868           fdi->et = GNUNET_SCHEDULER_ET_OUT;
869           fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
870           ++fdi;
871         }
872     }
873 }
874
875
876 /**
877  * calls the given function @a func on each FdInfo related to @a t.
878  * Optionally updates the event type field in each FdInfo after calling
879  * @a func.
880  *
881  * @param t the task
882  * @param driver_func the function to call with each FdInfo contained in
883  *                    in @a t
884  * @param if_not_ready only call @a driver_func on FdInfos that are not
885  *                     ready
886  * @param et the event type to be set in each FdInfo after calling
887  *           @a driver_func on it, or -1 if no updating not desired.
888  */
889 static void
890 driver_add_multiple(struct GNUNET_SCHEDULER_Task *t)
891 {
892   struct GNUNET_SCHEDULER_FdInfo *fdi;
893   int success = GNUNET_YES;
894
895   for (unsigned int i = 0; i != t->fds_len; ++i)
896     {
897       fdi = &t->fds[i];
898       success = scheduler_driver->add(scheduler_driver->cls,
899                                       t,
900                                       fdi) && success;
901       fdi->et = GNUNET_SCHEDULER_ET_NONE;
902     }
903   if (GNUNET_YES != success)
904     {
905       LOG(GNUNET_ERROR_TYPE_ERROR,
906           "driver could not add task\n");
907     }
908 }
909
910
911 static void
912 install_parent_control_handler(void *cls)
913 {
914   (void)cls;
915   install_parent_control_task = NULL;
916   GNUNET_OS_install_parent_control_handler(NULL);
917 }
918
919
920 static void
921 shutdown_pipe_cb(void *cls)
922 {
923   char c;
924   const struct GNUNET_DISK_FileHandle *pr;
925
926   (void)cls;
927   shutdown_pipe_task = NULL;
928   pr = GNUNET_DISK_pipe_handle(shutdown_pipe_handle,
929                                GNUNET_DISK_PIPE_END_READ);
930   GNUNET_assert(!GNUNET_DISK_handle_invalid(pr));
931   /* consume the signal */
932   GNUNET_DISK_file_read(pr, &c, sizeof(c));
933   /* mark all active tasks as ready due to shutdown */
934   GNUNET_SCHEDULER_shutdown();
935   shutdown_pipe_task =
936     GNUNET_SCHEDULER_add_read_file(GNUNET_TIME_UNIT_FOREVER_REL,
937                                    pr,
938                                    &shutdown_pipe_cb,
939                                    NULL);
940 }
941
942
943 /**
944  * Cancel the task with the specified identifier.
945  * The task must not yet have run. Only allowed to be called as long as the
946  * scheduler is running, that is one of the following conditions is met:
947  *
948  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
949  * - #GNUNET_SCHEDULER_driver_init has been run and
950  *   #GNUNET_SCHEDULER_driver_done has not been called yet
951  *
952  * @param task id of the task to cancel
953  * @return original closure of the task
954  */
955 void *
956 GNUNET_SCHEDULER_cancel(struct GNUNET_SCHEDULER_Task *task)
957 {
958   enum GNUNET_SCHEDULER_Priority p;
959   int is_fd_task;
960   void *ret;
961
962   LOG(GNUNET_ERROR_TYPE_DEBUG,
963       "canceling task %p\n",
964       task);
965
966   /* scheduler must be running */
967   GNUNET_assert(NULL != scheduler_driver);
968   is_fd_task = (NULL != task->fds);
969   if (is_fd_task)
970     {
971       int del_result = scheduler_driver->del(scheduler_driver->cls, task);
972       if (GNUNET_OK != del_result)
973         {
974           LOG(GNUNET_ERROR_TYPE_ERROR,
975               "driver could not delete task\n");
976           GNUNET_assert(0);
977         }
978     }
979   if (!task->in_ready_list)
980     {
981       if (is_fd_task)
982         {
983           GNUNET_CONTAINER_DLL_remove(pending_head,
984                                       pending_tail,
985                                       task);
986         }
987       else if (GNUNET_YES == task->on_shutdown)
988         {
989           GNUNET_CONTAINER_DLL_remove(shutdown_head,
990                                       shutdown_tail,
991                                       task);
992         }
993       else
994         {
995           GNUNET_CONTAINER_DLL_remove(pending_timeout_head,
996                                       pending_timeout_tail,
997                                       task);
998           if (pending_timeout_last == task)
999             pending_timeout_last = NULL;
1000         }
1001     }
1002   else
1003     {
1004       p = check_priority(task->priority);
1005       GNUNET_CONTAINER_DLL_remove(ready_head[p],
1006                                   ready_tail[p],
1007                                   task);
1008       ready_count--;
1009     }
1010   ret = task->callback_cls;
1011   destroy_task(task);
1012   return ret;
1013 }
1014
1015
1016 /**
1017  * Initialize backtrace data for task @a t
1018  *
1019  * @param t task to initialize
1020  */
1021 static void
1022 init_backtrace(struct GNUNET_SCHEDULER_Task *t)
1023 {
1024 #if EXECINFO
1025   void *backtrace_array[MAX_TRACE_DEPTH];
1026
1027   t->num_backtrace_strings
1028     = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1029   t->backtrace_strings =
1030     backtrace_symbols(backtrace_array,
1031                       t->num_backtrace_strings);
1032   dump_backtrace(t);
1033 #else
1034   (void)t;
1035 #endif
1036 }
1037
1038
1039 /**
1040  * Continue the current execution with the given function.  This is
1041  * similar to the other "add" functions except that there is no delay
1042  * and the reason code can be specified.
1043  *
1044  * @param task main function of the task
1045  * @param task_cls closure for @a task
1046  * @param reason reason for task invocation
1047  * @param priority priority to use for the task
1048  */
1049 void
1050 GNUNET_SCHEDULER_add_with_reason_and_priority(GNUNET_SCHEDULER_TaskCallback task,
1051                                               void *task_cls,
1052                                               enum GNUNET_SCHEDULER_Reason reason,
1053                                               enum GNUNET_SCHEDULER_Priority priority)
1054 {
1055   struct GNUNET_SCHEDULER_Task *t;
1056
1057   /* scheduler must be running */
1058   GNUNET_assert(NULL != scheduler_driver);
1059   GNUNET_assert(NULL != task);
1060   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1061   t->read_fd = -1;
1062   t->write_fd = -1;
1063   t->callback = task;
1064   t->callback_cls = task_cls;
1065 #if PROFILE_DELAYS
1066   t->start_time = GNUNET_TIME_absolute_get();
1067 #endif
1068   t->reason = reason;
1069   t->priority = check_priority(priority);
1070   t->lifeness = current_lifeness;
1071   LOG(GNUNET_ERROR_TYPE_DEBUG,
1072       "Adding continuation task %p\n",
1073       t);
1074   init_backtrace(t);
1075   queue_ready_task(t);
1076 }
1077
1078
1079 /**
1080  * Schedule a new task to be run at the specified time.  The task
1081  * will be scheduled for execution at time @a at.
1082  *
1083  * @param at time when the operation should run
1084  * @param priority priority to use for the task
1085  * @param task main function of the task
1086  * @param task_cls closure of @a task
1087  * @return unique task identifier for the job
1088  *         only valid until @a task is started!
1089  */
1090 struct GNUNET_SCHEDULER_Task *
1091 GNUNET_SCHEDULER_add_at_with_priority(struct GNUNET_TIME_Absolute at,
1092                                       enum GNUNET_SCHEDULER_Priority priority,
1093                                       GNUNET_SCHEDULER_TaskCallback task,
1094                                       void *task_cls)
1095 {
1096   struct GNUNET_SCHEDULER_Task *t;
1097   struct GNUNET_SCHEDULER_Task *pos;
1098   struct GNUNET_SCHEDULER_Task *prev;
1099
1100   /* scheduler must be running */
1101   GNUNET_assert(NULL != scheduler_driver);
1102   GNUNET_assert(NULL != task);
1103   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1104   GNUNET_async_scope_get(&t->scope);
1105   t->callback = task;
1106   t->callback_cls = task_cls;
1107   t->read_fd = -1;
1108   t->write_fd = -1;
1109 #if PROFILE_DELAYS
1110   t->start_time = GNUNET_TIME_absolute_get();
1111 #endif
1112   t->timeout = at;
1113   t->priority = check_priority(priority);
1114   t->lifeness = current_lifeness;
1115   /* try tail first (optimization in case we are
1116    * appending to a long list of tasks with timeouts) */
1117   if ((NULL == pending_timeout_head) ||
1118       (at.abs_value_us < pending_timeout_head->timeout.abs_value_us))
1119     {
1120       GNUNET_CONTAINER_DLL_insert(pending_timeout_head,
1121                                   pending_timeout_tail,
1122                                   t);
1123     }
1124   else
1125     {
1126       /* first move from heuristic start backwards to before start time */
1127       prev = pending_timeout_last;
1128       while ((NULL != prev) &&
1129              (prev->timeout.abs_value_us > t->timeout.abs_value_us))
1130         prev = prev->prev;
1131       /* now, move from heuristic start (or head of list) forward to insertion point */
1132       if (NULL == prev)
1133         pos = pending_timeout_head;
1134       else
1135         pos = prev->next;
1136       while ((NULL != pos) && (pos->timeout.abs_value_us <= t->timeout.abs_value_us))
1137         {
1138           prev = pos;
1139           pos = pos->next;
1140         }
1141       GNUNET_CONTAINER_DLL_insert_after(pending_timeout_head,
1142                                         pending_timeout_tail,
1143                                         prev,
1144                                         t);
1145     }
1146   /* finally, update heuristic insertion point to last insertion... */
1147   pending_timeout_last = t;
1148
1149   LOG(GNUNET_ERROR_TYPE_DEBUG,
1150       "Adding task %p\n",
1151       t);
1152   init_backtrace(t);
1153   return t;
1154 }
1155
1156
1157 /**
1158  * Schedule a new task to be run with a specified delay.  The task
1159  * will be scheduled for execution once the delay has expired.
1160  *
1161  * @param delay when should this operation time out?
1162  * @param priority priority to use for the task
1163  * @param task main function of the task
1164  * @param task_cls closure of @a task
1165  * @return unique task identifier for the job
1166  *         only valid until @a task is started!
1167  */
1168 struct GNUNET_SCHEDULER_Task *
1169 GNUNET_SCHEDULER_add_delayed_with_priority(struct GNUNET_TIME_Relative delay,
1170                                            enum GNUNET_SCHEDULER_Priority priority,
1171                                            GNUNET_SCHEDULER_TaskCallback task,
1172                                            void *task_cls)
1173 {
1174   return GNUNET_SCHEDULER_add_at_with_priority(GNUNET_TIME_relative_to_absolute(delay),
1175                                                priority,
1176                                                task,
1177                                                task_cls);
1178 }
1179
1180
1181 /**
1182  * Schedule a new task to be run with a specified priority.
1183  *
1184  * @param prio how important is the new task?
1185  * @param task main function of the task
1186  * @param task_cls closure of @a task
1187  * @return unique task identifier for the job
1188  *         only valid until @a task is started!
1189  */
1190 struct GNUNET_SCHEDULER_Task *
1191 GNUNET_SCHEDULER_add_with_priority(enum GNUNET_SCHEDULER_Priority prio,
1192                                    GNUNET_SCHEDULER_TaskCallback task,
1193                                    void *task_cls)
1194 {
1195   return GNUNET_SCHEDULER_add_delayed_with_priority(GNUNET_TIME_UNIT_ZERO,
1196                                                     prio,
1197                                                     task,
1198                                                     task_cls);
1199 }
1200
1201
1202 /**
1203  * Schedule a new task to be run at the specified time.  The task
1204  * will be scheduled for execution once specified time has been
1205  * reached. It will be run with the DEFAULT priority.
1206  *
1207  * @param at time at which this operation should run
1208  * @param task main function of the task
1209  * @param task_cls closure of @a task
1210  * @return unique task identifier for the job
1211  *         only valid until @a task is started!
1212  */
1213 struct GNUNET_SCHEDULER_Task *
1214 GNUNET_SCHEDULER_add_at(struct GNUNET_TIME_Absolute at,
1215                         GNUNET_SCHEDULER_TaskCallback task,
1216                         void *task_cls)
1217 {
1218   return GNUNET_SCHEDULER_add_at_with_priority(at,
1219                                                GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1220                                                task,
1221                                                task_cls);
1222 }
1223
1224
1225 /**
1226  * Schedule a new task to be run with a specified delay.  The task
1227  * will be scheduled for execution once the delay has expired. It
1228  * will be run with the DEFAULT priority.
1229  *
1230  * @param delay when should this operation time out?
1231  * @param task main function of the task
1232  * @param task_cls closure of @a task
1233  * @return unique task identifier for the job
1234  *         only valid until @a task is started!
1235  */
1236 struct GNUNET_SCHEDULER_Task *
1237 GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay,
1238                              GNUNET_SCHEDULER_TaskCallback task,
1239                              void *task_cls)
1240 {
1241   return GNUNET_SCHEDULER_add_delayed_with_priority(delay,
1242                                                     GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1243                                                     task,
1244                                                     task_cls);
1245 }
1246
1247
1248 /**
1249  * Schedule a new task to be run as soon as possible.  Note that this
1250  * does not guarantee that this will be the next task that is being
1251  * run, as other tasks with higher priority (or that are already ready
1252  * to run) might get to run first.  Just as with delays, clients must
1253  * not rely on any particular order of execution between tasks
1254  * scheduled concurrently.
1255  *
1256  * The task will be run with the DEFAULT priority.
1257  *
1258  * @param task main function of the task
1259  * @param task_cls closure of @a task
1260  * @return unique task identifier for the job
1261  *         only valid until @a task is started!
1262  */
1263 struct GNUNET_SCHEDULER_Task *
1264 GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task,
1265                          void *task_cls)
1266 {
1267   return GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_ZERO,
1268                                       task,
1269                                       task_cls);
1270 }
1271
1272
1273 /**
1274  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1275  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1276  * invoked.
1277  *
1278  * @param task main function of the task
1279  * @param task_cls closure of @a task
1280  * @return unique task identifier for the job
1281  *         only valid until @a task is started!
1282  */
1283 struct GNUNET_SCHEDULER_Task *
1284 GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task,
1285                               void *task_cls)
1286 {
1287   struct GNUNET_SCHEDULER_Task *t;
1288
1289   /* scheduler must be running */
1290   GNUNET_assert(NULL != scheduler_driver);
1291   GNUNET_assert(NULL != task);
1292   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1293   GNUNET_async_scope_get(&t->scope);
1294   t->callback = task;
1295   t->callback_cls = task_cls;
1296   t->read_fd = -1;
1297   t->write_fd = -1;
1298 #if PROFILE_DELAYS
1299   t->start_time = GNUNET_TIME_absolute_get();
1300 #endif
1301   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1302   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1303   t->on_shutdown = GNUNET_YES;
1304   t->lifeness = GNUNET_NO;
1305   GNUNET_CONTAINER_DLL_insert(shutdown_head,
1306                               shutdown_tail,
1307                               t);
1308   LOG(GNUNET_ERROR_TYPE_DEBUG,
1309       "Adding shutdown task %p\n",
1310       t);
1311   init_backtrace(t);
1312   return t;
1313 }
1314
1315
1316 /**
1317  * Schedule a new task to be run as soon as possible with the
1318  * (transitive) ignore-shutdown flag either explicitly set or
1319  * explicitly enabled.  This task (and all tasks created from it,
1320  * other than by another call to this function) will either count or
1321  * not count for the "lifeness" of the process.  This API is only
1322  * useful in a few special cases.
1323  *
1324  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
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_now_with_lifeness(int lifeness,
1332                                        GNUNET_SCHEDULER_TaskCallback task,
1333                                        void *task_cls)
1334 {
1335   struct GNUNET_SCHEDULER_Task *ret;
1336
1337   ret = GNUNET_SCHEDULER_add_now(task, task_cls);
1338   ret->lifeness = lifeness;
1339   return ret;
1340 }
1341
1342
1343 #if DEBUG_FDS
1344 /**
1345  * check a raw file descriptor and abort if it is bad (for debugging purposes)
1346  *
1347  * @param t the task related to the file descriptor
1348  * @param raw_fd the raw file descriptor to check
1349  */
1350 void
1351 check_fd(struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1352 {
1353   if (-1 != raw_fd)
1354     {
1355       int flags = fcntl(raw_fd, F_GETFD);
1356
1357       if ((flags == -1) && (errno == EBADF))
1358         {
1359           LOG(GNUNET_ERROR_TYPE_ERROR,
1360               "Got invalid file descriptor %d!\n",
1361               raw_fd);
1362           init_backtrace(t);
1363           GNUNET_assert(0);
1364         }
1365     }
1366 }
1367 #endif
1368
1369
1370 /**
1371  * Schedule a new task to be run with a specified delay or when any of
1372  * the specified file descriptor sets is ready.  The delay can be used
1373  * as a timeout on the socket(s) being ready.  The task will be
1374  * scheduled for execution once either the delay has expired or any of
1375  * the socket operations is ready.  This is the most general
1376  * function of the "add" family.  Note that the "prerequisite_task"
1377  * must be satisfied in addition to any of the other conditions.  In
1378  * other words, the task will be started when
1379  * <code>
1380  * (prerequisite-run)
1381  * && (delay-ready
1382  *     || any-rs-ready
1383  *     || any-ws-ready)
1384  * </code>
1385  *
1386  * @param delay how long should we wait?
1387  * @param priority priority to use
1388  * @param rfd file descriptor we want to read (can be -1)
1389  * @param wfd file descriptors we want to write (can be -1)
1390  * @param task main function of the task
1391  * @param task_cls closure of @a task
1392  * @return unique task identifier for the job
1393  *         only valid until @a task is started!
1394  */
1395 static struct GNUNET_SCHEDULER_Task *
1396 add_without_sets(struct GNUNET_TIME_Relative delay,
1397                  enum GNUNET_SCHEDULER_Priority priority,
1398                  const struct GNUNET_NETWORK_Handle *read_nh,
1399                  const struct GNUNET_NETWORK_Handle *write_nh,
1400                  const struct GNUNET_DISK_FileHandle *read_fh,
1401                  const struct GNUNET_DISK_FileHandle *write_fh,
1402                  GNUNET_SCHEDULER_TaskCallback task,
1403                  void *task_cls)
1404 {
1405   struct GNUNET_SCHEDULER_Task *t;
1406
1407   /* scheduler must be running */
1408   GNUNET_assert(NULL != scheduler_driver);
1409   GNUNET_assert(NULL != task);
1410   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1411   GNUNET_async_scope_get(&t->scope);
1412   init_fd_info(t,
1413                &read_nh,
1414                read_nh ? 1 : 0,
1415                &write_nh,
1416                write_nh ? 1 : 0,
1417                &read_fh,
1418                read_fh ? 1 : 0,
1419                &write_fh,
1420                write_fh ? 1 : 0);
1421   t->callback = task;
1422   t->callback_cls = task_cls;
1423 #if DEBUG_FDS
1424   check_fd(t, NULL != read_nh ? GNUNET_NETWORK_get_fd(read_nh) : -1);
1425   check_fd(t, NULL != write_nh ? GNUNET_NETWORK_get_fd(write_nh) : -1);
1426   check_fd(t, NULL != read_fh ? read_fh->fd : -1);
1427   check_fd(t, NULL != write_fh ? write_fh->fd : -1);
1428 #endif
1429 #if PROFILE_DELAYS
1430   t->start_time = GNUNET_TIME_absolute_get();
1431 #endif
1432   t->timeout = GNUNET_TIME_relative_to_absolute(delay);
1433   t->priority = check_priority((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1434   t->lifeness = current_lifeness;
1435   GNUNET_CONTAINER_DLL_insert(pending_head,
1436                               pending_tail,
1437                               t);
1438   driver_add_multiple(t);
1439   max_priority_added = GNUNET_MAX(max_priority_added,
1440                                   t->priority);
1441   init_backtrace(t);
1442   return t;
1443 }
1444
1445
1446 /**
1447  * Schedule a new task to be run with a specified delay or when the
1448  * specified file descriptor is ready for reading.  The delay can be
1449  * used as a timeout on the socket being ready.  The task will be
1450  * scheduled for execution once either the delay has expired or the
1451  * socket operation is ready.  It will be run with the DEFAULT priority.
1452  * Only allowed to be called as long as the scheduler is running, that
1453  * is one of the following conditions is met:
1454  *
1455  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1456  * - #GNUNET_SCHEDULER_driver_init has been run and
1457  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1458  *
1459  * @param delay when should this operation time out?
1460  * @param rfd read file-descriptor
1461  * @param task main function of the task
1462  * @param task_cls closure of @a task
1463  * @return unique task identifier for the job
1464  *         only valid until @a task is started!
1465  */
1466 struct GNUNET_SCHEDULER_Task *
1467 GNUNET_SCHEDULER_add_read_net(struct GNUNET_TIME_Relative delay,
1468                               struct GNUNET_NETWORK_Handle *rfd,
1469                               GNUNET_SCHEDULER_TaskCallback task,
1470                               void *task_cls)
1471 {
1472   return GNUNET_SCHEDULER_add_read_net_with_priority(delay,
1473                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1474                                                      rfd, task, task_cls);
1475 }
1476
1477
1478 /**
1479  * Schedule a new task to be run with a specified priority and to be
1480  * run after the specified delay or when the specified file descriptor
1481  * is ready for reading.  The delay can be used as a timeout on the
1482  * socket being ready.  The task will be scheduled for execution once
1483  * either the delay has expired or the socket operation is ready.  It
1484  * will be run with the DEFAULT priority.
1485  * Only allowed to be called as long as the scheduler is running, that
1486  * is one of the following conditions is met:
1487  *
1488  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1489  * - #GNUNET_SCHEDULER_driver_init has been run and
1490  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1491  *
1492  * @param delay when should this operation time out?
1493  * @param priority priority to use for the task
1494  * @param rfd read file-descriptor
1495  * @param task main function of the task
1496  * @param task_cls closure of @a task
1497  * @return unique task identifier for the job
1498  *         only valid until @a task is started!
1499  */
1500 struct GNUNET_SCHEDULER_Task *
1501 GNUNET_SCHEDULER_add_read_net_with_priority(struct GNUNET_TIME_Relative delay,
1502                                             enum GNUNET_SCHEDULER_Priority priority,
1503                                             struct GNUNET_NETWORK_Handle *rfd,
1504                                             GNUNET_SCHEDULER_TaskCallback task,
1505                                             void *task_cls)
1506 {
1507   return GNUNET_SCHEDULER_add_net_with_priority(delay, priority,
1508                                                 rfd,
1509                                                 GNUNET_YES,
1510                                                 GNUNET_NO,
1511                                                 task, task_cls);
1512 }
1513
1514
1515 /**
1516  * Schedule a new task to be run with a specified delay or when the
1517  * specified file descriptor is ready for writing.  The delay can be
1518  * used as a timeout on the socket being ready.  The task will be
1519  * scheduled for execution once either the delay has expired or the
1520  * socket operation is ready.  It will be run with the priority of
1521  * the calling task.
1522  * Only allowed to be called as long as the scheduler is running, that
1523  * is one of the following conditions is met:
1524  *
1525  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1526  * - #GNUNET_SCHEDULER_driver_init has been run and
1527  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1528  *
1529  * @param delay when should this operation time out?
1530  * @param wfd write file-descriptor
1531  * @param task main function of the task
1532  * @param task_cls closure of @a task
1533  * @return unique task identifier for the job
1534  *         only valid until @a task is started!
1535  */
1536 struct GNUNET_SCHEDULER_Task *
1537 GNUNET_SCHEDULER_add_write_net(struct GNUNET_TIME_Relative delay,
1538                                struct GNUNET_NETWORK_Handle *wfd,
1539                                GNUNET_SCHEDULER_TaskCallback task,
1540                                void *task_cls)
1541 {
1542   return GNUNET_SCHEDULER_add_net_with_priority(delay,
1543                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1544                                                 wfd,
1545                                                 GNUNET_NO, GNUNET_YES,
1546                                                 task, task_cls);
1547 }
1548
1549 /**
1550  * Schedule a new task to be run with a specified delay or when the
1551  * specified file descriptor is ready.  The delay can be
1552  * used as a timeout on the socket being ready.  The task will be
1553  * scheduled for execution once either the delay has expired or the
1554  * socket operation is ready.
1555  * Only allowed to be called as long as the scheduler is running, that
1556  * is one of the following conditions is met:
1557  *
1558  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1559  * - #GNUNET_SCHEDULER_driver_init has been run and
1560  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1561  *
1562  * @param delay when should this operation time out?
1563  * @param priority priority of the task
1564  * @param fd file-descriptor
1565  * @param on_read whether to poll the file-descriptor for readability
1566  * @param on_write whether to poll the file-descriptor for writability
1567  * @param task main function of the task
1568  * @param task_cls closure of task
1569  * @return unique task identifier for the job
1570  *         only valid until "task" is started!
1571  */
1572 struct GNUNET_SCHEDULER_Task *
1573 GNUNET_SCHEDULER_add_net_with_priority(struct GNUNET_TIME_Relative delay,
1574                                        enum GNUNET_SCHEDULER_Priority priority,
1575                                        struct GNUNET_NETWORK_Handle *fd,
1576                                        int on_read,
1577                                        int on_write,
1578                                        GNUNET_SCHEDULER_TaskCallback task,
1579                                        void *task_cls)
1580 {
1581   /* scheduler must be running */
1582   GNUNET_assert(NULL != scheduler_driver);
1583   GNUNET_assert(on_read || on_write);
1584   GNUNET_assert(GNUNET_NETWORK_get_fd(fd) >= 0);
1585   return add_without_sets(delay, priority,
1586                           on_read  ? fd : NULL,
1587                           on_write ? fd : NULL,
1588                           NULL,
1589                           NULL,
1590                           task, task_cls);
1591 }
1592
1593
1594 /**
1595  * Schedule a new task to be run with a specified delay or when the
1596  * specified file descriptor is ready for reading.  The delay can be
1597  * used as a timeout on the socket being ready.  The task will be
1598  * scheduled for execution once either the delay has expired or the
1599  * socket operation is ready. It will be run with the DEFAULT priority.
1600  * Only allowed to be called as long as the scheduler is running, that
1601  * is one of the following conditions is met:
1602  *
1603  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1604  * - #GNUNET_SCHEDULER_driver_init has been run and
1605  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1606  *
1607  * @param delay when should this operation time out?
1608  * @param rfd read file-descriptor
1609  * @param task main function of the task
1610  * @param task_cls closure of @a task
1611  * @return unique task identifier for the job
1612  *         only valid until @a task is started!
1613  */
1614 struct GNUNET_SCHEDULER_Task *
1615 GNUNET_SCHEDULER_add_read_file(struct GNUNET_TIME_Relative delay,
1616                                const struct GNUNET_DISK_FileHandle *rfd,
1617                                GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1618 {
1619   return GNUNET_SCHEDULER_add_file_with_priority(
1620     delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1621     rfd, GNUNET_YES, GNUNET_NO,
1622     task, task_cls);
1623 }
1624
1625
1626 /**
1627  * Schedule a new task to be run with a specified delay or when the
1628  * specified file descriptor is ready for writing.  The delay can be
1629  * used as a timeout on the socket being ready.  The task will be
1630  * scheduled for execution once either the delay has expired or the
1631  * socket operation is ready. It will be run with the DEFAULT priority.
1632  * Only allowed to be called as long as the scheduler is running, that
1633  * is one of the following conditions is met:
1634  *
1635  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1636  * - #GNUNET_SCHEDULER_driver_init has been run and
1637  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1638  *
1639  * @param delay when should this operation time out?
1640  * @param wfd write file-descriptor
1641  * @param task main function of the task
1642  * @param task_cls closure of @a task
1643  * @return unique task identifier for the job
1644  *         only valid until @a task is started!
1645  */
1646 struct GNUNET_SCHEDULER_Task *
1647 GNUNET_SCHEDULER_add_write_file(struct GNUNET_TIME_Relative delay,
1648                                 const struct GNUNET_DISK_FileHandle *wfd,
1649                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1650 {
1651   return GNUNET_SCHEDULER_add_file_with_priority(
1652     delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1653     wfd, GNUNET_NO, GNUNET_YES,
1654     task, task_cls);
1655 }
1656
1657
1658 /**
1659  * Schedule a new task to be run with a specified delay or when the
1660  * specified file descriptor is ready.  The delay can be
1661  * used as a timeout on the socket being ready.  The task will be
1662  * scheduled for execution once either the delay has expired or the
1663  * socket operation is ready.
1664  * Only allowed to be called as long as the scheduler is running, that
1665  * is one of the following conditions is met:
1666  *
1667  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1668  * - #GNUNET_SCHEDULER_driver_init has been run and
1669  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1670  *
1671  * @param delay when should this operation time out?
1672  * @param priority priority of the task
1673  * @param fd file-descriptor
1674  * @param on_read whether to poll the file-descriptor for readability
1675  * @param on_write whether to poll the file-descriptor for writability
1676  * @param task main function of the task
1677  * @param task_cls closure of @a task
1678  * @return unique task identifier for the job
1679  *         only valid until @a task is started!
1680  */
1681 struct GNUNET_SCHEDULER_Task *
1682 GNUNET_SCHEDULER_add_file_with_priority(struct GNUNET_TIME_Relative delay,
1683                                         enum GNUNET_SCHEDULER_Priority priority,
1684                                         const struct GNUNET_DISK_FileHandle *fd,
1685                                         int on_read, int on_write,
1686                                         GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1687 {
1688   /* scheduler must be running */
1689   GNUNET_assert(NULL != scheduler_driver);
1690   GNUNET_assert(on_read || on_write);
1691   GNUNET_assert(fd->fd >= 0);
1692   return add_without_sets(delay, priority,
1693                           NULL,
1694                           NULL,
1695                           on_read ? fd : NULL,
1696                           on_write ? fd : NULL,
1697                           task, task_cls);
1698 }
1699
1700
1701 void
1702 extract_handles(const struct GNUNET_NETWORK_FDSet *fdset,
1703                 const struct GNUNET_NETWORK_Handle ***ntarget,
1704                 unsigned int *extracted_nhandles,
1705                 const struct GNUNET_DISK_FileHandle ***ftarget,
1706                 unsigned int *extracted_fhandles)
1707 {
1708   // FIXME: this implementation only works for unix, for WIN32 the file handles
1709   // in fdset must be handled separately
1710   const struct GNUNET_NETWORK_Handle **nhandles;
1711   const struct GNUNET_DISK_FileHandle **fhandles;
1712   unsigned int nhandles_len;
1713   unsigned int fhandles_len;
1714
1715   nhandles = NULL;
1716   fhandles = NULL;
1717   nhandles_len = 0;
1718   fhandles_len = 0;
1719   for (int sock = 0; sock != fdset->nsds; ++sock)
1720     {
1721       if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native(fdset, sock))
1722         {
1723           struct GNUNET_NETWORK_Handle *nhandle;
1724           struct GNUNET_DISK_FileHandle *fhandle;
1725
1726           nhandle = GNUNET_NETWORK_socket_box_native(sock);
1727           if (NULL != nhandle)
1728             {
1729               GNUNET_array_append(nhandles, nhandles_len, nhandle);
1730             }
1731           else
1732             {
1733               fhandle = GNUNET_DISK_get_handle_from_int_fd(sock);
1734               if (NULL != fhandle)
1735                 {
1736                   GNUNET_array_append(fhandles, fhandles_len, fhandle);
1737                 }
1738               else
1739                 {
1740                   GNUNET_assert(0);
1741                 }
1742             }
1743         }
1744     }
1745   *ntarget = nhandles_len > 0 ? nhandles : NULL;
1746   *ftarget = fhandles_len > 0 ? fhandles : NULL;
1747   *extracted_nhandles = nhandles_len;
1748   *extracted_fhandles = fhandles_len;
1749 }
1750
1751
1752 /**
1753  * Schedule a new task to be run with a specified delay or when any of
1754  * the specified file descriptor sets is ready.  The delay can be used
1755  * as a timeout on the socket(s) being ready.  The task will be
1756  * scheduled for execution once either the delay has expired or any of
1757  * the socket operations is ready.  This is the most general
1758  * function of the "add" family.  Note that the "prerequisite_task"
1759  * must be satisfied in addition to any of the other conditions.  In
1760  * other words, the task will be started when
1761  * <code>
1762  * (prerequisite-run)
1763  * && (delay-ready
1764  *     || any-rs-ready
1765  *     || any-ws-ready) )
1766  * </code>
1767  * Only allowed to be called as long as the scheduler is running, that
1768  * is one of the following conditions is met:
1769  *
1770  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1771  * - #GNUNET_SCHEDULER_driver_init has been run and
1772  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1773  *
1774  * @param prio how important is this task?
1775  * @param delay how long should we wait?
1776  * @param rs set of file descriptors we want to read (can be NULL)
1777  * @param ws set of file descriptors we want to write (can be NULL)
1778  * @param task main function of the task
1779  * @param task_cls closure of @a task
1780  * @return unique task identifier for the job
1781  *         only valid until @a task is started!
1782  */
1783 struct GNUNET_SCHEDULER_Task *
1784 GNUNET_SCHEDULER_add_select(enum GNUNET_SCHEDULER_Priority prio,
1785                             struct GNUNET_TIME_Relative delay,
1786                             const struct GNUNET_NETWORK_FDSet *rs,
1787                             const struct GNUNET_NETWORK_FDSet *ws,
1788                             GNUNET_SCHEDULER_TaskCallback task,
1789                             void *task_cls)
1790 {
1791   struct GNUNET_SCHEDULER_Task *t;
1792   const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
1793   const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
1794   const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
1795   const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
1796   unsigned int read_nhandles_len = 0;
1797   unsigned int write_nhandles_len = 0;
1798   unsigned int read_fhandles_len = 0;
1799   unsigned int write_fhandles_len = 0;
1800
1801   /* scheduler must be running */
1802   GNUNET_assert(NULL != scheduler_driver);
1803   GNUNET_assert(NULL != task);
1804   int no_rs = (NULL == rs);
1805   int no_ws = (NULL == ws);
1806   int empty_rs = (NULL != rs) && (0 == rs->nsds);
1807   int empty_ws = (NULL != ws) && (0 == ws->nsds);
1808   int no_fds = (no_rs && no_ws) ||
1809                (empty_rs && empty_ws) ||
1810                (no_rs && empty_ws) ||
1811                (no_ws && empty_rs);
1812   if (!no_fds)
1813     {
1814       if (NULL != rs)
1815         {
1816           extract_handles(rs,
1817                           &read_nhandles,
1818                           &read_nhandles_len,
1819                           &read_fhandles,
1820                           &read_fhandles_len);
1821         }
1822       if (NULL != ws)
1823         {
1824           extract_handles(ws,
1825                           &write_nhandles,
1826                           &write_nhandles_len,
1827                           &write_fhandles,
1828                           &write_fhandles_len);
1829         }
1830     }
1831   /**
1832    * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
1833    * although its maximum FD number (nsds) is greater than 0. We handle
1834    * this case gracefully because some libraries such as libmicrohttpd
1835    * only provide a hint what the maximum FD number in an FD set might be
1836    * and not the exact FD number (see e.g. gnunet-rest-service.c)
1837    */
1838   int no_fds_extracted = (0 == read_nhandles_len) &&
1839                          (0 == read_fhandles_len) &&
1840                          (0 == write_nhandles_len) &&
1841                          (0 == write_fhandles_len);
1842   if (no_fds || no_fds_extracted)
1843     return GNUNET_SCHEDULER_add_delayed_with_priority(delay,
1844                                                       prio,
1845                                                       task,
1846                                                       task_cls);
1847   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1848   GNUNET_async_scope_get(&t->scope);
1849   init_fd_info(t,
1850                read_nhandles,
1851                read_nhandles_len,
1852                write_nhandles,
1853                write_nhandles_len,
1854                read_fhandles,
1855                read_fhandles_len,
1856                write_fhandles,
1857                write_fhandles_len);
1858   t->callback = task;
1859   t->callback_cls = task_cls;
1860   t->own_handles = GNUNET_YES;
1861   /* free the arrays of pointers to network / file handles, the actual
1862    * handles will be freed in destroy_task */
1863   GNUNET_array_grow(read_nhandles, read_nhandles_len, 0);
1864   GNUNET_array_grow(write_nhandles, write_nhandles_len, 0);
1865   GNUNET_array_grow(read_fhandles, read_fhandles_len, 0);
1866   GNUNET_array_grow(write_fhandles, write_fhandles_len, 0);
1867 #if PROFILE_DELAYS
1868   t->start_time = GNUNET_TIME_absolute_get();
1869 #endif
1870   t->timeout = GNUNET_TIME_relative_to_absolute(delay);
1871   t->priority =
1872     check_priority((prio ==
1873                     GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1874                    prio);
1875   t->lifeness = current_lifeness;
1876   GNUNET_CONTAINER_DLL_insert(pending_head,
1877                               pending_tail,
1878                               t);
1879   driver_add_multiple(t);
1880   max_priority_added = GNUNET_MAX(max_priority_added,
1881                                   t->priority);
1882   LOG(GNUNET_ERROR_TYPE_DEBUG,
1883       "Adding task %p\n",
1884       t);
1885   init_backtrace(t);
1886   return t;
1887 }
1888
1889
1890 /**
1891  * Function used by event-loop implementations to signal the scheduler
1892  * that a particular @a task is ready due to an event specified in the
1893  * et field of @a fdi.
1894  *
1895  * This function will then queue the task to notify the application
1896  * that the task is ready (with the respective priority).
1897  *
1898  * @param task the task that is ready
1899  * @param fdi information about the related FD
1900  */
1901 void
1902 GNUNET_SCHEDULER_task_ready(struct GNUNET_SCHEDULER_Task *task,
1903                             struct GNUNET_SCHEDULER_FdInfo *fdi)
1904 {
1905   enum GNUNET_SCHEDULER_Reason reason;
1906
1907   reason = task->reason;
1908   if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1909       (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)))
1910     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1911   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1912       (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)))
1913     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1914   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1915   task->reason = reason;
1916   if (GNUNET_NO == task->in_ready_list)
1917     {
1918       GNUNET_CONTAINER_DLL_remove(pending_head,
1919                                   pending_tail,
1920                                   task);
1921       queue_ready_task(task);
1922     }
1923 }
1924
1925
1926 /**
1927  * Function called by external event loop implementations to tell the
1928  * scheduler to run some of the tasks that are ready. Must be called
1929  * only after #GNUNET_SCHEDULER_driver_init has been called and before
1930  * #GNUNET_SCHEDULER_driver_done is called.
1931  * This function may return even though there are tasks left to run
1932  * just to give other tasks a chance as well.  If we return #GNUNET_YES,
1933  * the event loop implementation should call this function again as
1934  * soon as possible, while if we return #GNUNET_NO it must block until
1935  * either the operating system has more work (the scheduler has no more
1936  * work to do right now) or the timeout set by the scheduler (using the
1937  * set_wakeup callback) is reached.
1938  *
1939  * @param sh scheduler handle that was returned by
1940  *        #GNUNET_SCHEDULER_driver_init
1941  * @return #GNUNET_YES if there are more tasks that are ready,
1942  *         and thus we would like to run more (yield to avoid
1943  *         blocking other activities for too long) #GNUNET_NO
1944  *         if we are done running tasks (yield to block)
1945  */
1946 int
1947 GNUNET_SCHEDULER_do_work(struct GNUNET_SCHEDULER_Handle *sh)
1948 {
1949   enum GNUNET_SCHEDULER_Priority p;
1950   struct GNUNET_SCHEDULER_Task *pos;
1951   struct GNUNET_TIME_Absolute now;
1952
1953   /* check for tasks that reached the timeout! */
1954   now = GNUNET_TIME_absolute_get();
1955   pos = pending_timeout_head;
1956   while (NULL != pos)
1957     {
1958       struct GNUNET_SCHEDULER_Task *next = pos->next;
1959       if (now.abs_value_us >= pos->timeout.abs_value_us)
1960         pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1961       if (0 == pos->reason)
1962         break;
1963       GNUNET_CONTAINER_DLL_remove(pending_timeout_head,
1964                                   pending_timeout_tail,
1965                                   pos);
1966       if (pending_timeout_last == pos)
1967         pending_timeout_last = NULL;
1968       queue_ready_task(pos);
1969       pos = next;
1970     }
1971   pos = pending_head;
1972   while (NULL != pos)
1973     {
1974       struct GNUNET_SCHEDULER_Task *next = pos->next;
1975       if (now.abs_value_us >= pos->timeout.abs_value_us)
1976         {
1977           pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1978           GNUNET_CONTAINER_DLL_remove(pending_head,
1979                                       pending_tail,
1980                                       pos);
1981           queue_ready_task(pos);
1982         }
1983       pos = next;
1984     }
1985
1986   if (0 == ready_count)
1987     {
1988       struct GNUNET_TIME_Absolute timeout = get_timeout();
1989
1990       if (timeout.abs_value_us > now.abs_value_us)
1991         {
1992           /**
1993            * The event loop called this function before the current timeout was
1994            * reached (and no FD tasks are ready). This is acceptable if
1995            *
1996            * - the system time was changed while the driver was waiting for
1997            *   the timeout
1998            * - an external event loop called GNUnet API functions outside of
1999            *   the callbacks called in GNUNET_SCHEDULER_do_work and thus
2000            *   wasn't notified about the new timeout
2001            *
2002            * It might also mean we are busy-waiting because of a programming
2003            * error in the external event loop.
2004            */
2005           LOG(GNUNET_ERROR_TYPE_DEBUG,
2006               "GNUNET_SCHEDULER_do_work did not find any ready "
2007               "tasks and timeout has not been reached yet.\n");
2008         }
2009       else
2010         {
2011           /**
2012            * the current timeout was reached but no ready tasks were found,
2013            * internal scheduler error!
2014            */
2015           GNUNET_assert(0);
2016         }
2017     }
2018   else
2019     {
2020       /* find out which task priority level we are going to
2021          process this time */
2022       max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
2023       GNUNET_assert(NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
2024       /* yes, p>0 is correct, 0 is "KEEP" which should
2025        * always be an empty queue (see assertion)! */
2026       for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
2027         {
2028           pos = ready_head[p];
2029           if (NULL != pos)
2030             break;
2031         }
2032       GNUNET_assert(NULL != pos);       /* ready_count wrong? */
2033
2034       /* process all tasks at this priority level, then yield */
2035       while (NULL != (pos = ready_head[p]))
2036         {
2037           GNUNET_CONTAINER_DLL_remove(ready_head[p],
2038                                       ready_tail[p],
2039                                       pos);
2040           ready_count--;
2041           current_priority = pos->priority;
2042           current_lifeness = pos->lifeness;
2043           active_task = pos;
2044 #if PROFILE_DELAYS
2045           if (GNUNET_TIME_absolute_get_duration(pos->start_time).rel_value_us >
2046               DELAY_THRESHOLD.rel_value_us)
2047             {
2048               LOG(GNUNET_ERROR_TYPE_DEBUG,
2049                   "Task %p took %s to be scheduled\n",
2050                   pos,
2051                   GNUNET_STRINGS_relative_time_to_string(GNUNET_TIME_absolute_get_duration(pos->start_time),
2052                                                          GNUNET_YES));
2053             }
2054 #endif
2055           tc.reason = pos->reason;
2056           GNUNET_NETWORK_fdset_zero(sh->rs);
2057           GNUNET_NETWORK_fdset_zero(sh->ws);
2058           // FIXME: do we have to remove FdInfos from fds if they are not ready?
2059           tc.fds_len = pos->fds_len;
2060           tc.fds = pos->fds;
2061           for (unsigned int i = 0; i != pos->fds_len; ++i)
2062             {
2063               struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
2064               if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
2065                 {
2066                   GNUNET_NETWORK_fdset_set_native(sh->rs,
2067                                                   fdi->sock);
2068                 }
2069               if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
2070                 {
2071                   GNUNET_NETWORK_fdset_set_native(sh->ws,
2072                                                   fdi->sock);
2073                 }
2074             }
2075           tc.read_ready = sh->rs;
2076           tc.write_ready = sh->ws;
2077           LOG(GNUNET_ERROR_TYPE_DEBUG,
2078               "Running task %p\n",
2079               pos);
2080           GNUNET_assert(NULL != pos->callback);
2081           {
2082             struct GNUNET_AsyncScopeSave old_scope;
2083             if (pos->scope.have_scope)
2084               GNUNET_async_scope_enter(&pos->scope.scope_id, &old_scope);
2085             else
2086               GNUNET_async_scope_get(&old_scope);
2087             pos->callback(pos->callback_cls);
2088             GNUNET_async_scope_restore(&old_scope);
2089           }
2090           if (NULL != pos->fds)
2091             {
2092               int del_result = scheduler_driver->del(scheduler_driver->cls, pos);
2093               if (GNUNET_OK != del_result)
2094                 {
2095                   LOG(GNUNET_ERROR_TYPE_ERROR,
2096                       "driver could not delete task %p\n", pos);
2097                   GNUNET_assert(0);
2098                 }
2099             }
2100           active_task = NULL;
2101           dump_backtrace(pos);
2102           destroy_task(pos);
2103         }
2104     }
2105   shutdown_if_no_lifeness();
2106   if (0 == ready_count)
2107     {
2108       scheduler_driver->set_wakeup(scheduler_driver->cls,
2109                                    get_timeout());
2110       return GNUNET_NO;
2111     }
2112   scheduler_driver->set_wakeup(scheduler_driver->cls,
2113                                GNUNET_TIME_absolute_get());
2114   return GNUNET_YES;
2115 }
2116
2117
2118 /**
2119  * Function called by external event loop implementations to initialize
2120  * the scheduler. An external implementation has to provide @a driver
2121  * which contains callbacks for the scheduler (see definition of struct
2122  * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
2123  * external implementation to watch for events. If it detects any of
2124  * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
2125  * the scheduler handle it. If an event is related to a specific task
2126  * (e.g. the scheduler gave instructions to watch a file descriptor),
2127  * the external implementation is expected to mark that task ready
2128  * before by calling #GNUNET_SCHEDULER_task_ready.
2129
2130  * This function has to be called before any tasks are scheduled and
2131  * before GNUNET_SCHEDULER_do_work is called for the first time. It
2132  * allocates resources that have to be freed again by calling
2133  * #GNUNET_SCHEDULER_driver_done.
2134  *
2135  * This function installs the same signal handlers as
2136  * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
2137  * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
2138  * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
2139  * active tasks to be scheduled with reason
2140  * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
2141  * will execute normally!). Note that any particular signal will only
2142  * shut down one scheduler; applications should always only create a
2143  * single scheduler.
2144  *
2145  * @param driver to use for the event loop
2146  * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
2147  *         #GNUNET_SCHEDULER_driver_done
2148  */
2149 struct GNUNET_SCHEDULER_Handle *
2150 GNUNET_SCHEDULER_driver_init(const struct GNUNET_SCHEDULER_Driver *driver)
2151 {
2152   struct GNUNET_SCHEDULER_Handle *sh;
2153   const struct GNUNET_DISK_FileHandle *pr;
2154
2155   /* scheduler must not be running */
2156   GNUNET_assert(NULL == scheduler_driver);
2157   GNUNET_assert(NULL == shutdown_pipe_handle);
2158   /* general set-up */
2159   sh = GNUNET_new(struct GNUNET_SCHEDULER_Handle);
2160   shutdown_pipe_handle = GNUNET_DISK_pipe(GNUNET_NO,
2161                                           GNUNET_NO,
2162                                           GNUNET_NO,
2163                                           GNUNET_NO);
2164   GNUNET_assert(NULL != shutdown_pipe_handle);
2165   pr = GNUNET_DISK_pipe_handle(shutdown_pipe_handle,
2166                                GNUNET_DISK_PIPE_END_READ);
2167   my_pid = getpid();
2168   scheduler_driver = driver;
2169
2170   /* install signal handlers */
2171   LOG(GNUNET_ERROR_TYPE_DEBUG,
2172       "Registering signal handlers\n");
2173   sh->shc_int = GNUNET_SIGNAL_handler_install(SIGINT,
2174                                               &sighandler_shutdown);
2175   sh->shc_term = GNUNET_SIGNAL_handler_install(SIGTERM,
2176                                                &sighandler_shutdown);
2177 #if (SIGTERM != GNUNET_TERM_SIG)
2178   sh->shc_gterm = GNUNET_SIGNAL_handler_install(GNUNET_TERM_SIG,
2179                                                 &sighandler_shutdown);
2180 #endif
2181   sh->shc_pipe = GNUNET_SIGNAL_handler_install(SIGPIPE,
2182                                                &sighandler_pipe);
2183   sh->shc_quit = GNUNET_SIGNAL_handler_install(SIGQUIT,
2184                                                &sighandler_shutdown);
2185   sh->shc_hup = GNUNET_SIGNAL_handler_install(SIGHUP,
2186                                               &sighandler_shutdown);
2187
2188   /* Setup initial tasks */
2189   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2190   current_lifeness = GNUNET_NO;
2191   install_parent_control_task =
2192     GNUNET_SCHEDULER_add_now(&install_parent_control_handler,
2193                              NULL);
2194   shutdown_pipe_task =
2195     GNUNET_SCHEDULER_add_read_file(GNUNET_TIME_UNIT_FOREVER_REL,
2196                                    pr,
2197                                    &shutdown_pipe_cb,
2198                                    NULL);
2199   current_lifeness = GNUNET_YES;
2200   scheduler_driver->set_wakeup(scheduler_driver->cls,
2201                                get_timeout());
2202   /* begin main event loop */
2203   sh->rs = GNUNET_NETWORK_fdset_create();
2204   sh->ws = GNUNET_NETWORK_fdset_create();
2205   GNUNET_NETWORK_fdset_handle_set(sh->rs, pr);
2206   return sh;
2207 }
2208
2209
2210 /**
2211  * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
2212  * by external event loop implementations after the scheduler has
2213  * shut down. This is the case if both of the following conditions
2214  * are met:
2215  *
2216  * - all tasks the scheduler has added through the driver's add
2217  *   callback have been removed again through the driver's del
2218  *   callback
2219  * - the timeout the scheduler has set through the driver's
2220  *   add_wakeup callback is FOREVER
2221  *
2222  * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
2223  */
2224 void
2225 GNUNET_SCHEDULER_driver_done(struct GNUNET_SCHEDULER_Handle *sh)
2226 {
2227   GNUNET_assert(NULL == pending_head);
2228   GNUNET_assert(NULL == pending_timeout_head);
2229   GNUNET_assert(NULL == shutdown_head);
2230   for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
2231     {
2232       GNUNET_assert(NULL == ready_head[i]);
2233     }
2234   GNUNET_NETWORK_fdset_destroy(sh->rs);
2235   GNUNET_NETWORK_fdset_destroy(sh->ws);
2236
2237   /* uninstall signal handlers */
2238   GNUNET_SIGNAL_handler_uninstall(sh->shc_int);
2239   GNUNET_SIGNAL_handler_uninstall(sh->shc_term);
2240 #if (SIGTERM != GNUNET_TERM_SIG)
2241   GNUNET_SIGNAL_handler_uninstall(sh->shc_gterm);
2242 #endif
2243   GNUNET_SIGNAL_handler_uninstall(sh->shc_pipe);
2244   GNUNET_SIGNAL_handler_uninstall(sh->shc_quit);
2245   GNUNET_SIGNAL_handler_uninstall(sh->shc_hup);
2246   GNUNET_DISK_pipe_close(shutdown_pipe_handle);
2247   shutdown_pipe_handle = NULL;
2248   scheduler_driver = NULL;
2249   GNUNET_free(sh);
2250 }
2251
2252
2253 static int
2254 select_loop(struct GNUNET_SCHEDULER_Handle *sh,
2255             struct DriverContext *context)
2256 {
2257   struct GNUNET_NETWORK_FDSet *rs;
2258   struct GNUNET_NETWORK_FDSet *ws;
2259   int select_result;
2260
2261   GNUNET_assert(NULL != context);
2262   rs = GNUNET_NETWORK_fdset_create();
2263   ws = GNUNET_NETWORK_fdset_create();
2264   while ((NULL != context->scheduled_head) ||
2265          (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != context->timeout.abs_value_us))
2266     {
2267       LOG(GNUNET_ERROR_TYPE_DEBUG,
2268           "select timeout = %s\n",
2269           GNUNET_STRINGS_absolute_time_to_string(context->timeout));
2270
2271       GNUNET_NETWORK_fdset_zero(rs);
2272       GNUNET_NETWORK_fdset_zero(ws);
2273
2274       for (struct Scheduled *pos = context->scheduled_head;
2275            NULL != pos;
2276            pos = pos->next)
2277         {
2278           if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2279             {
2280               GNUNET_NETWORK_fdset_set_native(rs, pos->fdi->sock);
2281             }
2282           if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2283             {
2284               GNUNET_NETWORK_fdset_set_native(ws, pos->fdi->sock);
2285             }
2286         }
2287       struct GNUNET_TIME_Relative time_remaining =
2288         GNUNET_TIME_absolute_get_remaining(context->timeout);
2289       if (NULL == scheduler_select)
2290         {
2291           select_result = GNUNET_NETWORK_socket_select(rs,
2292                                                        ws,
2293                                                        NULL,
2294                                                        time_remaining);
2295         }
2296       else
2297         {
2298           select_result = scheduler_select(scheduler_select_cls,
2299                                            rs,
2300                                            ws,
2301                                            NULL,
2302                                            time_remaining);
2303         }
2304       if (select_result == GNUNET_SYSERR)
2305         {
2306           if (errno == EINTR)
2307             continue;
2308
2309           LOG_STRERROR(GNUNET_ERROR_TYPE_ERROR,
2310                        "select");
2311 #if USE_LSOF
2312           char lsof[512];
2313
2314           snprintf(lsof,
2315                    sizeof(lsof),
2316                    "lsof -p %d",
2317                    getpid());
2318           (void)close(1);
2319           (void)dup2(2, 1);
2320           if (0 != system(lsof))
2321             LOG_STRERROR(GNUNET_ERROR_TYPE_WARNING,
2322                          "system");
2323 #endif
2324 #if DEBUG_FDS
2325           for (struct Scheduled *s = context->scheduled_head;
2326                NULL != s;
2327                s = s->next)
2328             {
2329               int flags = fcntl(s->fdi->sock,
2330                                 F_GETFD);
2331
2332               if ((flags == -1) &&
2333                   (EBADF == errno))
2334                 {
2335                   LOG(GNUNET_ERROR_TYPE_ERROR,
2336                       "Got invalid file descriptor %d!\n",
2337                       s->fdi->sock);
2338 #if EXECINFO
2339                   dump_backtrace(s->task);
2340 #endif
2341                 }
2342             }
2343 #endif
2344           GNUNET_assert(0);
2345           GNUNET_NETWORK_fdset_destroy(rs);
2346           GNUNET_NETWORK_fdset_destroy(ws);
2347           return GNUNET_SYSERR;
2348         }
2349       if (select_result > 0)
2350         {
2351           for (struct Scheduled *pos = context->scheduled_head;
2352                NULL != pos;
2353                pos = pos->next)
2354             {
2355               int is_ready = GNUNET_NO;
2356
2357               if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2358                   GNUNET_YES ==
2359                   GNUNET_NETWORK_fdset_test_native(rs,
2360                                                    pos->fdi->sock))
2361                 {
2362                   pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2363                   is_ready = GNUNET_YES;
2364                 }
2365               if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2366                   GNUNET_YES ==
2367                   GNUNET_NETWORK_fdset_test_native(ws,
2368                                                    pos->fdi->sock))
2369                 {
2370                   pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2371                   is_ready = GNUNET_YES;
2372                 }
2373               if (GNUNET_YES == is_ready)
2374                 {
2375                   GNUNET_SCHEDULER_task_ready(pos->task,
2376                                               pos->fdi);
2377                 }
2378             }
2379         }
2380       if (GNUNET_YES == GNUNET_SCHEDULER_do_work(sh))
2381         {
2382           LOG(GNUNET_ERROR_TYPE_DEBUG,
2383               "scheduler has more tasks ready!\n");
2384         }
2385     }
2386   GNUNET_NETWORK_fdset_destroy(rs);
2387   GNUNET_NETWORK_fdset_destroy(ws);
2388   return GNUNET_OK;
2389 }
2390
2391
2392 static int
2393 select_add(void *cls,
2394            struct GNUNET_SCHEDULER_Task *task,
2395            struct GNUNET_SCHEDULER_FdInfo *fdi)
2396 {
2397   struct DriverContext *context = cls;
2398
2399   GNUNET_assert(NULL != context);
2400   GNUNET_assert(NULL != task);
2401   GNUNET_assert(NULL != fdi);
2402   GNUNET_assert(0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2403                 0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2404
2405   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2406     {
2407       /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2408       return GNUNET_SYSERR;
2409     }
2410
2411   struct Scheduled *scheduled = GNUNET_new(struct Scheduled);
2412   scheduled->task = task;
2413   scheduled->fdi = fdi;
2414   scheduled->et = fdi->et;
2415
2416   GNUNET_CONTAINER_DLL_insert(context->scheduled_head,
2417                               context->scheduled_tail,
2418                               scheduled);
2419   return GNUNET_OK;
2420 }
2421
2422
2423 static int
2424 select_del(void *cls,
2425            struct GNUNET_SCHEDULER_Task *task)
2426 {
2427   struct DriverContext *context;
2428   struct Scheduled *pos;
2429   int ret;
2430
2431   GNUNET_assert(NULL != cls);
2432
2433   context = cls;
2434   ret = GNUNET_SYSERR;
2435   pos = context->scheduled_head;
2436   while (NULL != pos)
2437     {
2438       struct Scheduled *next = pos->next;
2439       if (pos->task == task)
2440         {
2441           GNUNET_CONTAINER_DLL_remove(context->scheduled_head,
2442                                       context->scheduled_tail,
2443                                       pos);
2444           GNUNET_free(pos);
2445           ret = GNUNET_OK;
2446         }
2447       pos = next;
2448     }
2449   return ret;
2450 }
2451
2452
2453 static void
2454 select_set_wakeup(void *cls,
2455                   struct GNUNET_TIME_Absolute dt)
2456 {
2457   struct DriverContext *context = cls;
2458
2459   GNUNET_assert(NULL != context);
2460   context->timeout = dt;
2461 }
2462
2463
2464 /**
2465  * Obtain the driver for using select() as the event loop.
2466  *
2467  * @return NULL on error
2468  */
2469 struct GNUNET_SCHEDULER_Driver *
2470 GNUNET_SCHEDULER_driver_select()
2471 {
2472   struct GNUNET_SCHEDULER_Driver *select_driver;
2473
2474   select_driver = GNUNET_new(struct GNUNET_SCHEDULER_Driver);
2475
2476   select_driver->add = &select_add;
2477   select_driver->del = &select_del;
2478   select_driver->set_wakeup = &select_set_wakeup;
2479
2480   return select_driver;
2481 }
2482
2483
2484 /**
2485  * Change the async scope for the currently executing task and (transitively)
2486  * for all tasks scheduled by the current task after calling this function.
2487  * Nested tasks can begin their own nested async scope.
2488  *
2489  * Once the current task is finished, the async scope ID is reset to
2490  * its previous value.
2491  *
2492  * Must only be called from a running task.
2493  *
2494  * @param aid the asynchronous scope id to enter
2495  */
2496 void
2497 GNUNET_SCHEDULER_begin_async_scope(struct GNUNET_AsyncScopeId *aid)
2498 {
2499   struct GNUNET_AsyncScopeSave dummy_old_scope;
2500
2501   GNUNET_assert(NULL != active_task);
2502   /* Since we're in a task, the context will be automatically
2503      restored by the scheduler. */
2504   GNUNET_async_scope_enter(aid, &dummy_old_scope);
2505 }
2506
2507
2508 /* end of scheduler.c */