use absolute times in 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
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
30
31 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
32
33
34 #if HAVE_EXECINFO_H
35 #include "execinfo.h"
36
37 /**
38  * Use lsof to generate file descriptor reports on select error?
39  * (turn off for stable releases).
40  */
41 #define USE_LSOF GNUNET_NO
42
43 /**
44  * Obtain trace information for all scheduler calls that schedule tasks.
45  */
46 #define EXECINFO GNUNET_NO
47
48 /**
49  * Check each file descriptor before adding
50  */
51 #define DEBUG_FDS GNUNET_NO
52
53 /**
54  * Depth of the traces collected via EXECINFO.
55  */
56 #define MAX_TRACE_DEPTH 50
57 #endif
58
59 /**
60  * Should we figure out which tasks are delayed for a while
61  * before they are run? (Consider using in combination with EXECINFO).
62  */
63 #define PROFILE_DELAYS GNUNET_NO
64
65 /**
66  * Task that were in the queue for longer than this are reported if
67  * PROFILE_DELAYS is active.
68  */
69 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
70
71
72 /**
73  * Argument to be passed from the driver to
74  * #GNUNET_SCHEDULER_run_from_driver().  Contains the
75  * scheduler's internal state.
76  */
77 struct GNUNET_SCHEDULER_Handle
78 {
79   /**
80    * Passed here to avoid constantly allocating/deallocating
81    * this element, but generally we want to get rid of this.
82    * @deprecated
83    */
84   struct GNUNET_NETWORK_FDSet *rs;
85
86   /**
87    * Passed here to avoid constantly allocating/deallocating
88    * this element, but generally we want to get rid of this.
89    * @deprecated
90    */
91   struct GNUNET_NETWORK_FDSet *ws;
92 };
93
94
95 /**
96  * Entry in list of pending tasks.
97  */
98 struct GNUNET_SCHEDULER_Task
99 {
100   /**
101    * This is a linked list.
102    */
103   struct GNUNET_SCHEDULER_Task *next;
104
105   /**
106    * This is a linked list.
107    */
108   struct GNUNET_SCHEDULER_Task *prev;
109
110   /**
111    * Function to run when ready.
112    */
113   GNUNET_SCHEDULER_TaskCallback callback;
114
115   /**
116    * Closure for the @e callback.
117    */
118   void *callback_cls;
119
120   /**
121    * Information about which FDs are ready for this task (and why).
122    */
123   struct GNUNET_SCHEDULER_FdInfo *fds;
124
125   /**
126    * Storage location used for @e fds if we want to avoid
127    * a separate malloc() call in the common case that this
128    * task is only about a single FD.
129    */
130   struct GNUNET_SCHEDULER_FdInfo fdx;
131
132   /**
133    * Size of the @e fds array.
134    */
135   unsigned int fds_len;
136
137   /**
138    * Do we own the network and file handles referenced by the FdInfo
139    * structs in the fds array. This will only be GNUNET_YES if the
140    * task was created by the #GNUNET_SCHEDULER_add_select function.
141    */
142   int own_handles;
143
144   /**
145    * Absolute timeout value for the task, or
146    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
147    */
148   struct GNUNET_TIME_Absolute timeout;
149
150 #if PROFILE_DELAYS
151   /**
152    * When was the task scheduled?
153    */
154   struct GNUNET_TIME_Absolute start_time;
155 #endif
156
157   /**
158    * Why is the task ready?  Set after task is added to ready queue.
159    * Initially set to zero.  All reasons that have already been
160    * satisfied (i.e.  read or write ready) will be set over time.
161    */
162   enum GNUNET_SCHEDULER_Reason reason;
163
164   /**
165    * Task priority.
166    */
167   enum GNUNET_SCHEDULER_Priority priority;
168
169   /**
170    * Set if we only wait for reading from a single FD, otherwise -1.
171    */
172   int read_fd;
173
174   /**
175    * Set if we only wait for writing to a single FD, otherwise -1.
176    */
177   int write_fd;
178
179   /**
180    * Should the existence of this task in the queue be counted as
181    * reason to not shutdown the scheduler?
182    */
183   int lifeness;
184
185   /**
186    * Is this task run on shutdown?
187    */
188   int on_shutdown;
189
190   /**
191    * Is this task in the ready list?
192    */
193   int in_ready_list;
194
195 #if EXECINFO
196   /**
197    * Array of strings which make up a backtrace from the point when this
198    * task was scheduled (essentially, who scheduled the task?)
199    */
200   char **backtrace_strings;
201
202   /**
203    * Size of the backtrace_strings array
204    */
205   int num_backtrace_strings;
206 #endif
207
208 };
209
210
211 /**
212  * A struct representing an event the select driver is waiting for
213  */
214 struct Scheduled
215 {
216   struct Scheduled *prev;
217
218   struct Scheduled *next;
219
220   /**
221    * the task, the event is related to
222    */
223   struct GNUNET_SCHEDULER_Task *task;
224
225   /**
226    * information about the network socket / file descriptor where
227    * the event is expected to occur
228    */
229   struct GNUNET_SCHEDULER_FdInfo *fdi;
230
231   /**
232    * the event types (multiple event types can be ORed) the select
233    * driver is expected to wait for
234    */
235   enum GNUNET_SCHEDULER_EventType et;
236 };
237
238
239 /**
240  * Driver context used by GNUNET_SCHEDULER_run
241  */
242 struct DriverContext
243 {
244   /**
245    * the head of a DLL containing information about the events the
246    * select driver is waiting for
247    */
248   struct Scheduled *scheduled_head;
249
250   /**
251    * the tail of a DLL containing information about the events the
252    * select driver is waiting for
253    */
254   struct Scheduled *scheduled_tail;
255
256   /**
257    * the time when the select driver will wake up again (after
258    * calling select)
259    */
260   struct GNUNET_TIME_Absolute timeout;
261 };
262
263
264 /**
265  * The driver used for the event loop. Will be handed over to
266  * the scheduler in #GNUNET_SCHEDULER_run_from_driver(), peristed
267  * there in this variable for later use in functions like
268  * #GNUNET_SCHEDULER_add_select(), #add_without_sets() and
269  * #GNUNET_SCHEDULER_cancel().
270  */
271 static const struct GNUNET_SCHEDULER_Driver *scheduler_driver;
272
273 /**
274  * Head of list of tasks waiting for an event.
275  */
276 static struct GNUNET_SCHEDULER_Task *pending_head;
277
278 /**
279  * Tail of list of tasks waiting for an event.
280  */
281 static struct GNUNET_SCHEDULER_Task *pending_tail;
282
283 /**
284  * Head of list of tasks waiting for shutdown.
285  */
286 static struct GNUNET_SCHEDULER_Task *shutdown_head;
287
288 /**
289  * Tail of list of tasks waiting for shutdown.
290  */
291 static struct GNUNET_SCHEDULER_Task *shutdown_tail;
292
293 /**
294  * List of tasks waiting ONLY for a timeout event.
295  * Sorted by timeout (earliest first).  Used so that
296  * we do not traverse the list of these tasks when
297  * building select sets (we just look at the head
298  * to determine the respective timeout ONCE).
299  */
300 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
301
302 /**
303  * List of tasks waiting ONLY for a timeout event.
304  * Sorted by timeout (earliest first).  Used so that
305  * we do not traverse the list of these tasks when
306  * building select sets (we just look at the head
307  * to determine the respective timeout ONCE).
308  */
309 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
310
311 /**
312  * Last inserted task waiting ONLY for a timeout event.
313  * Used to (heuristically) speed up insertion.
314  */
315 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
316
317 /**
318  * ID of the task that is running right now.
319  */
320 static struct GNUNET_SCHEDULER_Task *active_task;
321
322 /**
323  * Head of list of tasks ready to run right now, grouped by importance.
324  */
325 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
326
327 /**
328  * Tail of list of tasks ready to run right now, grouped by importance.
329  */
330 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
331
332 /**
333  * Task for installing parent control handlers (it might happen that the
334  * scheduler is shutdown before this task is executed, so
335  * GNUNET_SCHEDULER_shutdown must cancel it in that case)
336  */
337 static struct GNUNET_SCHEDULER_Task *install_parent_control_task;
338
339 /**
340  * Task for reading from a pipe that signal handlers will use to initiate
341  * shutdown
342  */
343 static struct GNUNET_SCHEDULER_Task *shutdown_pipe_task;
344
345 /**
346  * Number of tasks on the ready list.
347  */
348 static unsigned int ready_count;
349
350 /**
351  * Priority of the task running right now.  Only
352  * valid while a task is running.
353  */
354 static enum GNUNET_SCHEDULER_Priority current_priority;
355
356 /**
357  * Priority of the highest task added in the current select
358  * iteration.
359  */
360 static enum GNUNET_SCHEDULER_Priority max_priority_added;
361
362 /**
363  * Value of the 'lifeness' flag for the current task.
364  */
365 static int current_lifeness;
366
367 /**
368  * Function to use as a select() in the scheduler.
369  * If NULL, we use GNUNET_NETWORK_socket_select().
370  */
371 static GNUNET_SCHEDULER_select scheduler_select;
372
373 /**
374  * Task context of the current task.
375  */
376 static struct GNUNET_SCHEDULER_TaskContext tc;
377
378 /**
379  * Closure for #scheduler_select.
380  */
381 static void *scheduler_select_cls;
382
383 /**
384  * Scheduler handle used for the driver functions
385  */
386 static struct GNUNET_SCHEDULER_Handle sh;
387
388
389 /**
390  * Sets the select function to use in the scheduler (scheduler_select).
391  *
392  * @param new_select new select function to use
393  * @param new_select_cls closure for @a new_select
394  * @return previously used select function, NULL for default
395  */
396 void
397 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
398                              void *new_select_cls)
399 {
400   scheduler_select = new_select;
401   scheduler_select_cls = new_select_cls;
402 }
403
404
405 /**
406  * Check that the given priority is legal (and return it).
407  *
408  * @param p priority value to check
409  * @return p on success, 0 on error
410  */
411 static enum GNUNET_SCHEDULER_Priority
412 check_priority (enum GNUNET_SCHEDULER_Priority p)
413 {
414   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
415     return p;
416   GNUNET_assert (0);
417   return 0;                     /* make compiler happy */
418 }
419
420
421 /**
422  * chooses the nearest timeout from all pending tasks, to be used
423  * to tell the driver the next wakeup time (using its set_wakeup
424  * callback)
425  */
426 struct GNUNET_TIME_Absolute
427 get_timeout ()
428 {
429   struct GNUNET_SCHEDULER_Task *pos;
430   struct GNUNET_TIME_Absolute now;
431   struct GNUNET_TIME_Absolute timeout;
432
433   pos = pending_timeout_head;
434   now = GNUNET_TIME_absolute_get ();
435   timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
436   if (NULL != pos)
437   {
438     if (0 != pos->reason)
439     {
440       return now;
441     }
442     else
443     {
444       timeout = pos->timeout;
445     }
446   }
447   for (pos = pending_head; NULL != pos; pos = pos->next)
448   {
449     if (0 != pos->reason)
450     {
451       return now;
452     }
453     else if ((pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
454              (timeout.abs_value_us > pos->timeout.abs_value_us))
455     {
456       timeout = pos->timeout;
457     }
458   }
459   return timeout;
460 }
461
462
463 /**
464  * Put a task that is ready for execution into the ready queue.
465  *
466  * @param task task ready for execution
467  */
468 static void
469 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
470 {
471   enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
472
473   GNUNET_CONTAINER_DLL_insert (ready_head[p],
474                                ready_tail[p],
475                                task);
476   task->in_ready_list = GNUNET_YES;
477   ready_count++;
478 }
479
480
481 /**
482  * Request the shutdown of a scheduler.  Marks all tasks
483  * awaiting shutdown as ready. Note that tasks
484  * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
485  * will be delayed until the next shutdown signal.
486  */
487 void
488 GNUNET_SCHEDULER_shutdown ()
489 {
490   struct GNUNET_SCHEDULER_Task *pos;
491
492   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
493               "GNUNET_SCHEDULER_shutdown\n");
494   if (NULL != install_parent_control_task)
495   {
496     GNUNET_SCHEDULER_cancel (install_parent_control_task);
497     install_parent_control_task = NULL;
498   }
499   if (NULL != shutdown_pipe_task)
500   {
501     GNUNET_SCHEDULER_cancel (shutdown_pipe_task);
502     shutdown_pipe_task = NULL;
503   }
504   while (NULL != (pos = shutdown_head))
505   {
506     GNUNET_CONTAINER_DLL_remove (shutdown_head,
507                                  shutdown_tail,
508                                  pos);
509     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
510     queue_ready_task (pos);
511   }
512 }
513
514
515 /**
516  * Output stack trace of task @a t.
517  *
518  * @param t task to dump stack trace of
519  */
520 static void
521 dump_backtrace (struct GNUNET_SCHEDULER_Task *t)
522 {
523 #if EXECINFO
524   for (unsigned int i = 0; i < t->num_backtrace_strings; i++)
525     LOG (GNUNET_ERROR_TYPE_WARNING,
526          "Task %p trace %u: %s\n",
527          t,
528          i,
529          t->backtrace_strings[i]);
530 #else
531   (void) t;
532 #endif
533 }
534
535
536 /**
537  * Destroy a task (release associated resources)
538  *
539  * @param t task to destroy
540  */
541 static void
542 destroy_task (struct GNUNET_SCHEDULER_Task *t)
543 {
544   unsigned int i;
545
546   LOG (GNUNET_ERROR_TYPE_DEBUG,
547        "destroying task %p\n",
548        t);
549
550   if (GNUNET_YES == t->own_handles)
551   {
552     for (i = 0; i != t->fds_len; ++i)
553     {
554       const struct GNUNET_NETWORK_Handle *fd = t->fds[i].fd;
555       const struct GNUNET_DISK_FileHandle *fh = t->fds[i].fh;
556       if (fd)
557       {
558         GNUNET_NETWORK_socket_free_memory_only_ ((struct GNUNET_NETWORK_Handle *) fd);
559       }
560       if (fh)
561       {
562         // FIXME: on WIN32 this is not enough! A function
563         // GNUNET_DISK_file_free_memory_only would be nice
564         GNUNET_free ((void *) fh);
565       }
566     }
567   }
568   if (t->fds_len > 1)
569   {
570     GNUNET_array_grow (t->fds, t->fds_len, 0);
571   }
572 #if EXECINFO
573   GNUNET_free (t->backtrace_strings);
574 #endif
575   GNUNET_free (t);
576 }
577
578
579 /**
580  * Pipe used to communicate shutdown via signal.
581  */
582 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
583
584 /**
585  * Process ID of this process at the time we installed the various
586  * signal handlers.
587  */
588 static pid_t my_pid;
589
590 /**
591  * Signal handler called for SIGPIPE.
592  */
593 #ifndef MINGW
594 static void
595 sighandler_pipe ()
596 {
597   return;
598 }
599 #endif
600
601
602 ///**
603 // * Wait for a short time.
604 // * Sleeps for @a ms ms (as that should be long enough for virtually all
605 // * modern systems to context switch and allow another process to do
606 // * some 'real' work).
607 // *
608 // * @param ms how many ms to wait
609 // */
610 //static void
611 //short_wait (unsigned int ms)
612 //{
613 //  struct GNUNET_TIME_Relative timeout;
614 //
615 //  timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
616 //  (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
617 //}
618
619
620 /**
621  * Signal handler called for signals that should cause us to shutdown.
622  */
623 static void
624 sighandler_shutdown ()
625 {
626   static char c;
627   int old_errno = errno;        /* backup errno */
628
629   if (getpid () != my_pid)
630     exit (1);                   /* we have fork'ed since the signal handler was created,
631                                  * ignore the signal, see https://gnunet.org/vfork discussion */
632   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
633                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
634                           &c, sizeof (c));
635   errno = old_errno;
636 }
637
638
639 void
640 shutdown_if_no_lifeness ()
641 {
642   struct GNUNET_SCHEDULER_Task *t;
643
644   if (ready_count > 0)
645     return;
646   for (t = pending_head; NULL != t; t = t->next)
647     if (GNUNET_YES == t->lifeness)
648       return;
649   for (t = shutdown_head; NULL != t; t = t->next)
650     if (GNUNET_YES == t->lifeness)
651       return;
652   for (t = pending_timeout_head; NULL != t; t = t->next)
653     if (GNUNET_YES == t->lifeness)
654       return;
655   /* No lifeness! */
656   GNUNET_SCHEDULER_shutdown ();
657 }
658
659
660 /**
661  * Initialize and run scheduler.  This function will return when all
662  * tasks have completed.  On systems with signals, receiving a SIGTERM
663  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
664  * to be run after the active task is complete.  As a result, SIGTERM
665  * causes all active tasks to be scheduled with reason
666  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
667  * afterwards will execute normally!). Note that any particular signal
668  * will only shut down one scheduler; applications should always only
669  * create a single scheduler.
670  *
671  * @param task task to run immediately
672  * @param task_cls closure of @a task
673  */
674 void
675 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
676                       void *task_cls)
677 {
678   struct GNUNET_SCHEDULER_Driver *driver;
679   struct DriverContext context = {.scheduled_head = NULL,
680                                   .scheduled_tail = NULL,
681                                   .timeout = GNUNET_TIME_UNIT_FOREVER_ABS};
682
683   driver = GNUNET_SCHEDULER_driver_select ();
684   driver->cls = &context;
685
686   GNUNET_SCHEDULER_run_with_driver (driver, task, task_cls);
687
688   GNUNET_free (driver);
689 }
690
691
692 /**
693  * Obtain the task context, giving the reason why the current task was
694  * started.
695  *
696  * @return current tasks' scheduler context
697  */
698 const struct GNUNET_SCHEDULER_TaskContext *
699 GNUNET_SCHEDULER_get_task_context ()
700 {
701   GNUNET_assert (NULL != active_task);
702   return &tc;
703 }
704
705
706 /**
707  * Get information about the current load of this scheduler.  Use this
708  * function to determine if an elective task should be added or simply
709  * dropped (if the decision should be made based on the number of
710  * tasks ready to run).
711  *
712  * @param p priority level to look at
713  * @return number of tasks pending right now
714  */
715 unsigned int
716 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
717 {
718   struct GNUNET_SCHEDULER_Task *pos;
719   unsigned int ret;
720
721   GNUNET_assert (NULL != active_task);
722   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
723     return ready_count;
724   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
725     p = current_priority;
726   ret = 0;
727   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
728     ret++;
729   return ret;
730 }
731
732
733 void
734 init_fd_info (struct GNUNET_SCHEDULER_Task *t,
735               const struct GNUNET_NETWORK_Handle *const *read_nh,
736               unsigned int read_nh_len,
737               const struct GNUNET_NETWORK_Handle *const *write_nh,
738               unsigned int write_nh_len,
739               const struct GNUNET_DISK_FileHandle *const *read_fh,
740               unsigned int read_fh_len,
741               const struct GNUNET_DISK_FileHandle *const *write_fh,
742               unsigned int write_fh_len)
743 {
744   // FIXME: if we have exactly two network handles / exactly two file handles
745   // and they are equal, we can make one FdInfo with both
746   // GNUNET_SCHEDULER_ET_IN and GNUNET_SCHEDULER_ET_OUT set.
747   struct GNUNET_SCHEDULER_FdInfo *fdi;
748
749   t->fds_len = read_nh_len + write_nh_len + read_fh_len + write_fh_len;
750   if (1 == t->fds_len)
751   {
752     fdi = &t->fdx;
753     t->fds = fdi;
754     if (1 == read_nh_len)
755     {
756       GNUNET_assert (NULL != read_nh);
757       GNUNET_assert (NULL != *read_nh);
758       fdi->fd = *read_nh;
759       fdi->et = GNUNET_SCHEDULER_ET_IN;
760       fdi->sock = GNUNET_NETWORK_get_fd (*read_nh);
761       t->read_fd = fdi->sock;
762       t->write_fd = -1;
763     }
764     else if (1 == write_nh_len)
765     {
766       GNUNET_assert (NULL != write_nh);
767       GNUNET_assert (NULL != *write_nh);
768       fdi->fd = *write_nh;
769       fdi->et = GNUNET_SCHEDULER_ET_OUT;
770       fdi->sock = GNUNET_NETWORK_get_fd (*write_nh);
771       t->read_fd = -1;
772       t->write_fd = fdi->sock;
773     }
774     else if (1 == read_fh_len)
775     {
776       GNUNET_assert (NULL != read_fh);
777       GNUNET_assert (NULL != *read_fh);
778       fdi->fh = *read_fh;
779       fdi->et = GNUNET_SCHEDULER_ET_IN;
780       fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
781       t->read_fd = fdi->sock;
782       t->write_fd = -1;
783     }
784     else
785     {
786       GNUNET_assert (NULL != write_fh);
787       GNUNET_assert (NULL != *write_fh);
788       fdi->fh = *write_fh;
789       fdi->et = GNUNET_SCHEDULER_ET_OUT;
790       fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
791       t->read_fd = -1;
792       t->write_fd = fdi->sock;
793     }
794   }
795   else
796   {
797     fdi = GNUNET_new_array (t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
798     t->fds = fdi;
799     t->read_fd = -1;
800     t->write_fd = -1;
801     unsigned int i;
802     for (i = 0; i != read_nh_len; ++i)
803     {
804       fdi->fd = read_nh[i];
805       GNUNET_assert (NULL != fdi->fd);
806       fdi->et = GNUNET_SCHEDULER_ET_IN;
807       fdi->sock = GNUNET_NETWORK_get_fd (read_nh[i]);
808       ++fdi;
809     }
810     for (i = 0; i != write_nh_len; ++i)
811     {
812       fdi->fd = write_nh[i];
813       GNUNET_assert (NULL != fdi->fd);
814       fdi->et = GNUNET_SCHEDULER_ET_OUT;
815       fdi->sock = GNUNET_NETWORK_get_fd (write_nh[i]);
816       ++fdi;
817     }
818     for (i = 0; i != read_fh_len; ++i)
819     {
820       fdi->fh = read_fh[i];
821       GNUNET_assert (NULL != fdi->fh);
822       fdi->et = GNUNET_SCHEDULER_ET_IN;
823       fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
824       ++fdi;
825     }
826     for (i = 0; i != write_fh_len; ++i)
827     {
828       fdi->fh = write_fh[i];
829       GNUNET_assert (NULL != fdi->fh);
830       fdi->et = GNUNET_SCHEDULER_ET_OUT;
831       fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
832       ++fdi;
833     }
834   }
835 }
836
837
838 /**
839  * calls the given function @a func on each FdInfo related to @a t.
840  * Optionally updates the event type field in each FdInfo after calling
841  * @a func.
842  *
843  * @param t the task
844  * @param driver_func the function to call with each FdInfo contained in
845  *                    in @a t
846  * @param if_not_ready only call @a driver_func on FdInfos that are not
847  *                     ready
848  * @param et the event type to be set in each FdInfo after calling
849  *           @a driver_func on it, or -1 if no updating not desired.
850  */
851 static void
852 driver_add_multiple (struct GNUNET_SCHEDULER_Task *t)
853 {
854   struct GNUNET_SCHEDULER_FdInfo *fdi;
855   int success = GNUNET_YES;
856
857   for (unsigned int i = 0; i != t->fds_len; ++i)
858   {
859     fdi = &t->fds[i];
860     success = scheduler_driver->add (scheduler_driver->cls,
861                                      t,
862                                      fdi) && success;
863     fdi->et = GNUNET_SCHEDULER_ET_NONE;
864   }
865   if (GNUNET_YES != success)
866   {
867     LOG (GNUNET_ERROR_TYPE_ERROR,
868          "driver could not add task\n");
869   }
870 }
871
872
873 static void
874 install_parent_control_handler (void *cls)
875 {
876   install_parent_control_task = NULL;
877   GNUNET_OS_install_parent_control_handler (NULL);
878 }
879
880
881 static void
882 shutdown_pipe_cb (void *cls)
883 {
884   char c;
885   const struct GNUNET_DISK_FileHandle *pr;
886
887   shutdown_pipe_task = NULL;
888   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
889                                 GNUNET_DISK_PIPE_END_READ);
890   GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
891   /* consume the signal */
892   GNUNET_DISK_file_read (pr, &c, sizeof (c));
893   /* mark all active tasks as ready due to shutdown */
894   GNUNET_SCHEDULER_shutdown ();
895 }
896
897
898 /**
899  * Cancel the task with the specified identifier.
900  * The task must not yet have run. Only allowed to be called as long as the
901  * scheduler is running (#GNUNET_SCHEDULER_run or
902  * #GNUNET_SCHEDULER_run_with_driver has been called and has not returned yet).
903  *
904  * @param task id of the task to cancel
905  * @return original closure of the task
906  */
907 void *
908 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
909 {
910   enum GNUNET_SCHEDULER_Priority p;
911   int is_fd_task;
912   void *ret;
913
914   LOG (GNUNET_ERROR_TYPE_DEBUG,
915        "canceling task %p\n",
916        task);
917
918   /* scheduler must be running */
919   GNUNET_assert (NULL != scheduler_driver);
920   GNUNET_assert ( (NULL != active_task) ||
921       (GNUNET_NO == task->lifeness) );
922   is_fd_task = (NULL != task->fds);
923   if (is_fd_task)
924   {
925     int del_result = scheduler_driver->del (scheduler_driver->cls, task);
926     if (GNUNET_OK != del_result)
927     {
928       LOG (GNUNET_ERROR_TYPE_ERROR,
929            "driver could not delete task\n");
930       GNUNET_assert (0);
931     }
932   }
933   if (! task->in_ready_list)
934   {
935     if (is_fd_task)
936     {
937       GNUNET_CONTAINER_DLL_remove (pending_head,
938                                    pending_tail,
939                                    task);
940     }
941     else if (GNUNET_YES == task->on_shutdown)
942     {
943       GNUNET_CONTAINER_DLL_remove (shutdown_head,
944                                    shutdown_tail,
945                                    task);
946     }
947     else
948     {
949       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
950                                    pending_timeout_tail,
951                                    task);
952       if (pending_timeout_last == task)
953         pending_timeout_last = NULL;
954     }
955   }
956   else
957   {
958     p = check_priority (task->priority);
959     GNUNET_CONTAINER_DLL_remove (ready_head[p],
960                                  ready_tail[p],
961                                  task);
962     ready_count--;
963   }
964   ret = task->callback_cls;
965   destroy_task (task);
966   return ret;
967 }
968
969
970 /**
971  * Initialize backtrace data for task @a t
972  *
973  * @param t task to initialize
974  */
975 static void
976 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
977 {
978 #if EXECINFO
979   void *backtrace_array[MAX_TRACE_DEPTH];
980
981   t->num_backtrace_strings
982     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
983   t->backtrace_strings =
984       backtrace_symbols (backtrace_array,
985        t->num_backtrace_strings);
986   dump_backtrace (t);
987 #else
988   (void) t;
989 #endif
990 }
991
992
993 /**
994  * Continue the current execution with the given function.  This is
995  * similar to the other "add" functions except that there is no delay
996  * and the reason code can be specified.
997  *
998  * @param task main function of the task
999  * @param task_cls closure for @a task
1000  * @param reason reason for task invocation
1001  * @param priority priority to use for the task
1002  */
1003 void
1004 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1005                                                void *task_cls,
1006                                                enum GNUNET_SCHEDULER_Reason reason,
1007                                                enum GNUNET_SCHEDULER_Priority priority)
1008 {
1009   struct GNUNET_SCHEDULER_Task *t;
1010
1011   GNUNET_assert (NULL != task);
1012   GNUNET_assert ((NULL != active_task) ||
1013                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1014   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1015   t->read_fd = -1;
1016   t->write_fd = -1;
1017   t->callback = task;
1018   t->callback_cls = task_cls;
1019 #if PROFILE_DELAYS
1020   t->start_time = GNUNET_TIME_absolute_get ();
1021 #endif
1022   t->reason = reason;
1023   t->priority = check_priority (priority);
1024   t->lifeness = current_lifeness;
1025   LOG (GNUNET_ERROR_TYPE_DEBUG,
1026        "Adding continuation task %p\n",
1027        t);
1028   init_backtrace (t);
1029   queue_ready_task (t);
1030 }
1031
1032
1033 /**
1034  * Schedule a new task to be run at the specified time.  The task
1035  * will be scheduled for execution at time @a at.
1036  *
1037  * @param at time when the operation should run
1038  * @param priority priority to use for the task
1039  * @param task main function of the task
1040  * @param task_cls closure of @a task
1041  * @return unique task identifier for the job
1042  *         only valid until @a task is started!
1043  */
1044 struct GNUNET_SCHEDULER_Task *
1045 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1046                                        enum GNUNET_SCHEDULER_Priority priority,
1047                                        GNUNET_SCHEDULER_TaskCallback task,
1048                                        void *task_cls)
1049 {
1050   struct GNUNET_SCHEDULER_Task *t;
1051   struct GNUNET_SCHEDULER_Task *pos;
1052   struct GNUNET_SCHEDULER_Task *prev;
1053
1054   GNUNET_assert (NULL != active_task);
1055   GNUNET_assert (NULL != task);
1056   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1057   t->callback = task;
1058   t->callback_cls = task_cls;
1059   t->read_fd = -1;
1060   t->write_fd = -1;
1061 #if PROFILE_DELAYS
1062   t->start_time = GNUNET_TIME_absolute_get ();
1063 #endif
1064   t->timeout = at;
1065   t->priority = check_priority (priority);
1066   t->lifeness = current_lifeness;
1067   /* try tail first (optimization in case we are
1068    * appending to a long list of tasks with timeouts) */
1069   if ( (NULL == pending_timeout_head) ||
1070        (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
1071   {
1072     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1073                                  pending_timeout_tail,
1074                                  t);
1075   }
1076   else
1077   {
1078     /* first move from heuristic start backwards to before start time */
1079     prev = pending_timeout_last;
1080     while ( (NULL != prev) &&
1081             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1082       prev = prev->prev;
1083     /* now, move from heuristic start (or head of list) forward to insertion point */
1084     if (NULL == prev)
1085       pos = pending_timeout_head;
1086     else
1087       pos = prev->next;
1088     while ( (NULL != pos) &&
1089             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1090               (0 != pos->reason) ) )
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     tasks_ready = GNUNET_SCHEDULER_run_from_driver (sh);
2339     GNUNET_assert (GNUNET_SYSERR != tasks_ready);
2340   }
2341   GNUNET_NETWORK_fdset_destroy (rs);
2342   GNUNET_NETWORK_fdset_destroy (ws);
2343   return GNUNET_OK;
2344 }
2345
2346
2347 void
2348 select_set_wakeup (void *cls,
2349                    struct GNUNET_TIME_Absolute dt)
2350 {
2351   struct DriverContext *context = cls;
2352   
2353   GNUNET_assert (NULL != context);
2354   context->timeout = dt;
2355 }
2356
2357
2358 /**
2359  * Obtain the driver for using select() as the event loop.
2360  *
2361  * @return NULL on error
2362  */
2363 struct GNUNET_SCHEDULER_Driver *
2364 GNUNET_SCHEDULER_driver_select ()
2365 {
2366   struct GNUNET_SCHEDULER_Driver *select_driver;
2367   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2368
2369   select_driver->loop = &select_loop;
2370   select_driver->add = &select_add;
2371   select_driver->del = &select_del;
2372   select_driver->set_wakeup = &select_set_wakeup;
2373
2374   return select_driver;
2375 }
2376
2377
2378 /* end of scheduler.c */