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