-Merge branch 'master' of ssh://gnunet.org/gnunet into gsoc2018/rest_api
[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 /**
19  * @file util/scheduler.c
20  * @brief schedule computations using continuation passing style
21  * @author Christian Grothoff
22  */
23 #include "platform.h"
24 #include "gnunet_util_lib.h"
25 #include "disk.h"
26 // DEBUG
27 #include <inttypes.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_do_work().  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    * context of the SIGINT handler
95    */
96   struct GNUNET_SIGNAL_Context *shc_int;
97
98   /**
99    * context of the SIGTERM handler
100    */
101   struct GNUNET_SIGNAL_Context *shc_term;
102
103 #if (SIGTERM != GNUNET_TERM_SIG)
104   /**
105    * context of the TERM_SIG handler
106    */
107   struct GNUNET_SIGNAL_Context *shc_gterm;
108 #endif
109
110 #ifndef MINGW
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 #endif
126 };
127
128
129 /**
130  * Entry in list of pending tasks.
131  */
132 struct GNUNET_SCHEDULER_Task
133 {
134   /**
135    * This is a linked list.
136    */
137   struct GNUNET_SCHEDULER_Task *next;
138
139   /**
140    * This is a linked list.
141    */
142   struct GNUNET_SCHEDULER_Task *prev;
143
144   /**
145    * Function to run when ready.
146    */
147   GNUNET_SCHEDULER_TaskCallback callback;
148
149   /**
150    * Closure for the @e callback.
151    */
152   void *callback_cls;
153
154   /**
155    * Information about which FDs are ready for this task (and why).
156    */
157   struct GNUNET_SCHEDULER_FdInfo *fds;
158
159   /**
160    * Storage location used for @e fds if we want to avoid
161    * a separate malloc() call in the common case that this
162    * task is only about a single FD.
163    */
164   struct GNUNET_SCHEDULER_FdInfo fdx;
165
166   /**
167    * Size of the @e fds array.
168    */
169   unsigned int fds_len;
170
171   /**
172    * Do we own the network and file handles referenced by the FdInfo
173    * structs in the fds array. This will only be GNUNET_YES if the
174    * task was created by the #GNUNET_SCHEDULER_add_select function.
175    */
176   int own_handles;
177
178   /**
179    * Absolute timeout value for the task, or
180    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
181    */
182   struct GNUNET_TIME_Absolute timeout;
183
184 #if PROFILE_DELAYS
185   /**
186    * When was the task scheduled?
187    */
188   struct GNUNET_TIME_Absolute start_time;
189 #endif
190
191   /**
192    * Why is the task ready?  Set after task is added to ready queue.
193    * Initially set to zero.  All reasons that have already been
194    * satisfied (i.e.  read or write ready) will be set over time.
195    */
196   enum GNUNET_SCHEDULER_Reason reason;
197
198   /**
199    * Task priority.
200    */
201   enum GNUNET_SCHEDULER_Priority priority;
202
203   /**
204    * Set if we only wait for reading from a single FD, otherwise -1.
205    */
206   int read_fd;
207
208   /**
209    * Set if we only wait for writing to a single FD, otherwise -1.
210    */
211   int write_fd;
212
213   /**
214    * Should the existence of this task in the queue be counted as
215    * reason to not shutdown the scheduler?
216    */
217   int lifeness;
218
219   /**
220    * Is this task run on shutdown?
221    */
222   int on_shutdown;
223
224   /**
225    * Is this task in the ready list?
226    */
227   int in_ready_list;
228
229 #if EXECINFO
230   /**
231    * Array of strings which make up a backtrace from the point when this
232    * task was scheduled (essentially, who scheduled the task?)
233    */
234   char **backtrace_strings;
235
236   /**
237    * Size of the backtrace_strings array
238    */
239   int num_backtrace_strings;
240 #endif
241
242 };
243
244
245 /**
246  * A struct representing an event the select driver is waiting for
247  */
248 struct Scheduled
249 {
250   struct Scheduled *prev;
251
252   struct Scheduled *next;
253
254   /**
255    * the task, the event is related to
256    */
257   struct GNUNET_SCHEDULER_Task *task;
258
259   /**
260    * information about the network socket / file descriptor where
261    * the event is expected to occur
262    */
263   struct GNUNET_SCHEDULER_FdInfo *fdi;
264
265   /**
266    * the event types (multiple event types can be ORed) the select
267    * driver is expected to wait for
268    */
269   enum GNUNET_SCHEDULER_EventType et;
270 };
271
272
273 /**
274  * Driver context used by GNUNET_SCHEDULER_run
275  */
276 struct DriverContext
277 {
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 #ifndef MINGW
623 static void
624 sighandler_pipe ()
625 {
626   return;
627 }
628 #endif
629
630
631 ///**
632 // * Wait for a short time.
633 // * Sleeps for @a ms ms (as that should be long enough for virtually all
634 // * modern systems to context switch and allow another process to do
635 // * some 'real' work).
636 // *
637 // * @param ms how many ms to wait
638 // */
639 //static void
640 //short_wait (unsigned int ms)
641 //{
642 //  struct GNUNET_TIME_Relative timeout;
643 //
644 //  timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
645 //  (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
646 //}
647
648
649 /**
650  * Signal handler called for signals that should cause us to shutdown.
651  */
652 static void
653 sighandler_shutdown ()
654 {
655   static char c;
656   int old_errno = errno;        /* backup errno */
657
658   if (getpid () != my_pid)
659     exit (1);                   /* we have fork'ed since the signal handler was created,
660                                  * ignore the signal, see https://gnunet.org/vfork discussion */
661   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
662                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
663                           &c, sizeof (c));
664   errno = old_errno;
665 }
666
667
668 static void
669 shutdown_if_no_lifeness ()
670 {
671   struct GNUNET_SCHEDULER_Task *t;
672
673   if (ready_count > 0)
674     return;
675   for (t = pending_head; NULL != t; t = t->next)
676     if (GNUNET_YES == t->lifeness)
677       return;
678   for (t = shutdown_head; NULL != t; t = t->next)
679     if (GNUNET_YES == t->lifeness)
680       return;
681   for (t = pending_timeout_head; NULL != t; t = t->next)
682     if (GNUNET_YES == t->lifeness)
683       return;
684   /* No lifeness! */
685   GNUNET_SCHEDULER_shutdown ();
686 }
687
688
689 static int
690 select_loop (struct GNUNET_SCHEDULER_Handle *sh,
691              struct DriverContext *context);
692
693
694 /**
695  * Initialize and run scheduler.  This function will return when all
696  * tasks have completed.  On systems with signals, receiving a SIGTERM
697  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
698  * to be run after the active task is complete.  As a result, SIGTERM
699  * causes all active tasks to be scheduled with reason
700  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
701  * afterwards will execute normally!). Note that any particular signal
702  * will only shut down one scheduler; applications should always only
703  * create a single scheduler.
704  *
705  * @param task task to run immediately
706  * @param task_cls closure of @a task
707  */
708 void
709 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
710                       void *task_cls)
711 {
712   struct GNUNET_SCHEDULER_Handle *sh;
713   struct GNUNET_SCHEDULER_Driver *driver;
714   struct DriverContext context = {.scheduled_head = NULL,
715                                   .scheduled_tail = NULL,
716                                   .timeout = GNUNET_TIME_absolute_get ()};
717
718   driver = GNUNET_SCHEDULER_driver_select ();
719   driver->cls = &context;
720   sh = GNUNET_SCHEDULER_driver_init (driver);
721   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
722                                                  task_cls,
723                                                  GNUNET_SCHEDULER_REASON_STARTUP,
724                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
725   select_loop (sh,
726                &context);
727   GNUNET_SCHEDULER_driver_done (sh);
728   GNUNET_free (driver);
729 }
730
731
732 /**
733  * Obtain the task context, giving the reason why the current task was
734  * started.
735  *
736  * @return current tasks' scheduler context
737  */
738 const struct GNUNET_SCHEDULER_TaskContext *
739 GNUNET_SCHEDULER_get_task_context ()
740 {
741   GNUNET_assert (NULL != active_task);
742   return &tc;
743 }
744
745
746 /**
747  * Get information about the current load of this scheduler.  Use this
748  * function to determine if an elective task should be added or simply
749  * dropped (if the decision should be made based on the number of
750  * tasks ready to run).
751  *
752  * @param p priority level to look at
753  * @return number of tasks pending right now
754  */
755 unsigned int
756 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
757 {
758   struct GNUNET_SCHEDULER_Task *pos;
759   unsigned int ret;
760
761   GNUNET_assert (NULL != active_task);
762   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
763     return ready_count;
764   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
765     p = current_priority;
766   ret = 0;
767   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
768     ret++;
769   return ret;
770 }
771
772
773 void
774 init_fd_info (struct GNUNET_SCHEDULER_Task *t,
775               const struct GNUNET_NETWORK_Handle *const *read_nh,
776               unsigned int read_nh_len,
777               const struct GNUNET_NETWORK_Handle *const *write_nh,
778               unsigned int write_nh_len,
779               const struct GNUNET_DISK_FileHandle *const *read_fh,
780               unsigned int read_fh_len,
781               const struct GNUNET_DISK_FileHandle *const *write_fh,
782               unsigned int write_fh_len)
783 {
784   // FIXME: if we have exactly two network handles / exactly two file handles
785   // and they are equal, we can make one FdInfo with both
786   // GNUNET_SCHEDULER_ET_IN and GNUNET_SCHEDULER_ET_OUT set.
787   struct GNUNET_SCHEDULER_FdInfo *fdi;
788
789   t->fds_len = read_nh_len + write_nh_len + read_fh_len + write_fh_len;
790   if (1 == t->fds_len)
791   {
792     fdi = &t->fdx;
793     t->fds = fdi;
794     if (1 == read_nh_len)
795     {
796       GNUNET_assert (NULL != read_nh);
797       GNUNET_assert (NULL != *read_nh);
798       fdi->fd = *read_nh;
799       fdi->et = GNUNET_SCHEDULER_ET_IN;
800       fdi->sock = GNUNET_NETWORK_get_fd (*read_nh);
801       t->read_fd = fdi->sock;
802       t->write_fd = -1;
803     }
804     else if (1 == write_nh_len)
805     {
806       GNUNET_assert (NULL != write_nh);
807       GNUNET_assert (NULL != *write_nh);
808       fdi->fd = *write_nh;
809       fdi->et = GNUNET_SCHEDULER_ET_OUT;
810       fdi->sock = GNUNET_NETWORK_get_fd (*write_nh);
811       t->read_fd = -1;
812       t->write_fd = fdi->sock;
813     }
814     else if (1 == read_fh_len)
815     {
816       GNUNET_assert (NULL != read_fh);
817       GNUNET_assert (NULL != *read_fh);
818       fdi->fh = *read_fh;
819       fdi->et = GNUNET_SCHEDULER_ET_IN;
820       fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
821       t->read_fd = fdi->sock;
822       t->write_fd = -1;
823     }
824     else
825     {
826       GNUNET_assert (NULL != write_fh);
827       GNUNET_assert (NULL != *write_fh);
828       fdi->fh = *write_fh;
829       fdi->et = GNUNET_SCHEDULER_ET_OUT;
830       fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
831       t->read_fd = -1;
832       t->write_fd = fdi->sock;
833     }
834   }
835   else
836   {
837     fdi = GNUNET_new_array (t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
838     t->fds = fdi;
839     t->read_fd = -1;
840     t->write_fd = -1;
841     unsigned int i;
842     for (i = 0; i != read_nh_len; ++i)
843     {
844       fdi->fd = read_nh[i];
845       GNUNET_assert (NULL != fdi->fd);
846       fdi->et = GNUNET_SCHEDULER_ET_IN;
847       fdi->sock = GNUNET_NETWORK_get_fd (read_nh[i]);
848       ++fdi;
849     }
850     for (i = 0; i != write_nh_len; ++i)
851     {
852       fdi->fd = write_nh[i];
853       GNUNET_assert (NULL != fdi->fd);
854       fdi->et = GNUNET_SCHEDULER_ET_OUT;
855       fdi->sock = GNUNET_NETWORK_get_fd (write_nh[i]);
856       ++fdi;
857     }
858     for (i = 0; i != read_fh_len; ++i)
859     {
860       fdi->fh = read_fh[i];
861       GNUNET_assert (NULL != fdi->fh);
862       fdi->et = GNUNET_SCHEDULER_ET_IN;
863       fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
864       ++fdi;
865     }
866     for (i = 0; i != write_fh_len; ++i)
867     {
868       fdi->fh = write_fh[i];
869       GNUNET_assert (NULL != fdi->fh);
870       fdi->et = GNUNET_SCHEDULER_ET_OUT;
871       fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
872       ++fdi;
873     }
874   }
875 }
876
877
878 /**
879  * calls the given function @a func on each FdInfo related to @a t.
880  * Optionally updates the event type field in each FdInfo after calling
881  * @a func.
882  *
883  * @param t the task
884  * @param driver_func the function to call with each FdInfo contained in
885  *                    in @a t
886  * @param if_not_ready only call @a driver_func on FdInfos that are not
887  *                     ready
888  * @param et the event type to be set in each FdInfo after calling
889  *           @a driver_func on it, or -1 if no updating not desired.
890  */
891 static void
892 driver_add_multiple (struct GNUNET_SCHEDULER_Task *t)
893 {
894   struct GNUNET_SCHEDULER_FdInfo *fdi;
895   int success = GNUNET_YES;
896
897   for (unsigned int i = 0; i != t->fds_len; ++i)
898   {
899     fdi = &t->fds[i];
900     success = scheduler_driver->add (scheduler_driver->cls,
901                                      t,
902                                      fdi) && success;
903     fdi->et = GNUNET_SCHEDULER_ET_NONE;
904   }
905   if (GNUNET_YES != success)
906   {
907     LOG (GNUNET_ERROR_TYPE_ERROR,
908          "driver could not add task\n");
909   }
910 }
911
912
913 static void
914 install_parent_control_handler (void *cls)
915 {
916   install_parent_control_task = NULL;
917   GNUNET_OS_install_parent_control_handler (NULL);
918 }
919
920
921 static void
922 shutdown_pipe_cb (void *cls)
923 {
924   char c;
925   const struct GNUNET_DISK_FileHandle *pr;
926
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   GNUNET_assert ( (NULL != active_task) ||
969       (GNUNET_NO == task->lifeness) );
970   is_fd_task = (NULL != task->fds);
971   if (is_fd_task)
972   {
973     int del_result = scheduler_driver->del (scheduler_driver->cls, task);
974     if (GNUNET_OK != del_result)
975     {
976       LOG (GNUNET_ERROR_TYPE_ERROR,
977            "driver could not delete task\n");
978       GNUNET_assert (0);
979     }
980   }
981   if (! task->in_ready_list)
982   {
983     if (is_fd_task)
984     {
985       GNUNET_CONTAINER_DLL_remove (pending_head,
986                                    pending_tail,
987                                    task);
988     }
989     else if (GNUNET_YES == task->on_shutdown)
990     {
991       GNUNET_CONTAINER_DLL_remove (shutdown_head,
992                                    shutdown_tail,
993                                    task);
994     }
995     else
996     {
997       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
998                                    pending_timeout_tail,
999                                    task);
1000       if (pending_timeout_last == task)
1001         pending_timeout_last = NULL;
1002     }
1003   }
1004   else
1005   {
1006     p = check_priority (task->priority);
1007     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1008                                  ready_tail[p],
1009                                  task);
1010     ready_count--;
1011   }
1012   ret = task->callback_cls;
1013   destroy_task (task);
1014   return ret;
1015 }
1016
1017
1018 /**
1019  * Initialize backtrace data for task @a t
1020  *
1021  * @param t task to initialize
1022  */
1023 static void
1024 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
1025 {
1026 #if EXECINFO
1027   void *backtrace_array[MAX_TRACE_DEPTH];
1028
1029   t->num_backtrace_strings
1030     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1031   t->backtrace_strings =
1032       backtrace_symbols (backtrace_array,
1033        t->num_backtrace_strings);
1034   dump_backtrace (t);
1035 #else
1036   (void) t;
1037 #endif
1038 }
1039
1040
1041 /**
1042  * Continue the current execution with the given function.  This is
1043  * similar to the other "add" functions except that there is no delay
1044  * and the reason code can be specified.
1045  *
1046  * @param task main function of the task
1047  * @param task_cls closure for @a task
1048  * @param reason reason for task invocation
1049  * @param priority priority to use for the task
1050  */
1051 void
1052 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1053                                                void *task_cls,
1054                                                enum GNUNET_SCHEDULER_Reason reason,
1055                                                enum GNUNET_SCHEDULER_Priority priority)
1056 {
1057   struct GNUNET_SCHEDULER_Task *t;
1058
1059   GNUNET_assert (NULL != task);
1060   GNUNET_assert ((NULL != active_task) ||
1061                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1062   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1063   t->read_fd = -1;
1064   t->write_fd = -1;
1065   t->callback = task;
1066   t->callback_cls = task_cls;
1067 #if PROFILE_DELAYS
1068   t->start_time = GNUNET_TIME_absolute_get ();
1069 #endif
1070   t->reason = reason;
1071   t->priority = check_priority (priority);
1072   t->lifeness = current_lifeness;
1073   LOG (GNUNET_ERROR_TYPE_DEBUG,
1074        "Adding continuation task %p\n",
1075        t);
1076   init_backtrace (t);
1077   queue_ready_task (t);
1078 }
1079
1080
1081 /**
1082  * Schedule a new task to be run at the specified time.  The task
1083  * will be scheduled for execution at time @a at.
1084  *
1085  * @param at time when the operation should run
1086  * @param priority priority to use for the task
1087  * @param task main function of the task
1088  * @param task_cls closure of @a task
1089  * @return unique task identifier for the job
1090  *         only valid until @a task is started!
1091  */
1092 struct GNUNET_SCHEDULER_Task *
1093 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1094                                        enum GNUNET_SCHEDULER_Priority priority,
1095                                        GNUNET_SCHEDULER_TaskCallback task,
1096                                        void *task_cls)
1097 {
1098   struct GNUNET_SCHEDULER_Task *t;
1099   struct GNUNET_SCHEDULER_Task *pos;
1100   struct GNUNET_SCHEDULER_Task *prev;
1101
1102   GNUNET_assert (NULL != active_task);
1103   GNUNET_assert (NULL != task);
1104   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
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   GNUNET_assert (NULL != active_task);
1290   GNUNET_assert (NULL != task);
1291   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1292   t->callback = task;
1293   t->callback_cls = task_cls;
1294   t->read_fd = -1;
1295   t->write_fd = -1;
1296 #if PROFILE_DELAYS
1297   t->start_time = GNUNET_TIME_absolute_get ();
1298 #endif
1299   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1300   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1301   t->on_shutdown = GNUNET_YES;
1302   t->lifeness = GNUNET_NO;
1303   GNUNET_CONTAINER_DLL_insert (shutdown_head,
1304                                shutdown_tail,
1305                                t);
1306   LOG (GNUNET_ERROR_TYPE_DEBUG,
1307        "Adding shutdown task %p\n",
1308        t);
1309   init_backtrace (t);
1310   return t;
1311 }
1312
1313
1314 /**
1315  * Schedule a new task to be run as soon as possible with the
1316  * (transitive) ignore-shutdown flag either explicitly set or
1317  * explicitly enabled.  This task (and all tasks created from it,
1318  * other than by another call to this function) will either count or
1319  * not count for the "lifeness" of the process.  This API is only
1320  * useful in a few special cases.
1321  *
1322  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1323  * @param task main function of the task
1324  * @param task_cls closure of @a task
1325  * @return unique task identifier for the job
1326  *         only valid until @a task is started!
1327  */
1328 struct GNUNET_SCHEDULER_Task *
1329 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1330                                         GNUNET_SCHEDULER_TaskCallback task,
1331                                         void *task_cls)
1332 {
1333   struct GNUNET_SCHEDULER_Task *ret;
1334
1335   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1336   ret->lifeness = lifeness;
1337   return ret;
1338 }
1339
1340
1341 #if DEBUG_FDS
1342 /**
1343  * check a raw file descriptor and abort if it is bad (for debugging purposes)
1344  *
1345  * @param t the task related to the file descriptor
1346  * @param raw_fd the raw file descriptor to check
1347  */
1348 void
1349 check_fd (struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1350 {
1351   if (-1 != raw_fd)
1352   {
1353     int flags = fcntl (raw_fd, F_GETFD);
1354
1355     if ((flags == -1) && (errno == EBADF))
1356     {
1357       LOG (GNUNET_ERROR_TYPE_ERROR,
1358            "Got invalid file descriptor %d!\n",
1359            raw_fd);
1360       init_backtrace (t);
1361       GNUNET_assert (0);
1362     }
1363   }
1364 }
1365 #endif
1366
1367
1368 /**
1369  * Schedule a new task to be run with a specified delay or when any of
1370  * the specified file descriptor sets is ready.  The delay can be used
1371  * as a timeout on the socket(s) being ready.  The task will be
1372  * scheduled for execution once either the delay has expired or any of
1373  * the socket operations is ready.  This is the most general
1374  * function of the "add" family.  Note that the "prerequisite_task"
1375  * must be satisfied in addition to any of the other conditions.  In
1376  * other words, the task will be started when
1377  * <code>
1378  * (prerequisite-run)
1379  * && (delay-ready
1380  *     || any-rs-ready
1381  *     || any-ws-ready)
1382  * </code>
1383  *
1384  * @param delay how long should we wait?
1385  * @param priority priority to use
1386  * @param rfd file descriptor we want to read (can be -1)
1387  * @param wfd file descriptors we want to write (can be -1)
1388  * @param task main function of the task
1389  * @param task_cls closure of @a task
1390  * @return unique task identifier for the job
1391  *         only valid until @a task is started!
1392  */
1393 #ifndef MINGW
1394 static struct GNUNET_SCHEDULER_Task *
1395 add_without_sets (struct GNUNET_TIME_Relative delay,
1396                   enum GNUNET_SCHEDULER_Priority priority,
1397                   const struct GNUNET_NETWORK_Handle *read_nh,
1398                   const struct GNUNET_NETWORK_Handle *write_nh,
1399                   const struct GNUNET_DISK_FileHandle *read_fh,
1400                   const struct GNUNET_DISK_FileHandle *write_fh,
1401                   GNUNET_SCHEDULER_TaskCallback task,
1402                   void *task_cls)
1403 {
1404   struct GNUNET_SCHEDULER_Task *t;
1405
1406   GNUNET_assert (NULL != active_task);
1407   GNUNET_assert (NULL != task);
1408   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1409   init_fd_info (t,
1410                 &read_nh,
1411                 read_nh ? 1 : 0,
1412                 &write_nh,
1413                 write_nh ? 1 : 0,
1414                 &read_fh,
1415                 read_fh ? 1 : 0,
1416                 &write_fh,
1417                 write_fh ? 1 : 0);
1418   t->callback = task;
1419   t->callback_cls = task_cls;
1420 #if DEBUG_FDS
1421   check_fd (t, NULL != read_nh ? GNUNET_NETWORK_get_fd (read_nh) : -1);
1422   check_fd (t, NULL != write_nh ? GNUNET_NETWORK_get_fd (write_nh) : -1);
1423   check_fd (t, NULL != read_fh ? read_fh->fd : -1);
1424   check_fd (t, NULL != write_fh ? write_fh->fd : -1);
1425 #endif
1426 #if PROFILE_DELAYS
1427   t->start_time = GNUNET_TIME_absolute_get ();
1428 #endif
1429   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1430   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1431   t->lifeness = current_lifeness;
1432   GNUNET_CONTAINER_DLL_insert (pending_head,
1433                                pending_tail,
1434                                t);
1435   driver_add_multiple (t);
1436   max_priority_added = GNUNET_MAX (max_priority_added,
1437                                    t->priority);
1438   init_backtrace (t);
1439   return t;
1440 }
1441 #endif
1442
1443
1444 /**
1445  * Schedule a new task to be run with a specified delay or when the
1446  * specified file descriptor is ready for reading.  The delay can be
1447  * used as a timeout on the socket being ready.  The task will be
1448  * scheduled for execution once either the delay has expired or the
1449  * socket operation is ready.  It will be run with the DEFAULT priority.
1450  * Only allowed to be called as long as the scheduler is running, that
1451  * is one of the following conditions is met:
1452  *
1453  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1454  * - #GNUNET_SCHEDULER_driver_init has been run and
1455  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1456  *
1457  * @param delay when should this operation time out?
1458  * @param rfd read file-descriptor
1459  * @param task main function of the task
1460  * @param task_cls closure of @a task
1461  * @return unique task identifier for the job
1462  *         only valid until @a task is started!
1463  */
1464 struct GNUNET_SCHEDULER_Task *
1465 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1466                                struct GNUNET_NETWORK_Handle *rfd,
1467                                GNUNET_SCHEDULER_TaskCallback task,
1468                                void *task_cls)
1469 {
1470   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1471                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1472                   rfd, task, task_cls);
1473 }
1474
1475
1476 /**
1477  * Schedule a new task to be run with a specified priority and to be
1478  * run after the specified delay or when the specified file descriptor
1479  * is ready for reading.  The delay can be used as a timeout on the
1480  * socket being ready.  The task will be scheduled for execution once
1481  * either the delay has expired or the socket operation is ready.  It
1482  * will be run with the DEFAULT priority.
1483  * Only allowed to be called as long as the scheduler is running, that
1484  * is one of the following conditions is met:
1485  *
1486  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1487  * - #GNUNET_SCHEDULER_driver_init has been run and
1488  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1489  *
1490  * @param delay when should this operation time out?
1491  * @param priority priority to use for the task
1492  * @param rfd read file-descriptor
1493  * @param task main function of the task
1494  * @param task_cls closure of @a task
1495  * @return unique task identifier for the job
1496  *         only valid until @a task is started!
1497  */
1498 struct GNUNET_SCHEDULER_Task *
1499 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1500                enum GNUNET_SCHEDULER_Priority priority,
1501                struct GNUNET_NETWORK_Handle *rfd,
1502                GNUNET_SCHEDULER_TaskCallback task,
1503                                              void *task_cls)
1504 {
1505   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1506                                                  rfd,
1507                                                  GNUNET_YES,
1508                                                  GNUNET_NO,
1509                                                  task, task_cls);
1510 }
1511
1512
1513 /**
1514  * Schedule a new task to be run with a specified delay or when the
1515  * specified file descriptor is ready for writing.  The delay can be
1516  * used as a timeout on the socket being ready.  The task will be
1517  * scheduled for execution once either the delay has expired or the
1518  * socket operation is ready.  It will be run with the priority of
1519  * the calling task.
1520  * Only allowed to be called as long as the scheduler is running, that
1521  * is one of the following conditions is met:
1522  *
1523  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1524  * - #GNUNET_SCHEDULER_driver_init has been run and
1525  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1526  *
1527  * @param delay when should this operation time out?
1528  * @param wfd write file-descriptor
1529  * @param task main function of the task
1530  * @param task_cls closure of @a task
1531  * @return unique task identifier for the job
1532  *         only valid until @a task is started!
1533  */
1534 struct GNUNET_SCHEDULER_Task *
1535 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1536                                 struct GNUNET_NETWORK_Handle *wfd,
1537                                 GNUNET_SCHEDULER_TaskCallback task,
1538                                 void *task_cls)
1539 {
1540   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1541                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1542                                                  wfd,
1543                                                  GNUNET_NO, GNUNET_YES,
1544                                                  task, task_cls);
1545 }
1546
1547 /**
1548  * Schedule a new task to be run with a specified delay or when the
1549  * specified file descriptor is ready.  The delay can be
1550  * used as a timeout on the socket being ready.  The task will be
1551  * scheduled for execution once either the delay has expired or the
1552  * socket operation is ready.
1553  * Only allowed to be called as long as the scheduler is running, that
1554  * is one of the following conditions is met:
1555  *
1556  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1557  * - #GNUNET_SCHEDULER_driver_init has been run and
1558  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1559  *
1560  * @param delay when should this operation time out?
1561  * @param priority priority of the task
1562  * @param fd file-descriptor
1563  * @param on_read whether to poll the file-descriptor for readability
1564  * @param on_write whether to poll the file-descriptor for writability
1565  * @param task main function of the task
1566  * @param task_cls closure of task
1567  * @return unique task identifier for the job
1568  *         only valid until "task" is started!
1569  */
1570 struct GNUNET_SCHEDULER_Task *
1571 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1572                                          enum GNUNET_SCHEDULER_Priority priority,
1573                                          struct GNUNET_NETWORK_Handle *fd,
1574                                          int on_read,
1575                                          int on_write,
1576                                          GNUNET_SCHEDULER_TaskCallback task,
1577                                          void *task_cls)
1578 {
1579   /* scheduler must be running */
1580   GNUNET_assert (NULL != scheduler_driver);
1581
1582 #if MINGW
1583   struct GNUNET_NETWORK_FDSet *s;
1584   struct GNUNET_SCHEDULER_Task * ret;
1585
1586   GNUNET_assert (NULL != fd);
1587   s = GNUNET_NETWORK_fdset_create ();
1588   GNUNET_NETWORK_fdset_set (s, fd);
1589   ret = GNUNET_SCHEDULER_add_select (
1590       priority, delay,
1591       on_read  ? s : NULL,
1592       on_write ? s : NULL,
1593       task, task_cls);
1594   GNUNET_NETWORK_fdset_destroy (s);
1595   return ret;
1596 #else
1597   GNUNET_assert (on_read || on_write);
1598   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1599   return add_without_sets (delay, priority,
1600                            on_read  ? fd : NULL,
1601                            on_write ? fd : NULL,
1602                            NULL,
1603                            NULL,
1604                            task, task_cls);
1605 #endif
1606 }
1607
1608
1609 /**
1610  * Schedule a new task to be run with a specified delay or when the
1611  * specified file descriptor is ready for reading.  The delay can be
1612  * used as a timeout on the socket being ready.  The task will be
1613  * scheduled for execution once either the delay has expired or the
1614  * socket operation is ready. It will be run with the DEFAULT priority.
1615  * Only allowed to be called as long as the scheduler is running, that
1616  * is one of the following conditions is met:
1617  *
1618  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1619  * - #GNUNET_SCHEDULER_driver_init has been run and
1620  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1621  *
1622  * @param delay when should this operation time out?
1623  * @param rfd read file-descriptor
1624  * @param task main function of the task
1625  * @param task_cls closure of @a task
1626  * @return unique task identifier for the job
1627  *         only valid until @a task is started!
1628  */
1629 struct GNUNET_SCHEDULER_Task *
1630 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1631                                 const struct GNUNET_DISK_FileHandle *rfd,
1632                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1633 {
1634   return GNUNET_SCHEDULER_add_file_with_priority (
1635       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1636       rfd, GNUNET_YES, GNUNET_NO,
1637       task, task_cls);
1638 }
1639
1640
1641 /**
1642  * Schedule a new task to be run with a specified delay or when the
1643  * specified file descriptor is ready for writing.  The delay can be
1644  * used as a timeout on the socket being ready.  The task will be
1645  * scheduled for execution once either the delay has expired or the
1646  * socket operation is ready. It will be run with the DEFAULT priority.
1647  * Only allowed to be called as long as the scheduler is running, that
1648  * is one of the following conditions is met:
1649  *
1650  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1651  * - #GNUNET_SCHEDULER_driver_init has been run and
1652  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1653  *
1654  * @param delay when should this operation time out?
1655  * @param wfd write file-descriptor
1656  * @param task main function of the task
1657  * @param task_cls closure of @a task
1658  * @return unique task identifier for the job
1659  *         only valid until @a task is started!
1660  */
1661 struct GNUNET_SCHEDULER_Task *
1662 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1663                                  const struct GNUNET_DISK_FileHandle *wfd,
1664                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1665 {
1666   return GNUNET_SCHEDULER_add_file_with_priority (
1667       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1668       wfd, GNUNET_NO, GNUNET_YES,
1669       task, task_cls);
1670 }
1671
1672
1673 /**
1674  * Schedule a new task to be run with a specified delay or when the
1675  * specified file descriptor is ready.  The delay can be
1676  * used as a timeout on the socket being ready.  The task will be
1677  * scheduled for execution once either the delay has expired or the
1678  * socket operation is ready.
1679  * Only allowed to be called as long as the scheduler is running, that
1680  * is one of the following conditions is met:
1681  *
1682  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1683  * - #GNUNET_SCHEDULER_driver_init has been run and
1684  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1685  *
1686  * @param delay when should this operation time out?
1687  * @param priority priority of the task
1688  * @param fd file-descriptor
1689  * @param on_read whether to poll the file-descriptor for readability
1690  * @param on_write whether to poll the file-descriptor for writability
1691  * @param task main function of the task
1692  * @param task_cls closure of @a task
1693  * @return unique task identifier for the job
1694  *         only valid until @a task is started!
1695  */
1696 struct GNUNET_SCHEDULER_Task *
1697 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1698                                          enum GNUNET_SCHEDULER_Priority priority,
1699                                          const struct GNUNET_DISK_FileHandle *fd,
1700                                          int on_read, int on_write,
1701                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1702 {
1703   /* scheduler must be running */
1704   GNUNET_assert (NULL != scheduler_driver);
1705
1706 #if MINGW
1707   struct GNUNET_NETWORK_FDSet *s;
1708   struct GNUNET_SCHEDULER_Task * ret;
1709
1710   GNUNET_assert (NULL != fd);
1711   s = GNUNET_NETWORK_fdset_create ();
1712   GNUNET_NETWORK_fdset_handle_set (s, fd);
1713   ret = GNUNET_SCHEDULER_add_select (
1714       priority, delay,
1715       on_read  ? s : NULL,
1716       on_write ? s : NULL,
1717       task, task_cls);
1718   GNUNET_NETWORK_fdset_destroy (s);
1719   return ret;
1720 #else
1721   GNUNET_assert (on_read || on_write);
1722   GNUNET_assert (fd->fd >= 0);
1723   return add_without_sets (delay, priority,
1724                            NULL,
1725                            NULL,
1726                            on_read ? fd : NULL,
1727                            on_write ? fd : NULL,
1728                            task, task_cls);
1729 #endif
1730 }
1731
1732
1733 void
1734 extract_handles (const struct GNUNET_NETWORK_FDSet *fdset,
1735                  const struct GNUNET_NETWORK_Handle ***ntarget,
1736                  unsigned int *extracted_nhandles,
1737                  const struct GNUNET_DISK_FileHandle ***ftarget,
1738                  unsigned int *extracted_fhandles)
1739 {
1740   // FIXME: this implementation only works for unix, for WIN32 the file handles
1741   // in fdset must be handled separately
1742   const struct GNUNET_NETWORK_Handle **nhandles;
1743   const struct GNUNET_DISK_FileHandle **fhandles;
1744   unsigned int nhandles_len;
1745   unsigned int fhandles_len;
1746
1747   nhandles = NULL;
1748   fhandles = NULL;
1749   nhandles_len = 0;
1750   fhandles_len = 0;
1751   for (int sock = 0; sock != fdset->nsds; ++sock)
1752   {
1753     if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
1754     {
1755       struct GNUNET_NETWORK_Handle *nhandle;
1756       struct GNUNET_DISK_FileHandle *fhandle;
1757
1758       nhandle = GNUNET_NETWORK_socket_box_native (sock);
1759       if (NULL != nhandle)
1760       {
1761         GNUNET_array_append (nhandles, nhandles_len, nhandle);
1762       }
1763       else
1764       {
1765         fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
1766         if (NULL != fhandle)
1767         {
1768           GNUNET_array_append (fhandles, fhandles_len, fhandle);
1769         }
1770         else
1771         {
1772           GNUNET_assert (0);
1773         }
1774       }
1775     }
1776   }
1777   *ntarget = nhandles_len > 0 ? nhandles : NULL;
1778   *ftarget = fhandles_len > 0 ? fhandles : NULL;
1779   *extracted_nhandles = nhandles_len;
1780   *extracted_fhandles = fhandles_len;
1781 }
1782
1783
1784 /**
1785  * Schedule a new task to be run with a specified delay or when any of
1786  * the specified file descriptor sets is ready.  The delay can be used
1787  * as a timeout on the socket(s) being ready.  The task will be
1788  * scheduled for execution once either the delay has expired or any of
1789  * the socket operations is ready.  This is the most general
1790  * function of the "add" family.  Note that the "prerequisite_task"
1791  * must be satisfied in addition to any of the other conditions.  In
1792  * other words, the task will be started when
1793  * <code>
1794  * (prerequisite-run)
1795  * && (delay-ready
1796  *     || any-rs-ready
1797  *     || any-ws-ready) )
1798  * </code>
1799  * Only allowed to be called as long as the scheduler is running, that
1800  * is one of the following conditions is met:
1801  *
1802  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1803  * - #GNUNET_SCHEDULER_driver_init has been run and
1804  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1805  *
1806  * @param prio how important is this task?
1807  * @param delay how long should we wait?
1808  * @param rs set of file descriptors we want to read (can be NULL)
1809  * @param ws set of file descriptors we want to write (can be NULL)
1810  * @param task main function of the task
1811  * @param task_cls closure of @a task
1812  * @return unique task identifier for the job
1813  *         only valid until @a task is started!
1814  */
1815 struct GNUNET_SCHEDULER_Task *
1816 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1817                              struct GNUNET_TIME_Relative delay,
1818                              const struct GNUNET_NETWORK_FDSet *rs,
1819                              const struct GNUNET_NETWORK_FDSet *ws,
1820                              GNUNET_SCHEDULER_TaskCallback task,
1821                              void *task_cls)
1822 {
1823   struct GNUNET_SCHEDULER_Task *t;
1824   const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
1825   const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
1826   const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
1827   const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
1828   unsigned int read_nhandles_len = 0;
1829   unsigned int write_nhandles_len = 0;
1830   unsigned int read_fhandles_len = 0;
1831   unsigned int write_fhandles_len = 0;
1832
1833   /* scheduler must be running */
1834   GNUNET_assert (NULL != scheduler_driver);
1835   GNUNET_assert (NULL != active_task);
1836   GNUNET_assert (NULL != task);
1837   int no_rs = (NULL == rs);
1838   int no_ws = (NULL == ws);
1839   int empty_rs = (NULL != rs) && (0 == rs->nsds);
1840   int empty_ws = (NULL != ws) && (0 == ws->nsds);
1841   int no_fds = (no_rs && no_ws) ||
1842                (empty_rs && empty_ws) ||
1843                (no_rs && empty_ws) ||
1844                (no_ws && empty_rs);
1845   if (! no_fds)
1846   {
1847     if (NULL != rs)
1848     {
1849       extract_handles (rs,
1850                        &read_nhandles,
1851                        &read_nhandles_len,
1852                        &read_fhandles,
1853                        &read_fhandles_len);
1854     }
1855     if (NULL != ws)
1856     {
1857       extract_handles (ws,
1858                        &write_nhandles,
1859                        &write_nhandles_len,
1860                        &write_fhandles,
1861                        &write_fhandles_len);
1862     }
1863   }
1864   /**
1865    * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
1866    * although its maximum FD number (nsds) is greater than 0. We handle
1867    * this case gracefully because some libraries such as libmicrohttpd
1868    * only provide a hint what the maximum FD number in an FD set might be
1869    * and not the exact FD number (see e.g. gnunet-rest-service.c)
1870    */
1871   int no_fds_extracted = (0 == read_nhandles_len) &&
1872                          (0 == read_fhandles_len) &&
1873                          (0 == write_nhandles_len) &&
1874                          (0 == write_fhandles_len);
1875   if (no_fds || no_fds_extracted)
1876     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1877                                                        prio,
1878                                                        task,
1879                                                        task_cls);
1880   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1881   init_fd_info (t,
1882                 read_nhandles,
1883                 read_nhandles_len,
1884                 write_nhandles,
1885                 write_nhandles_len,
1886                 read_fhandles,
1887                 read_fhandles_len,
1888                 write_fhandles,
1889                 write_fhandles_len);
1890   t->callback = task;
1891   t->callback_cls = task_cls;
1892   t->own_handles = GNUNET_YES;
1893   /* free the arrays of pointers to network / file handles, the actual
1894    * handles will be freed in destroy_task */
1895   GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
1896   GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
1897   GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
1898   GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
1899 #if PROFILE_DELAYS
1900   t->start_time = GNUNET_TIME_absolute_get ();
1901 #endif
1902   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1903   t->priority =
1904       check_priority ((prio ==
1905                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1906                       prio);
1907   t->lifeness = current_lifeness;
1908   GNUNET_CONTAINER_DLL_insert (pending_head,
1909                                pending_tail,
1910                                t);
1911   driver_add_multiple (t);
1912   max_priority_added = GNUNET_MAX (max_priority_added,
1913            t->priority);
1914   LOG (GNUNET_ERROR_TYPE_DEBUG,
1915        "Adding task %p\n",
1916        t);
1917   init_backtrace (t);
1918   return t;
1919 }
1920
1921
1922 /**
1923  * Function used by event-loop implementations to signal the scheduler
1924  * that a particular @a task is ready due to an event specified in the
1925  * et field of @a fdi.
1926  *
1927  * This function will then queue the task to notify the application
1928  * that the task is ready (with the respective priority).
1929  *
1930  * @param task the task that is ready
1931  * @param fdi information about the related FD
1932  */
1933 void
1934 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1935                              struct GNUNET_SCHEDULER_FdInfo *fdi)
1936 {
1937   enum GNUNET_SCHEDULER_Reason reason;
1938
1939   reason = task->reason;
1940   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1941        (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)) )
1942     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1943   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1944        (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)) )
1945     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1946   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1947   task->reason = reason;
1948   if (GNUNET_NO == task->in_ready_list)
1949   {
1950     GNUNET_CONTAINER_DLL_remove (pending_head,
1951                                  pending_tail,
1952                                  task);
1953     queue_ready_task (task);
1954   }
1955 }
1956
1957
1958 /**
1959  * Function called by external event loop implementations to tell the
1960  * scheduler to run some of the tasks that are ready. Must be called
1961  * only after #GNUNET_SCHEDULER_driver_init has been called and before
1962  * #GNUNET_SCHEDULER_driver_done is called.
1963  * This function may return even though there are tasks left to run
1964  * just to give other tasks a chance as well.  If we return #GNUNET_YES,
1965  * the event loop implementation should call this function again as
1966  * soon as possible, while if we return #GNUNET_NO it must block until
1967  * either the operating system has more work (the scheduler has no more
1968  * work to do right now) or the timeout set by the scheduler (using the
1969  * set_wakeup callback) is reached.
1970  *
1971  * @param sh scheduler handle that was returned by
1972  *        #GNUNET_SCHEDULER_driver_init
1973  * @return #GNUNET_YES if there are more tasks that are ready,
1974  *         and thus we would like to run more (yield to avoid
1975  *         blocking other activities for too long) #GNUNET_NO
1976  *         if we are done running tasks (yield to block)
1977  */
1978 int
1979 GNUNET_SCHEDULER_do_work (struct GNUNET_SCHEDULER_Handle *sh)
1980 {
1981   enum GNUNET_SCHEDULER_Priority p;
1982   struct GNUNET_SCHEDULER_Task *pos;
1983   struct GNUNET_TIME_Absolute now;
1984
1985   /* check for tasks that reached the timeout! */
1986   now = GNUNET_TIME_absolute_get ();
1987   pos = pending_timeout_head;
1988   while (NULL != pos)
1989   {
1990     struct GNUNET_SCHEDULER_Task *next = pos->next;
1991     if (now.abs_value_us >= pos->timeout.abs_value_us)
1992       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1993     if (0 == pos->reason)
1994       break;
1995     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1996                                  pending_timeout_tail,
1997                                  pos);
1998     if (pending_timeout_last == pos)
1999       pending_timeout_last = NULL;
2000     queue_ready_task (pos);
2001     pos = next;
2002   }
2003   pos = pending_head;
2004   while (NULL != pos)
2005   {
2006     struct GNUNET_SCHEDULER_Task *next = pos->next;
2007     if (now.abs_value_us >= pos->timeout.abs_value_us)
2008     {
2009       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2010       GNUNET_CONTAINER_DLL_remove (pending_head,
2011                                    pending_tail,
2012                                    pos);
2013       queue_ready_task (pos);
2014     }
2015     pos = next;
2016   }
2017
2018   if (0 == ready_count)
2019   {
2020     struct GNUNET_TIME_Absolute timeout = get_timeout ();
2021
2022     if (timeout.abs_value_us > now.abs_value_us)
2023     {
2024       /**
2025        * The driver called this function before the current timeout was
2026        * reached (and no FD tasks are ready). This can happen in the
2027        * rare case when the system time is changed while the driver is
2028        * waiting for the timeout, so we handle this gracefully. It might
2029        * also be a programming error in the driver though.
2030        */
2031       LOG (GNUNET_ERROR_TYPE_DEBUG,
2032            "GNUNET_SCHEDULER_do_work did not find any ready "
2033            "tasks and timeout has not been reached yet.\n");
2034       return GNUNET_NO;
2035     }
2036     /**
2037      * the current timeout was reached but no ready tasks were found,
2038      * internal scheduler error!
2039      */
2040     GNUNET_assert (0);
2041   }
2042
2043   /* find out which task priority level we are going to
2044      process this time */
2045   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
2046   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
2047   /* yes, p>0 is correct, 0 is "KEEP" which should
2048    * always be an empty queue (see assertion)! */
2049   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
2050   {
2051     pos = ready_head[p];
2052     if (NULL != pos)
2053       break;
2054   }
2055   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
2056
2057   /* process all tasks at this priority level, then yield */
2058   while (NULL != (pos = ready_head[p]))
2059   {
2060     GNUNET_CONTAINER_DLL_remove (ready_head[p],
2061          ready_tail[p],
2062          pos);
2063     ready_count--;
2064     current_priority = pos->priority;
2065     current_lifeness = pos->lifeness;
2066     active_task = pos;
2067 #if PROFILE_DELAYS
2068     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
2069         DELAY_THRESHOLD.rel_value_us)
2070     {
2071       LOG (GNUNET_ERROR_TYPE_DEBUG,
2072            "Task %p took %s to be scheduled\n",
2073            pos,
2074            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
2075                                                    GNUNET_YES));
2076     }
2077 #endif
2078     tc.reason = pos->reason;
2079     GNUNET_NETWORK_fdset_zero (sh->rs);
2080     GNUNET_NETWORK_fdset_zero (sh->ws);
2081     // FIXME: do we have to remove FdInfos from fds if they are not ready?
2082     tc.fds_len = pos->fds_len;
2083     tc.fds = pos->fds;
2084     for (unsigned int i = 0; i != pos->fds_len; ++i)
2085     {
2086       struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
2087       if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
2088       {
2089         GNUNET_NETWORK_fdset_set_native (sh->rs,
2090                                          fdi->sock);
2091       }
2092       if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
2093       {
2094         GNUNET_NETWORK_fdset_set_native (sh->ws,
2095                                          fdi->sock);
2096       }
2097     }
2098     tc.read_ready = sh->rs;
2099     tc.write_ready = sh->ws;
2100     LOG (GNUNET_ERROR_TYPE_DEBUG,
2101          "Running task %p\n",
2102          pos);
2103     GNUNET_assert (NULL != pos->callback);
2104     pos->callback (pos->callback_cls);
2105     if (NULL != pos->fds)
2106     {
2107       int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
2108       if (GNUNET_OK != del_result)
2109       {
2110         LOG (GNUNET_ERROR_TYPE_ERROR,
2111            "driver could not delete task %p\n", pos);
2112         GNUNET_assert (0);
2113       }
2114     }
2115     active_task = NULL;
2116     dump_backtrace (pos);
2117     destroy_task (pos);
2118   }
2119   shutdown_if_no_lifeness ();
2120   if (0 == ready_count)
2121   {
2122     scheduler_driver->set_wakeup (scheduler_driver->cls,
2123                                   get_timeout ());
2124     return GNUNET_NO;
2125   }
2126   scheduler_driver->set_wakeup (scheduler_driver->cls,
2127                                 GNUNET_TIME_absolute_get ());
2128   return GNUNET_YES;
2129 }
2130
2131
2132 /**
2133  * Function called by external event loop implementations to initialize
2134  * the scheduler. An external implementation has to provide @a driver
2135  * which contains callbacks for the scheduler (see definition of struct
2136  * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
2137  * external implementation to watch for events. If it detects any of
2138  * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
2139  * the scheduler handle it. If an event is related to a specific task
2140  * (e.g. the scheduler gave instructions to watch a file descriptor),
2141  * the external implementation is expected to mark that task ready
2142  * before by calling #GNUNET_SCHEDULER_task_ready.
2143
2144  * This function has to be called before any tasks are scheduled and
2145  * before GNUNET_SCHEDULER_do_work is called for the first time. It
2146  * allocates resources that have to be freed again by calling
2147  * #GNUNET_SCHEDULER_driver_done.
2148  *
2149  * This function installs the same signal handlers as
2150  * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
2151  * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
2152  * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
2153  * active tasks to be scheduled with reason
2154  * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
2155  * will execute normally!). Note that any particular signal will only
2156  * shut down one scheduler; applications should always only create a
2157  * single scheduler.
2158  *
2159  * @param driver to use for the event loop
2160  * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
2161  *         #GNUNET_SCHEDULER_driver_done
2162  */
2163 struct GNUNET_SCHEDULER_Handle *
2164 GNUNET_SCHEDULER_driver_init (const struct GNUNET_SCHEDULER_Driver *driver)
2165 {
2166   struct GNUNET_SCHEDULER_Handle *sh;
2167   struct GNUNET_SCHEDULER_Task tsk;
2168   const struct GNUNET_DISK_FileHandle *pr;
2169
2170   /* general set-up */
2171   GNUNET_assert (NULL == active_task);
2172   GNUNET_assert (NULL == shutdown_pipe_handle);
2173   sh = GNUNET_new (struct GNUNET_SCHEDULER_Handle);
2174   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
2175                                            GNUNET_NO,
2176                                            GNUNET_NO,
2177                                            GNUNET_NO);
2178   GNUNET_assert (NULL != shutdown_pipe_handle);
2179   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2180                                 GNUNET_DISK_PIPE_END_READ);
2181   my_pid = getpid ();
2182   scheduler_driver = driver;
2183
2184   /* install signal handlers */
2185   LOG (GNUNET_ERROR_TYPE_DEBUG,
2186        "Registering signal handlers\n");
2187   sh->shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2188                                                &sighandler_shutdown);
2189   sh->shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2190                                                 &sighandler_shutdown);
2191 #if (SIGTERM != GNUNET_TERM_SIG)
2192   sh->shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2193                                                  &sighandler_shutdown);
2194 #endif
2195 #ifndef MINGW
2196   sh->shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2197                                                 &sighandler_pipe);
2198   sh->shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2199                                                 &sighandler_shutdown);
2200   sh->shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2201                                                &sighandler_shutdown);
2202 #endif
2203
2204   /* Setup initial tasks */
2205   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2206   current_lifeness = GNUNET_NO;
2207   memset (&tsk,
2208           0,
2209           sizeof (tsk));
2210   active_task = &tsk;
2211   install_parent_control_task =
2212     GNUNET_SCHEDULER_add_now (&install_parent_control_handler,
2213                               NULL);
2214   shutdown_pipe_task =
2215     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2216                                     pr,
2217                                     &shutdown_pipe_cb,
2218                                     NULL);
2219   current_lifeness = GNUNET_YES;
2220   active_task = NULL;
2221   scheduler_driver->set_wakeup (scheduler_driver->cls,
2222                                 get_timeout ());
2223   /* begin main event loop */
2224   sh->rs = GNUNET_NETWORK_fdset_create ();
2225   sh->ws = GNUNET_NETWORK_fdset_create ();
2226   GNUNET_NETWORK_fdset_handle_set (sh->rs, pr);
2227   return sh;
2228 }
2229
2230
2231 /**
2232  * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
2233  * by external event loop implementations after the scheduler has
2234  * shut down. This is the case if both of the following conditions
2235  * are met:
2236  *
2237  * - all tasks the scheduler has added through the driver's add
2238  *   callback have been removed again through the driver's del
2239  *   callback
2240  * - the timeout the scheduler has set through the driver's
2241  *   add_wakeup callback is FOREVER
2242  *
2243  * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
2244  */
2245 void
2246 GNUNET_SCHEDULER_driver_done (struct GNUNET_SCHEDULER_Handle *sh)
2247 {
2248   GNUNET_assert (NULL == pending_head);
2249   GNUNET_assert (NULL == pending_timeout_head);
2250   GNUNET_assert (NULL == shutdown_head);
2251   for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
2252   {
2253     GNUNET_assert (NULL == ready_head[i]);
2254   }
2255   GNUNET_NETWORK_fdset_destroy (sh->rs);
2256   GNUNET_NETWORK_fdset_destroy (sh->ws);
2257
2258   /* uninstall signal handlers */
2259   GNUNET_SIGNAL_handler_uninstall (sh->shc_int);
2260   GNUNET_SIGNAL_handler_uninstall (sh->shc_term);
2261 #if (SIGTERM != GNUNET_TERM_SIG)
2262   GNUNET_SIGNAL_handler_uninstall (sh->shc_gterm);
2263 #endif
2264 #ifndef MINGW
2265   GNUNET_SIGNAL_handler_uninstall (sh->shc_pipe);
2266   GNUNET_SIGNAL_handler_uninstall (sh->shc_quit);
2267   GNUNET_SIGNAL_handler_uninstall (sh->shc_hup);
2268 #endif
2269   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2270   shutdown_pipe_handle = NULL;
2271   scheduler_driver = NULL;
2272   GNUNET_free (sh);
2273 }
2274
2275
2276 static int
2277 select_loop (struct GNUNET_SCHEDULER_Handle *sh,
2278              struct DriverContext *context)
2279 {
2280   struct GNUNET_NETWORK_FDSet *rs;
2281   struct GNUNET_NETWORK_FDSet *ws;
2282   int select_result;
2283
2284   GNUNET_assert (NULL != context);
2285   rs = GNUNET_NETWORK_fdset_create ();
2286   ws = GNUNET_NETWORK_fdset_create ();
2287   while ( (NULL != context->scheduled_head) ||
2288           (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != context->timeout.abs_value_us) )
2289   {
2290     LOG (GNUNET_ERROR_TYPE_DEBUG,
2291          "select timeout = %s\n",
2292          GNUNET_STRINGS_absolute_time_to_string (context->timeout));
2293
2294     GNUNET_NETWORK_fdset_zero (rs);
2295     GNUNET_NETWORK_fdset_zero (ws);
2296
2297     for (struct Scheduled *pos = context->scheduled_head;
2298          NULL != pos;
2299          pos = pos->next)
2300     {
2301       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2302       {
2303         GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2304       }
2305       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2306       {
2307         GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2308       }
2309     }
2310     struct GNUNET_TIME_Relative time_remaining =
2311       GNUNET_TIME_absolute_get_remaining (context->timeout);
2312     if (NULL == scheduler_select)
2313     {
2314       select_result = GNUNET_NETWORK_socket_select (rs,
2315                                                     ws,
2316                                                     NULL,
2317                                                     time_remaining);
2318     }
2319     else
2320     {
2321       select_result = scheduler_select (scheduler_select_cls,
2322                                         rs,
2323                                         ws,
2324                                         NULL,
2325                                         time_remaining);
2326     }
2327     if (select_result == GNUNET_SYSERR)
2328     {
2329       if (errno == EINTR)
2330         continue;
2331
2332       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2333                     "select");
2334 #ifndef MINGW
2335 #if USE_LSOF
2336       char lsof[512];
2337
2338       snprintf (lsof,
2339                 sizeof (lsof),
2340                 "lsof -p %d",
2341                 getpid ());
2342       (void) close (1);
2343       (void) dup2 (2, 1);
2344       if (0 != system (lsof))
2345         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2346                       "system");
2347 #endif
2348 #endif
2349 #if DEBUG_FDS
2350       for (struct Scheduled *s = context->scheduled_head;
2351            NULL != s;
2352            s = s->next)
2353       {
2354         int flags = fcntl (s->fdi->sock,
2355                            F_GETFD);
2356
2357         if ( (flags == -1) &&
2358              (EBADF == errno) )
2359         {
2360           LOG (GNUNET_ERROR_TYPE_ERROR,
2361                "Got invalid file descriptor %d!\n",
2362                s->fdi->sock);
2363 #if EXECINFO
2364           dump_backtrace (s->task);
2365 #endif
2366         }
2367       }
2368 #endif
2369       GNUNET_assert (0);
2370       GNUNET_NETWORK_fdset_destroy (rs);
2371       GNUNET_NETWORK_fdset_destroy (ws);
2372       return GNUNET_SYSERR;
2373     }
2374     if (select_result > 0)
2375     {
2376       for (struct Scheduled *pos = context->scheduled_head;
2377            NULL != pos;
2378            pos = pos->next)
2379       {
2380         int is_ready = GNUNET_NO;
2381
2382         if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2383             GNUNET_YES ==
2384             GNUNET_NETWORK_fdset_test_native (rs,
2385                                               pos->fdi->sock))
2386         {
2387           pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2388           is_ready = GNUNET_YES;
2389         }
2390         if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2391             GNUNET_YES ==
2392             GNUNET_NETWORK_fdset_test_native (ws,
2393                                               pos->fdi->sock))
2394         {
2395           pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2396           is_ready = GNUNET_YES;
2397         }
2398         if (GNUNET_YES == is_ready)
2399         {
2400           GNUNET_SCHEDULER_task_ready (pos->task,
2401                                        pos->fdi);
2402         }
2403       }
2404     }
2405     if (GNUNET_YES == GNUNET_SCHEDULER_do_work (sh))
2406     {
2407       LOG (GNUNET_ERROR_TYPE_DEBUG,
2408            "scheduler has more tasks ready!\n");
2409     }
2410   }
2411   GNUNET_NETWORK_fdset_destroy (rs);
2412   GNUNET_NETWORK_fdset_destroy (ws);
2413   return GNUNET_OK;
2414 }
2415
2416
2417 static int
2418 select_add (void *cls,
2419             struct GNUNET_SCHEDULER_Task *task,
2420             struct GNUNET_SCHEDULER_FdInfo *fdi)
2421 {
2422   struct DriverContext *context = cls;
2423   GNUNET_assert (NULL != context);
2424   GNUNET_assert (NULL != task);
2425   GNUNET_assert (NULL != fdi);
2426   GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2427                  0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2428
2429   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2430   {
2431     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2432     return GNUNET_SYSERR;
2433   }
2434
2435   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2436   scheduled->task = task;
2437   scheduled->fdi = fdi;
2438   scheduled->et = fdi->et;
2439
2440   GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2441                                context->scheduled_tail,
2442                                scheduled);
2443   return GNUNET_OK;
2444 }
2445
2446
2447 static int
2448 select_del (void *cls,
2449             struct GNUNET_SCHEDULER_Task *task)
2450 {
2451   struct DriverContext *context;
2452   struct Scheduled *pos;
2453   int ret;
2454
2455   GNUNET_assert (NULL != cls);
2456
2457   context = cls;
2458   ret = GNUNET_SYSERR;
2459   pos = context->scheduled_head;
2460   while (NULL != pos)
2461   {
2462     struct Scheduled *next = pos->next;
2463     if (pos->task == task)
2464     {
2465       GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2466                                    context->scheduled_tail,
2467                                    pos);
2468       GNUNET_free (pos);
2469       ret = GNUNET_OK;
2470     }
2471     pos = next;
2472   }
2473   return ret;
2474 }
2475
2476
2477 static void
2478 select_set_wakeup (void *cls,
2479                    struct GNUNET_TIME_Absolute dt)
2480 {
2481   struct DriverContext *context = cls;
2482
2483   GNUNET_assert (NULL != context);
2484   context->timeout = dt;
2485 }
2486
2487
2488 /**
2489  * Obtain the driver for using select() as the event loop.
2490  *
2491  * @return NULL on error
2492  */
2493 struct GNUNET_SCHEDULER_Driver *
2494 GNUNET_SCHEDULER_driver_select ()
2495 {
2496   struct GNUNET_SCHEDULER_Driver *select_driver;
2497   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2498
2499   select_driver->add = &select_add;
2500   select_driver->del = &select_del;
2501   select_driver->set_wakeup = &select_set_wakeup;
2502
2503   return select_driver;
2504 }
2505
2506
2507 /* end of scheduler.c */