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