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