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