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