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