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