make some functions static, ensure shutdown tasks could be run repeatedly if 1st...
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2009-2017 GNUnet e.V.
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file util/scheduler.c
22  * @brief schedule computations using continuation passing style
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "disk.h"
28 // DEBUG
29 #include <inttypes.h>
30
31 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
32
33 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
34
35
36 #if HAVE_EXECINFO_H
37 #include "execinfo.h"
38
39 /**
40  * Use lsof to generate file descriptor reports on select error?
41  * (turn off for stable releases).
42  */
43 #define USE_LSOF GNUNET_NO
44
45 /**
46  * Obtain trace information for all scheduler calls that schedule tasks.
47  */
48 #define EXECINFO GNUNET_NO
49
50 /**
51  * Check each file descriptor before adding
52  */
53 #define DEBUG_FDS GNUNET_NO
54
55 /**
56  * Depth of the traces collected via EXECINFO.
57  */
58 #define MAX_TRACE_DEPTH 50
59 #endif
60
61 /**
62  * Should we figure out which tasks are delayed for a while
63  * before they are run? (Consider using in combination with EXECINFO).
64  */
65 #define PROFILE_DELAYS GNUNET_NO
66
67 /**
68  * Task that were in the queue for longer than this are reported if
69  * PROFILE_DELAYS is active.
70  */
71 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
72
73
74 /**
75  * Argument to be passed from the driver to
76  * #GNUNET_SCHEDULER_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   shutdown_pipe_task =
938     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
939                                     pr,
940                                     &shutdown_pipe_cb,
941                                     NULL);
942 }
943
944
945 /**
946  * Cancel the task with the specified identifier.
947  * The task must not yet have run. Only allowed to be called as long as the
948  * scheduler is running, that is one of the following conditions is met:
949  *
950  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
951  * - #GNUNET_SCHEDULER_driver_init has been run and
952  *   #GNUNET_SCHEDULER_driver_done has not been called yet
953  *
954  * @param task id of the task to cancel
955  * @return original closure of the task
956  */
957 void *
958 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
959 {
960   enum GNUNET_SCHEDULER_Priority p;
961   int is_fd_task;
962   void *ret;
963
964   LOG (GNUNET_ERROR_TYPE_DEBUG,
965        "canceling task %p\n",
966        task);
967
968   /* scheduler must be running */
969   GNUNET_assert (NULL != scheduler_driver);
970   GNUNET_assert ( (NULL != active_task) ||
971       (GNUNET_NO == task->lifeness) );
972   is_fd_task = (NULL != task->fds);
973   if (is_fd_task)
974   {
975     int del_result = scheduler_driver->del (scheduler_driver->cls, task);
976     if (GNUNET_OK != del_result)
977     {
978       LOG (GNUNET_ERROR_TYPE_ERROR,
979            "driver could not delete task\n");
980       GNUNET_assert (0);
981     }
982   }
983   if (! task->in_ready_list)
984   {
985     if (is_fd_task)
986     {
987       GNUNET_CONTAINER_DLL_remove (pending_head,
988                                    pending_tail,
989                                    task);
990     }
991     else if (GNUNET_YES == task->on_shutdown)
992     {
993       GNUNET_CONTAINER_DLL_remove (shutdown_head,
994                                    shutdown_tail,
995                                    task);
996     }
997     else
998     {
999       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1000                                    pending_timeout_tail,
1001                                    task);
1002       if (pending_timeout_last == task)
1003         pending_timeout_last = NULL;
1004     }
1005   }
1006   else
1007   {
1008     p = check_priority (task->priority);
1009     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1010                                  ready_tail[p],
1011                                  task);
1012     ready_count--;
1013   }
1014   ret = task->callback_cls;
1015   destroy_task (task);
1016   return ret;
1017 }
1018
1019
1020 /**
1021  * Initialize backtrace data for task @a t
1022  *
1023  * @param t task to initialize
1024  */
1025 static void
1026 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
1027 {
1028 #if EXECINFO
1029   void *backtrace_array[MAX_TRACE_DEPTH];
1030
1031   t->num_backtrace_strings
1032     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1033   t->backtrace_strings =
1034       backtrace_symbols (backtrace_array,
1035        t->num_backtrace_strings);
1036   dump_backtrace (t);
1037 #else
1038   (void) t;
1039 #endif
1040 }
1041
1042
1043 /**
1044  * Continue the current execution with the given function.  This is
1045  * similar to the other "add" functions except that there is no delay
1046  * and the reason code can be specified.
1047  *
1048  * @param task main function of the task
1049  * @param task_cls closure for @a task
1050  * @param reason reason for task invocation
1051  * @param priority priority to use for the task
1052  */
1053 void
1054 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1055                                                void *task_cls,
1056                                                enum GNUNET_SCHEDULER_Reason reason,
1057                                                enum GNUNET_SCHEDULER_Priority priority)
1058 {
1059   struct GNUNET_SCHEDULER_Task *t;
1060
1061   GNUNET_assert (NULL != task);
1062   GNUNET_assert ((NULL != active_task) ||
1063                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1064   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1065   t->read_fd = -1;
1066   t->write_fd = -1;
1067   t->callback = task;
1068   t->callback_cls = task_cls;
1069 #if PROFILE_DELAYS
1070   t->start_time = GNUNET_TIME_absolute_get ();
1071 #endif
1072   t->reason = reason;
1073   t->priority = check_priority (priority);
1074   t->lifeness = current_lifeness;
1075   LOG (GNUNET_ERROR_TYPE_DEBUG,
1076        "Adding continuation task %p\n",
1077        t);
1078   init_backtrace (t);
1079   queue_ready_task (t);
1080 }
1081
1082
1083 /**
1084  * Schedule a new task to be run at the specified time.  The task
1085  * will be scheduled for execution at time @a at.
1086  *
1087  * @param at time when the operation should run
1088  * @param priority priority to use for the task
1089  * @param task main function of the task
1090  * @param task_cls closure of @a task
1091  * @return unique task identifier for the job
1092  *         only valid until @a task is started!
1093  */
1094 struct GNUNET_SCHEDULER_Task *
1095 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1096                                        enum GNUNET_SCHEDULER_Priority priority,
1097                                        GNUNET_SCHEDULER_TaskCallback task,
1098                                        void *task_cls)
1099 {
1100   struct GNUNET_SCHEDULER_Task *t;
1101   struct GNUNET_SCHEDULER_Task *pos;
1102   struct GNUNET_SCHEDULER_Task *prev;
1103
1104   GNUNET_assert (NULL != active_task);
1105   GNUNET_assert (NULL != task);
1106   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1107   t->callback = task;
1108   t->callback_cls = task_cls;
1109   t->read_fd = -1;
1110   t->write_fd = -1;
1111 #if PROFILE_DELAYS
1112   t->start_time = GNUNET_TIME_absolute_get ();
1113 #endif
1114   t->timeout = at;
1115   t->priority = check_priority (priority);
1116   t->lifeness = current_lifeness;
1117   /* try tail first (optimization in case we are
1118    * appending to a long list of tasks with timeouts) */
1119   if ( (NULL == pending_timeout_head) ||
1120        (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
1121   {
1122     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1123                                  pending_timeout_tail,
1124                                  t);
1125   }
1126   else
1127   {
1128     /* first move from heuristic start backwards to before start time */
1129     prev = pending_timeout_last;
1130     while ( (NULL != prev) &&
1131             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1132       prev = prev->prev;
1133     /* now, move from heuristic start (or head of list) forward to insertion point */
1134     if (NULL == prev)
1135       pos = pending_timeout_head;
1136     else
1137       pos = prev->next;
1138     while ((NULL != pos) && (pos->timeout.abs_value_us <= t->timeout.abs_value_us))
1139     {
1140       prev = pos;
1141       pos = pos->next;
1142     }
1143     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1144                                        pending_timeout_tail,
1145                                        prev,
1146                                        t);
1147   }
1148   /* finally, update heuristic insertion point to last insertion... */
1149   pending_timeout_last = t;
1150
1151   LOG (GNUNET_ERROR_TYPE_DEBUG,
1152        "Adding task %p\n",
1153        t);
1154   init_backtrace (t);
1155   return t;
1156 }
1157
1158
1159 /**
1160  * Schedule a new task to be run with a specified delay.  The task
1161  * will be scheduled for execution once the delay has expired.
1162  *
1163  * @param delay when should this operation time out?
1164  * @param priority priority to use for the task
1165  * @param task main function of the task
1166  * @param task_cls closure of @a task
1167  * @return unique task identifier for the job
1168  *         only valid until @a task is started!
1169  */
1170 struct GNUNET_SCHEDULER_Task *
1171 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1172                                             enum GNUNET_SCHEDULER_Priority priority,
1173                                             GNUNET_SCHEDULER_TaskCallback task,
1174                                             void *task_cls)
1175 {
1176   return GNUNET_SCHEDULER_add_at_with_priority (GNUNET_TIME_relative_to_absolute (delay),
1177                                                 priority,
1178                                                 task,
1179                                                 task_cls);
1180 }
1181
1182
1183 /**
1184  * Schedule a new task to be run with a specified priority.
1185  *
1186  * @param prio how important is the new task?
1187  * @param task main function of the task
1188  * @param task_cls closure of @a task
1189  * @return unique task identifier for the job
1190  *         only valid until @a task is started!
1191  */
1192 struct GNUNET_SCHEDULER_Task *
1193 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1194                                     GNUNET_SCHEDULER_TaskCallback task,
1195                                     void *task_cls)
1196 {
1197   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1198                                                      prio,
1199                                                      task,
1200                                                      task_cls);
1201 }
1202
1203
1204 /**
1205  * Schedule a new task to be run at the specified time.  The task
1206  * will be scheduled for execution once specified time has been
1207  * reached. It will be run with the DEFAULT priority.
1208  *
1209  * @param at time at which this operation should run
1210  * @param task main function of the task
1211  * @param task_cls closure of @a task
1212  * @return unique task identifier for the job
1213  *         only valid until @a task is started!
1214  */
1215 struct GNUNET_SCHEDULER_Task *
1216 GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1217                          GNUNET_SCHEDULER_TaskCallback task,
1218                          void *task_cls)
1219 {
1220   return GNUNET_SCHEDULER_add_at_with_priority (at,
1221                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1222                                                 task,
1223                                                 task_cls);
1224 }
1225
1226
1227 /**
1228  * Schedule a new task to be run with a specified delay.  The task
1229  * will be scheduled for execution once the delay has expired. It
1230  * will be run with the DEFAULT priority.
1231  *
1232  * @param delay when should this operation time out?
1233  * @param task main function of the task
1234  * @param task_cls closure of @a task
1235  * @return unique task identifier for the job
1236  *         only valid until @a task is started!
1237  */
1238 struct GNUNET_SCHEDULER_Task *
1239 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1240                               GNUNET_SCHEDULER_TaskCallback task,
1241                               void *task_cls)
1242 {
1243   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1244                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1245                  task,
1246                  task_cls);
1247 }
1248
1249
1250 /**
1251  * Schedule a new task to be run as soon as possible.  Note that this
1252  * does not guarantee that this will be the next task that is being
1253  * run, as other tasks with higher priority (or that are already ready
1254  * to run) might get to run first.  Just as with delays, clients must
1255  * not rely on any particular order of execution between tasks
1256  * scheduled concurrently.
1257  *
1258  * The task will be run with the DEFAULT priority.
1259  *
1260  * @param task main function of the task
1261  * @param task_cls closure of @a task
1262  * @return unique task identifier for the job
1263  *         only valid until @a task is started!
1264  */
1265 struct GNUNET_SCHEDULER_Task *
1266 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1267                           void *task_cls)
1268 {
1269   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
1270                                        task,
1271                                        task_cls);
1272 }
1273
1274
1275 /**
1276  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1277  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1278  * invoked.
1279  *
1280  * @param task main function of the task
1281  * @param task_cls closure of @a task
1282  * @return unique task identifier for the job
1283  *         only valid until @a task is started!
1284  */
1285 struct GNUNET_SCHEDULER_Task *
1286 GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1287                                void *task_cls)
1288 {
1289   struct GNUNET_SCHEDULER_Task *t;
1290
1291   GNUNET_assert (NULL != active_task);
1292   GNUNET_assert (NULL != task);
1293   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1294   t->callback = task;
1295   t->callback_cls = task_cls;
1296   t->read_fd = -1;
1297   t->write_fd = -1;
1298 #if PROFILE_DELAYS
1299   t->start_time = GNUNET_TIME_absolute_get ();
1300 #endif
1301   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1302   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1303   t->on_shutdown = GNUNET_YES;
1304   t->lifeness = GNUNET_NO;
1305   GNUNET_CONTAINER_DLL_insert (shutdown_head,
1306                                shutdown_tail,
1307                                t);
1308   LOG (GNUNET_ERROR_TYPE_DEBUG,
1309        "Adding shutdown task %p\n",
1310        t);
1311   init_backtrace (t);
1312   return t;
1313 }
1314
1315
1316 /**
1317  * Schedule a new task to be run as soon as possible with the
1318  * (transitive) ignore-shutdown flag either explicitly set or
1319  * explicitly enabled.  This task (and all tasks created from it,
1320  * other than by another call to this function) will either count or
1321  * not count for the "lifeness" of the process.  This API is only
1322  * useful in a few special cases.
1323  *
1324  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1325  * @param task main function of the task
1326  * @param task_cls closure of @a task
1327  * @return unique task identifier for the job
1328  *         only valid until @a task is started!
1329  */
1330 struct GNUNET_SCHEDULER_Task *
1331 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1332                                         GNUNET_SCHEDULER_TaskCallback task,
1333                                         void *task_cls)
1334 {
1335   struct GNUNET_SCHEDULER_Task *ret;
1336
1337   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1338   ret->lifeness = lifeness;
1339   return ret;
1340 }
1341
1342
1343 #if DEBUG_FDS
1344 /**
1345  * check a raw file descriptor and abort if it is bad (for debugging purposes)
1346  *
1347  * @param t the task related to the file descriptor
1348  * @param raw_fd the raw file descriptor to check
1349  */
1350 void
1351 check_fd (struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1352 {
1353   if (-1 != raw_fd)
1354   {
1355     int flags = fcntl (raw_fd, F_GETFD);
1356
1357     if ((flags == -1) && (errno == EBADF))
1358     {
1359       LOG (GNUNET_ERROR_TYPE_ERROR,
1360            "Got invalid file descriptor %d!\n",
1361            raw_fd);
1362       init_backtrace (t);
1363       GNUNET_assert (0);
1364     }
1365   }
1366 }
1367 #endif
1368
1369
1370 /**
1371  * Schedule a new task to be run with a specified delay or when any of
1372  * the specified file descriptor sets is ready.  The delay can be used
1373  * as a timeout on the socket(s) being ready.  The task will be
1374  * scheduled for execution once either the delay has expired or any of
1375  * the socket operations is ready.  This is the most general
1376  * function of the "add" family.  Note that the "prerequisite_task"
1377  * must be satisfied in addition to any of the other conditions.  In
1378  * other words, the task will be started when
1379  * <code>
1380  * (prerequisite-run)
1381  * && (delay-ready
1382  *     || any-rs-ready
1383  *     || any-ws-ready)
1384  * </code>
1385  *
1386  * @param delay how long should we wait?
1387  * @param priority priority to use
1388  * @param rfd file descriptor we want to read (can be -1)
1389  * @param wfd file descriptors we want to write (can be -1)
1390  * @param task main function of the task
1391  * @param task_cls closure of @a task
1392  * @return unique task identifier for the job
1393  *         only valid until @a task is started!
1394  */
1395 #ifndef MINGW
1396 static struct GNUNET_SCHEDULER_Task *
1397 add_without_sets (struct GNUNET_TIME_Relative delay,
1398                   enum GNUNET_SCHEDULER_Priority priority,
1399                   const struct GNUNET_NETWORK_Handle *read_nh,
1400                   const struct GNUNET_NETWORK_Handle *write_nh,
1401                   const struct GNUNET_DISK_FileHandle *read_fh,
1402                   const struct GNUNET_DISK_FileHandle *write_fh,
1403                   GNUNET_SCHEDULER_TaskCallback task,
1404                   void *task_cls)
1405 {
1406   struct GNUNET_SCHEDULER_Task *t;
1407
1408   GNUNET_assert (NULL != active_task);
1409   GNUNET_assert (NULL != task);
1410   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1411   init_fd_info (t,
1412                 &read_nh,
1413                 read_nh ? 1 : 0,
1414                 &write_nh,
1415                 write_nh ? 1 : 0,
1416                 &read_fh,
1417                 read_fh ? 1 : 0,
1418                 &write_fh,
1419                 write_fh ? 1 : 0);
1420   t->callback = task;
1421   t->callback_cls = task_cls;
1422 #if DEBUG_FDS
1423   check_fd (t, NULL != read_nh ? GNUNET_NETWORK_get_fd (read_nh) : -1);
1424   check_fd (t, NULL != write_nh ? GNUNET_NETWORK_get_fd (write_nh) : -1);
1425   check_fd (t, NULL != read_fh ? read_fh->fd : -1);
1426   check_fd (t, NULL != write_fh ? write_fh->fd : -1);
1427 #endif
1428 #if PROFILE_DELAYS
1429   t->start_time = GNUNET_TIME_absolute_get ();
1430 #endif
1431   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1432   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1433   t->lifeness = current_lifeness;
1434   GNUNET_CONTAINER_DLL_insert (pending_head,
1435                                pending_tail,
1436                                t);
1437   driver_add_multiple (t);
1438   max_priority_added = GNUNET_MAX (max_priority_added,
1439                                    t->priority);
1440   init_backtrace (t);
1441   return t;
1442 }
1443 #endif
1444
1445
1446 /**
1447  * Schedule a new task to be run with a specified delay or when the
1448  * specified file descriptor is ready for reading.  The delay can be
1449  * used as a timeout on the socket being ready.  The task will be
1450  * scheduled for execution once either the delay has expired or the
1451  * socket operation is ready.  It will be run with the DEFAULT priority.
1452  * Only allowed to be called as long as the scheduler is running, that
1453  * is one of the following conditions is met:
1454  *
1455  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1456  * - #GNUNET_SCHEDULER_driver_init has been run and
1457  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1458  *
1459  * @param delay when should this operation time out?
1460  * @param rfd read file-descriptor
1461  * @param task main function of the task
1462  * @param task_cls closure of @a task
1463  * @return unique task identifier for the job
1464  *         only valid until @a task is started!
1465  */
1466 struct GNUNET_SCHEDULER_Task *
1467 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1468                                struct GNUNET_NETWORK_Handle *rfd,
1469                                GNUNET_SCHEDULER_TaskCallback task,
1470                                void *task_cls)
1471 {
1472   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1473                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1474                   rfd, task, task_cls);
1475 }
1476
1477
1478 /**
1479  * Schedule a new task to be run with a specified priority and to be
1480  * run after the specified delay or when the specified file descriptor
1481  * is ready for reading.  The delay can be used as a timeout on the
1482  * socket being ready.  The task will be scheduled for execution once
1483  * either the delay has expired or the socket operation is ready.  It
1484  * will be run with the DEFAULT priority.
1485  * Only allowed to be called as long as the scheduler is running, that
1486  * is one of the following conditions is met:
1487  *
1488  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1489  * - #GNUNET_SCHEDULER_driver_init has been run and
1490  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1491  *
1492  * @param delay when should this operation time out?
1493  * @param priority priority to use for the task
1494  * @param rfd read file-descriptor
1495  * @param task main function of the task
1496  * @param task_cls closure of @a task
1497  * @return unique task identifier for the job
1498  *         only valid until @a task is started!
1499  */
1500 struct GNUNET_SCHEDULER_Task *
1501 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1502                enum GNUNET_SCHEDULER_Priority priority,
1503                struct GNUNET_NETWORK_Handle *rfd,
1504                GNUNET_SCHEDULER_TaskCallback task,
1505                                              void *task_cls)
1506 {
1507   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1508                                                  rfd,
1509                                                  GNUNET_YES,
1510                                                  GNUNET_NO,
1511                                                  task, task_cls);
1512 }
1513
1514
1515 /**
1516  * Schedule a new task to be run with a specified delay or when the
1517  * specified file descriptor is ready for writing.  The delay can be
1518  * used as a timeout on the socket being ready.  The task will be
1519  * scheduled for execution once either the delay has expired or the
1520  * socket operation is ready.  It will be run with the priority of
1521  * the calling task.
1522  * Only allowed to be called as long as the scheduler is running, that
1523  * is one of the following conditions is met:
1524  *
1525  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1526  * - #GNUNET_SCHEDULER_driver_init has been run and
1527  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1528  *
1529  * @param delay when should this operation time out?
1530  * @param wfd write file-descriptor
1531  * @param task main function of the task
1532  * @param task_cls closure of @a task
1533  * @return unique task identifier for the job
1534  *         only valid until @a task is started!
1535  */
1536 struct GNUNET_SCHEDULER_Task *
1537 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1538                                 struct GNUNET_NETWORK_Handle *wfd,
1539                                 GNUNET_SCHEDULER_TaskCallback task,
1540                                 void *task_cls)
1541 {
1542   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1543                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1544                                                  wfd,
1545                                                  GNUNET_NO, GNUNET_YES,
1546                                                  task, task_cls);
1547 }
1548
1549 /**
1550  * Schedule a new task to be run with a specified delay or when the
1551  * specified file descriptor is ready.  The delay can be
1552  * used as a timeout on the socket being ready.  The task will be
1553  * scheduled for execution once either the delay has expired or the
1554  * socket operation is ready.
1555  * Only allowed to be called as long as the scheduler is running, that
1556  * is one of the following conditions is met:
1557  *
1558  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1559  * - #GNUNET_SCHEDULER_driver_init has been run and
1560  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1561  *
1562  * @param delay when should this operation time out?
1563  * @param priority priority of the task
1564  * @param fd file-descriptor
1565  * @param on_read whether to poll the file-descriptor for readability
1566  * @param on_write whether to poll the file-descriptor for writability
1567  * @param task main function of the task
1568  * @param task_cls closure of task
1569  * @return unique task identifier for the job
1570  *         only valid until "task" is started!
1571  */
1572 struct GNUNET_SCHEDULER_Task *
1573 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1574                                          enum GNUNET_SCHEDULER_Priority priority,
1575                                          struct GNUNET_NETWORK_Handle *fd,
1576                                          int on_read,
1577                                          int on_write,
1578                                          GNUNET_SCHEDULER_TaskCallback task,
1579                                          void *task_cls)
1580 {
1581   /* scheduler must be running */
1582   GNUNET_assert (NULL != scheduler_driver);
1583
1584 #if MINGW
1585   struct GNUNET_NETWORK_FDSet *s;
1586   struct GNUNET_SCHEDULER_Task * ret;
1587
1588   GNUNET_assert (NULL != fd);
1589   s = GNUNET_NETWORK_fdset_create ();
1590   GNUNET_NETWORK_fdset_set (s, fd);
1591   ret = GNUNET_SCHEDULER_add_select (
1592       priority, delay,
1593       on_read  ? s : NULL,
1594       on_write ? s : NULL,
1595       task, task_cls);
1596   GNUNET_NETWORK_fdset_destroy (s);
1597   return ret;
1598 #else
1599   GNUNET_assert (on_read || on_write);
1600   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1601   return add_without_sets (delay, priority,
1602                            on_read  ? fd : NULL,
1603                            on_write ? fd : NULL,
1604                            NULL,
1605                            NULL,
1606                            task, task_cls);
1607 #endif
1608 }
1609
1610
1611 /**
1612  * Schedule a new task to be run with a specified delay or when the
1613  * specified file descriptor is ready for reading.  The delay can be
1614  * used as a timeout on the socket being ready.  The task will be
1615  * scheduled for execution once either the delay has expired or the
1616  * socket operation is ready. It will be run with the DEFAULT priority.
1617  * Only allowed to be called as long as the scheduler is running, that
1618  * is one of the following conditions is met:
1619  *
1620  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1621  * - #GNUNET_SCHEDULER_driver_init has been run and
1622  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1623  *
1624  * @param delay when should this operation time out?
1625  * @param rfd read file-descriptor
1626  * @param task main function of the task
1627  * @param task_cls closure of @a task
1628  * @return unique task identifier for the job
1629  *         only valid until @a task is started!
1630  */
1631 struct GNUNET_SCHEDULER_Task *
1632 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1633                                 const struct GNUNET_DISK_FileHandle *rfd,
1634                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1635 {
1636   return GNUNET_SCHEDULER_add_file_with_priority (
1637       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1638       rfd, GNUNET_YES, GNUNET_NO,
1639       task, task_cls);
1640 }
1641
1642
1643 /**
1644  * Schedule a new task to be run with a specified delay or when the
1645  * specified file descriptor is ready for writing.  The delay can be
1646  * used as a timeout on the socket being ready.  The task will be
1647  * scheduled for execution once either the delay has expired or the
1648  * socket operation is ready. It will be run with the DEFAULT priority.
1649  * Only allowed to be called as long as the scheduler is running, that
1650  * is one of the following conditions is met:
1651  *
1652  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1653  * - #GNUNET_SCHEDULER_driver_init has been run and
1654  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1655  *
1656  * @param delay when should this operation time out?
1657  * @param wfd write file-descriptor
1658  * @param task main function of the task
1659  * @param task_cls closure of @a task
1660  * @return unique task identifier for the job
1661  *         only valid until @a task is started!
1662  */
1663 struct GNUNET_SCHEDULER_Task *
1664 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1665                                  const struct GNUNET_DISK_FileHandle *wfd,
1666                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1667 {
1668   return GNUNET_SCHEDULER_add_file_with_priority (
1669       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1670       wfd, GNUNET_NO, GNUNET_YES,
1671       task, task_cls);
1672 }
1673
1674
1675 /**
1676  * Schedule a new task to be run with a specified delay or when the
1677  * specified file descriptor is ready.  The delay can be
1678  * used as a timeout on the socket being ready.  The task will be
1679  * scheduled for execution once either the delay has expired or the
1680  * socket operation is ready.
1681  * Only allowed to be called as long as the scheduler is running, that
1682  * is one of the following conditions is met:
1683  *
1684  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1685  * - #GNUNET_SCHEDULER_driver_init has been run and
1686  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1687  *
1688  * @param delay when should this operation time out?
1689  * @param priority priority of the task
1690  * @param fd file-descriptor
1691  * @param on_read whether to poll the file-descriptor for readability
1692  * @param on_write whether to poll the file-descriptor for writability
1693  * @param task main function of the task
1694  * @param task_cls closure of @a task
1695  * @return unique task identifier for the job
1696  *         only valid until @a task is started!
1697  */
1698 struct GNUNET_SCHEDULER_Task *
1699 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1700                                          enum GNUNET_SCHEDULER_Priority priority,
1701                                          const struct GNUNET_DISK_FileHandle *fd,
1702                                          int on_read, int on_write,
1703                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1704 {
1705   /* scheduler must be running */
1706   GNUNET_assert (NULL != scheduler_driver);
1707
1708 #if MINGW
1709   struct GNUNET_NETWORK_FDSet *s;
1710   struct GNUNET_SCHEDULER_Task * ret;
1711
1712   GNUNET_assert (NULL != fd);
1713   s = GNUNET_NETWORK_fdset_create ();
1714   GNUNET_NETWORK_fdset_handle_set (s, fd);
1715   ret = GNUNET_SCHEDULER_add_select (
1716       priority, delay,
1717       on_read  ? s : NULL,
1718       on_write ? s : NULL,
1719       task, task_cls);
1720   GNUNET_NETWORK_fdset_destroy (s);
1721   return ret;
1722 #else
1723   GNUNET_assert (on_read || on_write);
1724   GNUNET_assert (fd->fd >= 0);
1725   return add_without_sets (delay, priority,
1726                            NULL,
1727                            NULL,
1728                            on_read ? fd : NULL,
1729                            on_write ? fd : NULL,
1730                            task, task_cls);
1731 #endif
1732 }
1733
1734
1735 void
1736 extract_handles (const struct GNUNET_NETWORK_FDSet *fdset,
1737                  const struct GNUNET_NETWORK_Handle ***ntarget,
1738                  unsigned int *extracted_nhandles,
1739                  const struct GNUNET_DISK_FileHandle ***ftarget,
1740                  unsigned int *extracted_fhandles)
1741 {
1742   // FIXME: this implementation only works for unix, for WIN32 the file handles
1743   // in fdset must be handled separately
1744   const struct GNUNET_NETWORK_Handle **nhandles;
1745   const struct GNUNET_DISK_FileHandle **fhandles;
1746   unsigned int nhandles_len;
1747   unsigned int fhandles_len;
1748
1749   nhandles = NULL;
1750   fhandles = NULL;
1751   nhandles_len = 0;
1752   fhandles_len = 0;
1753   for (int sock = 0; sock != fdset->nsds; ++sock)
1754   {
1755     if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
1756     {
1757       struct GNUNET_NETWORK_Handle *nhandle;
1758       struct GNUNET_DISK_FileHandle *fhandle;
1759
1760       nhandle = GNUNET_NETWORK_socket_box_native (sock);
1761       if (NULL != nhandle)
1762       {
1763         GNUNET_array_append (nhandles, nhandles_len, nhandle);
1764       }
1765       else
1766       {
1767         fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
1768         if (NULL != fhandle)
1769         {
1770           GNUNET_array_append (fhandles, fhandles_len, fhandle);
1771         }
1772         else
1773         {
1774           GNUNET_assert (0);
1775         }
1776       }
1777     }
1778   }
1779   *ntarget = nhandles_len > 0 ? nhandles : NULL;
1780   *ftarget = fhandles_len > 0 ? fhandles : NULL;
1781   *extracted_nhandles = nhandles_len;
1782   *extracted_fhandles = fhandles_len;
1783 }
1784
1785
1786 /**
1787  * Schedule a new task to be run with a specified delay or when any of
1788  * the specified file descriptor sets is ready.  The delay can be used
1789  * as a timeout on the socket(s) being ready.  The task will be
1790  * scheduled for execution once either the delay has expired or any of
1791  * the socket operations is ready.  This is the most general
1792  * function of the "add" family.  Note that the "prerequisite_task"
1793  * must be satisfied in addition to any of the other conditions.  In
1794  * other words, the task will be started when
1795  * <code>
1796  * (prerequisite-run)
1797  * && (delay-ready
1798  *     || any-rs-ready
1799  *     || any-ws-ready) )
1800  * </code>
1801  * Only allowed to be called as long as the scheduler is running, that
1802  * is one of the following conditions is met:
1803  *
1804  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1805  * - #GNUNET_SCHEDULER_driver_init has been run and
1806  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1807  *
1808  * @param prio how important is this task?
1809  * @param delay how long should we wait?
1810  * @param rs set of file descriptors we want to read (can be NULL)
1811  * @param ws set of file descriptors we want to write (can be NULL)
1812  * @param task main function of the task
1813  * @param task_cls closure of @a task
1814  * @return unique task identifier for the job
1815  *         only valid until @a task is started!
1816  */
1817 struct GNUNET_SCHEDULER_Task *
1818 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1819                              struct GNUNET_TIME_Relative delay,
1820                              const struct GNUNET_NETWORK_FDSet *rs,
1821                              const struct GNUNET_NETWORK_FDSet *ws,
1822                              GNUNET_SCHEDULER_TaskCallback task,
1823                              void *task_cls)
1824 {
1825   struct GNUNET_SCHEDULER_Task *t;
1826   const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
1827   const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
1828   const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
1829   const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
1830   unsigned int read_nhandles_len = 0;
1831   unsigned int write_nhandles_len = 0;
1832   unsigned int read_fhandles_len = 0;
1833   unsigned int write_fhandles_len = 0;
1834
1835   /* scheduler must be running */
1836   GNUNET_assert (NULL != scheduler_driver);
1837   GNUNET_assert (NULL != active_task);
1838   GNUNET_assert (NULL != task);
1839   int no_rs = (NULL == rs);
1840   int no_ws = (NULL == ws);
1841   int empty_rs = (NULL != rs) && (0 == rs->nsds);
1842   int empty_ws = (NULL != ws) && (0 == ws->nsds);
1843   int no_fds = (no_rs && no_ws) ||
1844                (empty_rs && empty_ws) ||
1845                (no_rs && empty_ws) ||
1846                (no_ws && empty_rs);
1847   if (! no_fds)
1848   {
1849     if (NULL != rs)
1850     {
1851       extract_handles (rs,
1852                        &read_nhandles,
1853                        &read_nhandles_len,
1854                        &read_fhandles,
1855                        &read_fhandles_len);
1856     }
1857     if (NULL != ws)
1858     {
1859       extract_handles (ws,
1860                        &write_nhandles,
1861                        &write_nhandles_len,
1862                        &write_fhandles,
1863                        &write_fhandles_len);
1864     }
1865   }
1866   /**
1867    * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
1868    * although its maximum FD number (nsds) is greater than 0. We handle
1869    * this case gracefully because some libraries such as libmicrohttpd
1870    * only provide a hint what the maximum FD number in an FD set might be
1871    * and not the exact FD number (see e.g. gnunet-rest-service.c)
1872    */
1873   int no_fds_extracted = (0 == read_nhandles_len) &&
1874                          (0 == read_fhandles_len) &&
1875                          (0 == write_nhandles_len) &&
1876                          (0 == write_fhandles_len);
1877   if (no_fds || no_fds_extracted)
1878     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1879                                                        prio,
1880                                                        task,
1881                                                        task_cls);
1882   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1883   init_fd_info (t,
1884                 read_nhandles,
1885                 read_nhandles_len,
1886                 write_nhandles,
1887                 write_nhandles_len,
1888                 read_fhandles,
1889                 read_fhandles_len,
1890                 write_fhandles,
1891                 write_fhandles_len);
1892   t->callback = task;
1893   t->callback_cls = task_cls;
1894   t->own_handles = GNUNET_YES;
1895   /* free the arrays of pointers to network / file handles, the actual
1896    * handles will be freed in destroy_task */
1897   GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
1898   GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
1899   GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
1900   GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
1901 #if PROFILE_DELAYS
1902   t->start_time = GNUNET_TIME_absolute_get ();
1903 #endif
1904   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1905   t->priority =
1906       check_priority ((prio ==
1907                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1908                       prio);
1909   t->lifeness = current_lifeness;
1910   GNUNET_CONTAINER_DLL_insert (pending_head,
1911                                pending_tail,
1912                                t);
1913   driver_add_multiple (t);
1914   max_priority_added = GNUNET_MAX (max_priority_added,
1915            t->priority);
1916   LOG (GNUNET_ERROR_TYPE_DEBUG,
1917        "Adding task %p\n",
1918        t);
1919   init_backtrace (t);
1920   return t;
1921 }
1922
1923
1924 /**
1925  * Function used by event-loop implementations to signal the scheduler
1926  * that a particular @a task is ready due to an event specified in the
1927  * et field of @a fdi.
1928  *
1929  * This function will then queue the task to notify the application
1930  * that the task is ready (with the respective priority).
1931  *
1932  * @param task the task that is ready
1933  * @param fdi information about the related FD
1934  */
1935 void
1936 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1937                              struct GNUNET_SCHEDULER_FdInfo *fdi)
1938 {
1939   enum GNUNET_SCHEDULER_Reason reason;
1940
1941   reason = task->reason;
1942   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1943        (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)) )
1944     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1945   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1946        (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)) )
1947     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1948   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1949   task->reason = reason;
1950   if (GNUNET_NO == task->in_ready_list)
1951   {
1952     GNUNET_CONTAINER_DLL_remove (pending_head,
1953                                  pending_tail,
1954                                  task);
1955     queue_ready_task (task);
1956   }
1957 }
1958
1959
1960 /**
1961  * Function called by external event loop implementations to tell the
1962  * scheduler to run some of the tasks that are ready. Must be called
1963  * only after #GNUNET_SCHEDULER_driver_init has been called and before
1964  * #GNUNET_SCHEDULER_driver_done is called.
1965  * This function may return even though there are tasks left to run
1966  * just to give other tasks a chance as well.  If we return #GNUNET_YES,
1967  * the event loop implementation should call this function again as
1968  * soon as possible, while if we return #GNUNET_NO it must block until
1969  * either the operating system has more work (the scheduler has no more
1970  * work to do right now) or the timeout set by the scheduler (using the
1971  * set_wakeup callback) is reached.
1972  *
1973  * @param sh scheduler handle that was returned by
1974  *        #GNUNET_SCHEDULER_driver_init
1975  * @return #GNUNET_YES if there are more tasks that are ready,
1976  *         and thus we would like to run more (yield to avoid
1977  *         blocking other activities for too long) #GNUNET_NO
1978  *         if we are done running tasks (yield to block)
1979  */
1980 int
1981 GNUNET_SCHEDULER_do_work (struct GNUNET_SCHEDULER_Handle *sh)
1982 {
1983   enum GNUNET_SCHEDULER_Priority p;
1984   struct GNUNET_SCHEDULER_Task *pos;
1985   struct GNUNET_TIME_Absolute now;
1986
1987   /* check for tasks that reached the timeout! */
1988   now = GNUNET_TIME_absolute_get ();
1989   pos = pending_timeout_head;
1990   while (NULL != pos)
1991   {
1992     struct GNUNET_SCHEDULER_Task *next = pos->next;
1993     if (now.abs_value_us >= pos->timeout.abs_value_us)
1994       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1995     if (0 == pos->reason)
1996       break;
1997     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1998                                  pending_timeout_tail,
1999                                  pos);
2000     if (pending_timeout_last == pos)
2001       pending_timeout_last = NULL;
2002     queue_ready_task (pos);
2003     pos = next;
2004   }
2005   pos = pending_head;
2006   while (NULL != pos)
2007   {
2008     struct GNUNET_SCHEDULER_Task *next = pos->next;
2009     if (now.abs_value_us >= pos->timeout.abs_value_us)
2010     {
2011       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2012       GNUNET_CONTAINER_DLL_remove (pending_head,
2013                                    pending_tail,
2014                                    pos);
2015       queue_ready_task (pos);
2016     }
2017     pos = next;
2018   }
2019
2020   if (0 == ready_count)
2021   {
2022     struct GNUNET_TIME_Absolute timeout = get_timeout ();
2023
2024     if (timeout.abs_value_us > now.abs_value_us)
2025     {
2026       /**
2027        * The driver called this function before the current timeout was
2028        * reached (and no FD tasks are ready). This can happen in the
2029        * rare case when the system time is changed while the driver is
2030        * waiting for the timeout, so we handle this gracefully. It might
2031        * also be a programming error in the driver though.
2032        */
2033       LOG (GNUNET_ERROR_TYPE_DEBUG,
2034            "GNUNET_SCHEDULER_do_work did not find any ready "
2035            "tasks and timeout has not been reached yet.\n");
2036       return GNUNET_NO;
2037     }
2038     /**
2039      * the current timeout was reached but no ready tasks were found,
2040      * internal scheduler error!
2041      */
2042     GNUNET_assert (0);
2043   }
2044
2045   /* find out which task priority level we are going to
2046      process this time */
2047   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
2048   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
2049   /* yes, p>0 is correct, 0 is "KEEP" which should
2050    * always be an empty queue (see assertion)! */
2051   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
2052   {
2053     pos = ready_head[p];
2054     if (NULL != pos)
2055       break;
2056   }
2057   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
2058
2059   /* process all tasks at this priority level, then yield */
2060   while (NULL != (pos = ready_head[p]))
2061   {
2062     GNUNET_CONTAINER_DLL_remove (ready_head[p],
2063          ready_tail[p],
2064          pos);
2065     ready_count--;
2066     current_priority = pos->priority;
2067     current_lifeness = pos->lifeness;
2068     active_task = pos;
2069 #if PROFILE_DELAYS
2070     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
2071         DELAY_THRESHOLD.rel_value_us)
2072     {
2073       LOG (GNUNET_ERROR_TYPE_DEBUG,
2074            "Task %p took %s to be scheduled\n",
2075            pos,
2076            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
2077                                                    GNUNET_YES));
2078     }
2079 #endif
2080     tc.reason = pos->reason;
2081     GNUNET_NETWORK_fdset_zero (sh->rs);
2082     GNUNET_NETWORK_fdset_zero (sh->ws);
2083     // FIXME: do we have to remove FdInfos from fds if they are not ready?
2084     tc.fds_len = pos->fds_len;
2085     tc.fds = pos->fds;
2086     for (unsigned int i = 0; i != pos->fds_len; ++i)
2087     {
2088       struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
2089       if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
2090       {
2091         GNUNET_NETWORK_fdset_set_native (sh->rs,
2092                                          fdi->sock);
2093       }
2094       if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
2095       {
2096         GNUNET_NETWORK_fdset_set_native (sh->ws,
2097                                          fdi->sock);
2098       }
2099     }
2100     tc.read_ready = sh->rs;
2101     tc.write_ready = sh->ws;
2102     LOG (GNUNET_ERROR_TYPE_DEBUG,
2103          "Running task %p\n",
2104          pos);
2105     GNUNET_assert (NULL != pos->callback);
2106     pos->callback (pos->callback_cls);
2107     if (NULL != pos->fds)
2108     {
2109       int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
2110       if (GNUNET_OK != del_result)
2111       {
2112         LOG (GNUNET_ERROR_TYPE_ERROR,
2113            "driver could not delete task %p\n", pos);
2114         GNUNET_assert (0);
2115       }
2116     }
2117     active_task = NULL;
2118     dump_backtrace (pos);
2119     destroy_task (pos);
2120   }
2121   shutdown_if_no_lifeness ();
2122   if (0 == ready_count)
2123   {
2124     scheduler_driver->set_wakeup (scheduler_driver->cls,
2125                                   get_timeout ());
2126     return GNUNET_NO;
2127   }
2128   scheduler_driver->set_wakeup (scheduler_driver->cls,
2129                                 GNUNET_TIME_absolute_get ());
2130   return GNUNET_YES;
2131 }
2132
2133
2134 /**
2135  * Function called by external event loop implementations to initialize
2136  * the scheduler. An external implementation has to provide @a driver
2137  * which contains callbacks for the scheduler (see definition of struct
2138  * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
2139  * external implementation to watch for events. If it detects any of
2140  * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
2141  * the scheduler handle it. If an event is related to a specific task
2142  * (e.g. the scheduler gave instructions to watch a file descriptor),
2143  * the external implementation is expected to mark that task ready
2144  * before by calling #GNUNET_SCHEDULER_task_ready.
2145
2146  * This function has to be called before any tasks are scheduled and
2147  * before GNUNET_SCHEDULER_do_work is called for the first time. It
2148  * allocates resources that have to be freed again by calling
2149  * #GNUNET_SCHEDULER_driver_done.
2150  *
2151  * This function installs the same signal handlers as
2152  * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
2153  * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
2154  * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
2155  * active tasks to be scheduled with reason
2156  * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
2157  * will execute normally!). Note that any particular signal will only
2158  * shut down one scheduler; applications should always only create a
2159  * single scheduler.
2160  *
2161  * @param driver to use for the event loop
2162  * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
2163  *         #GNUNET_SCHEDULER_driver_done
2164  */
2165 struct GNUNET_SCHEDULER_Handle *
2166 GNUNET_SCHEDULER_driver_init (const struct GNUNET_SCHEDULER_Driver *driver)
2167 {
2168   struct GNUNET_SCHEDULER_Handle *sh;
2169   struct GNUNET_SCHEDULER_Task tsk;
2170   const struct GNUNET_DISK_FileHandle *pr;
2171
2172   /* general set-up */
2173   GNUNET_assert (NULL == active_task);
2174   GNUNET_assert (NULL == shutdown_pipe_handle);
2175   sh = GNUNET_new (struct GNUNET_SCHEDULER_Handle);
2176   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
2177                                            GNUNET_NO,
2178                                            GNUNET_NO,
2179                                            GNUNET_NO);
2180   GNUNET_assert (NULL != shutdown_pipe_handle);
2181   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2182                                 GNUNET_DISK_PIPE_END_READ);
2183   my_pid = getpid ();
2184   scheduler_driver = driver;
2185
2186   /* install signal handlers */
2187   LOG (GNUNET_ERROR_TYPE_DEBUG,
2188        "Registering signal handlers\n");
2189   sh->shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2190                                                &sighandler_shutdown);
2191   sh->shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2192                                                 &sighandler_shutdown);
2193 #if (SIGTERM != GNUNET_TERM_SIG)
2194   sh->shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2195                                                  &sighandler_shutdown);
2196 #endif
2197 #ifndef MINGW
2198   sh->shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2199                                                 &sighandler_pipe);
2200   sh->shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2201                                                 &sighandler_shutdown);
2202   sh->shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2203                                                &sighandler_shutdown);
2204 #endif
2205
2206   /* Setup initial tasks */
2207   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2208   current_lifeness = GNUNET_NO;
2209   memset (&tsk,
2210           0,
2211           sizeof (tsk));
2212   active_task = &tsk;
2213   install_parent_control_task =
2214     GNUNET_SCHEDULER_add_now (&install_parent_control_handler,
2215                               NULL);
2216   shutdown_pipe_task =
2217     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2218                                     pr,
2219                                     &shutdown_pipe_cb,
2220                                     NULL);
2221   current_lifeness = GNUNET_YES;
2222   active_task = NULL;
2223   scheduler_driver->set_wakeup (scheduler_driver->cls,
2224                                 get_timeout ());
2225   /* begin main event loop */
2226   sh->rs = GNUNET_NETWORK_fdset_create ();
2227   sh->ws = GNUNET_NETWORK_fdset_create ();
2228   GNUNET_NETWORK_fdset_handle_set (sh->rs, pr);
2229   return sh;
2230 }
2231
2232
2233 /**
2234  * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
2235  * by external event loop implementations after the scheduler has
2236  * shut down. This is the case if both of the following conditions
2237  * are met:
2238  *
2239  * - all tasks the scheduler has added through the driver's add
2240  *   callback have been removed again through the driver's del
2241  *   callback
2242  * - the timeout the scheduler has set through the driver's
2243  *   add_wakeup callback is FOREVER
2244  *
2245  * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
2246  */
2247 void
2248 GNUNET_SCHEDULER_driver_done (struct GNUNET_SCHEDULER_Handle *sh)
2249 {
2250   GNUNET_assert (NULL == pending_head);
2251   GNUNET_assert (NULL == pending_timeout_head);
2252   GNUNET_assert (NULL == shutdown_head);
2253   for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
2254   {
2255     GNUNET_assert (NULL == ready_head[i]);
2256   }
2257   GNUNET_NETWORK_fdset_destroy (sh->rs);
2258   GNUNET_NETWORK_fdset_destroy (sh->ws);
2259
2260   /* uninstall signal handlers */
2261   GNUNET_SIGNAL_handler_uninstall (sh->shc_int);
2262   GNUNET_SIGNAL_handler_uninstall (sh->shc_term);
2263 #if (SIGTERM != GNUNET_TERM_SIG)
2264   GNUNET_SIGNAL_handler_uninstall (sh->shc_gterm);
2265 #endif
2266 #ifndef MINGW
2267   GNUNET_SIGNAL_handler_uninstall (sh->shc_pipe);
2268   GNUNET_SIGNAL_handler_uninstall (sh->shc_quit);
2269   GNUNET_SIGNAL_handler_uninstall (sh->shc_hup);
2270 #endif
2271   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2272   shutdown_pipe_handle = NULL;
2273   scheduler_driver = NULL;
2274   GNUNET_free (sh);
2275 }
2276
2277
2278 static int
2279 select_loop (struct GNUNET_SCHEDULER_Handle *sh,
2280              struct DriverContext *context)
2281 {
2282   struct GNUNET_NETWORK_FDSet *rs;
2283   struct GNUNET_NETWORK_FDSet *ws;
2284   int select_result;
2285
2286   GNUNET_assert (NULL != context);
2287   rs = GNUNET_NETWORK_fdset_create ();
2288   ws = GNUNET_NETWORK_fdset_create ();
2289   while ( (NULL != context->scheduled_head) ||
2290           (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != context->timeout.abs_value_us) )
2291   {
2292     LOG (GNUNET_ERROR_TYPE_DEBUG,
2293          "select timeout = %s\n",
2294          GNUNET_STRINGS_absolute_time_to_string (context->timeout));
2295
2296     GNUNET_NETWORK_fdset_zero (rs);
2297     GNUNET_NETWORK_fdset_zero (ws);
2298
2299     for (struct Scheduled *pos = context->scheduled_head;
2300          NULL != pos;
2301          pos = pos->next)
2302     {
2303       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2304       {
2305         GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2306       }
2307       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2308       {
2309         GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2310       }
2311     }
2312     struct GNUNET_TIME_Relative time_remaining =
2313       GNUNET_TIME_absolute_get_remaining (context->timeout);
2314     if (NULL == scheduler_select)
2315     {
2316       select_result = GNUNET_NETWORK_socket_select (rs,
2317                                                     ws,
2318                                                     NULL,
2319                                                     time_remaining);
2320     }
2321     else
2322     {
2323       select_result = scheduler_select (scheduler_select_cls,
2324                                         rs,
2325                                         ws,
2326                                         NULL,
2327                                         time_remaining);
2328     }
2329     if (select_result == GNUNET_SYSERR)
2330     {
2331       if (errno == EINTR)
2332         continue;
2333
2334       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR,
2335                     "select");
2336 #ifndef MINGW
2337 #if USE_LSOF
2338       char lsof[512];
2339
2340       snprintf (lsof,
2341                 sizeof (lsof),
2342                 "lsof -p %d",
2343                 getpid ());
2344       (void) close (1);
2345       (void) dup2 (2, 1);
2346       if (0 != system (lsof))
2347         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2348                       "system");
2349 #endif
2350 #endif
2351 #if DEBUG_FDS
2352       for (struct Scheduled *s = context->scheduled_head;
2353            NULL != s;
2354            s = s->next)
2355       {
2356         int flags = fcntl (s->fdi->sock,
2357                            F_GETFD);
2358
2359         if ( (flags == -1) &&
2360              (EBADF == errno) )
2361         {
2362           LOG (GNUNET_ERROR_TYPE_ERROR,
2363                "Got invalid file descriptor %d!\n",
2364                s->fdi->sock);
2365 #if EXECINFO
2366           dump_backtrace (s->task);
2367 #endif
2368         }
2369       }
2370 #endif
2371       GNUNET_assert (0);
2372       GNUNET_NETWORK_fdset_destroy (rs);
2373       GNUNET_NETWORK_fdset_destroy (ws);
2374       return GNUNET_SYSERR;
2375     }
2376     if (select_result > 0)
2377     {
2378       for (struct Scheduled *pos = context->scheduled_head;
2379            NULL != pos;
2380            pos = pos->next)
2381       {
2382         int is_ready = GNUNET_NO;
2383
2384         if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2385             GNUNET_YES ==
2386             GNUNET_NETWORK_fdset_test_native (rs,
2387                                               pos->fdi->sock))
2388         {
2389           pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2390           is_ready = GNUNET_YES;
2391         }
2392         if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2393             GNUNET_YES ==
2394             GNUNET_NETWORK_fdset_test_native (ws,
2395                                               pos->fdi->sock))
2396         {
2397           pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2398           is_ready = GNUNET_YES;
2399         }
2400         if (GNUNET_YES == is_ready)
2401         {
2402           GNUNET_SCHEDULER_task_ready (pos->task,
2403                                        pos->fdi);
2404         }
2405       }
2406     }
2407     if (GNUNET_YES == GNUNET_SCHEDULER_do_work (sh))
2408     {
2409       LOG (GNUNET_ERROR_TYPE_DEBUG,
2410            "scheduler has more tasks ready!\n");
2411     }
2412   }
2413   GNUNET_NETWORK_fdset_destroy (rs);
2414   GNUNET_NETWORK_fdset_destroy (ws);
2415   return GNUNET_OK;
2416 }
2417
2418
2419 static int
2420 select_add (void *cls,
2421             struct GNUNET_SCHEDULER_Task *task,
2422             struct GNUNET_SCHEDULER_FdInfo *fdi)
2423 {
2424   struct DriverContext *context = cls;
2425   GNUNET_assert (NULL != context);
2426   GNUNET_assert (NULL != task);
2427   GNUNET_assert (NULL != fdi);
2428   GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2429                  0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2430
2431   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2432   {
2433     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2434     return GNUNET_SYSERR;
2435   }
2436
2437   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2438   scheduled->task = task;
2439   scheduled->fdi = fdi;
2440   scheduled->et = fdi->et;
2441
2442   GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2443                                context->scheduled_tail,
2444                                scheduled);
2445   return GNUNET_OK;
2446 }
2447
2448
2449 static int
2450 select_del (void *cls,
2451             struct GNUNET_SCHEDULER_Task *task)
2452 {
2453   struct DriverContext *context;
2454   struct Scheduled *pos;
2455   int ret;
2456
2457   GNUNET_assert (NULL != cls);
2458
2459   context = cls;
2460   ret = GNUNET_SYSERR;
2461   pos = context->scheduled_head;
2462   while (NULL != pos)
2463   {
2464     struct Scheduled *next = pos->next;
2465     if (pos->task == task)
2466     {
2467       GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2468                                    context->scheduled_tail,
2469                                    pos);
2470       GNUNET_free (pos);
2471       ret = GNUNET_OK;
2472     }
2473     pos = next;
2474   }
2475   return ret;
2476 }
2477
2478
2479 static void
2480 select_set_wakeup (void *cls,
2481                    struct GNUNET_TIME_Absolute dt)
2482 {
2483   struct DriverContext *context = cls;
2484
2485   GNUNET_assert (NULL != context);
2486   context->timeout = dt;
2487 }
2488
2489
2490 /**
2491  * Obtain the driver for using select() as the event loop.
2492  *
2493  * @return NULL on error
2494  */
2495 struct GNUNET_SCHEDULER_Driver *
2496 GNUNET_SCHEDULER_driver_select ()
2497 {
2498   struct GNUNET_SCHEDULER_Driver *select_driver;
2499   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2500
2501   select_driver->add = &select_add;
2502   select_driver->del = &select_del;
2503   select_driver->set_wakeup = &select_set_wakeup;
2504
2505   return select_driver;
2506 }
2507
2508
2509 /* end of scheduler.c */