plibc: win32 related, socket
[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    * Passed here to avoid constantly allocating/deallocating
82    * this element, but generally we want to get rid of this.
83    * @deprecated
84    */
85   struct GNUNET_NETWORK_FDSet *rs;
86
87   /**
88    * Passed here to avoid constantly allocating/deallocating
89    * this element, but generally we want to get rid of this.
90    * @deprecated
91    */
92   struct GNUNET_NETWORK_FDSet *ws;
93
94   /**
95    * context of the SIGINT handler
96    */
97   struct GNUNET_SIGNAL_Context *shc_int;
98
99   /**
100    * context of the SIGTERM handler
101    */
102   struct GNUNET_SIGNAL_Context *shc_term;
103
104 #if (SIGTERM != GNUNET_TERM_SIG)
105   /**
106    * context of the TERM_SIG handler
107    */
108   struct GNUNET_SIGNAL_Context *shc_gterm;
109 #endif
110
111 #ifndef MINGW
112   /**
113    * context of the SIGQUIT handler
114    */
115   struct GNUNET_SIGNAL_Context *shc_quit;
116
117   /**
118    * context of the SIGHUP handler
119    */
120   struct GNUNET_SIGNAL_Context *shc_hup;
121
122   /**
123    * context of hte SIGPIPE handler
124    */
125   struct GNUNET_SIGNAL_Context *shc_pipe;
126 #endif
127 };
128
129
130 /**
131  * Entry in list of pending tasks.
132  */
133 struct GNUNET_SCHEDULER_Task {
134   /**
135    * This is a linked list.
136    */
137   struct GNUNET_SCHEDULER_Task *next;
138
139   /**
140    * This is a linked list.
141    */
142   struct GNUNET_SCHEDULER_Task *prev;
143
144   /**
145    * Function to run when ready.
146    */
147   GNUNET_SCHEDULER_TaskCallback callback;
148
149   /**
150    * Closure for the @e callback.
151    */
152   void *callback_cls;
153
154   /**
155    * Information about which FDs are ready for this task (and why).
156    */
157   struct GNUNET_SCHEDULER_FdInfo *fds;
158
159   /**
160    * Storage location used for @e fds if we want to avoid
161    * a separate malloc() call in the common case that this
162    * task is only about a single FD.
163    */
164   struct GNUNET_SCHEDULER_FdInfo fdx;
165
166   /**
167    * Size of the @e fds array.
168    */
169   unsigned int fds_len;
170
171   /**
172    * Do we own the network and file handles referenced by the FdInfo
173    * structs in the fds array. This will only be GNUNET_YES if the
174    * task was created by the #GNUNET_SCHEDULER_add_select function.
175    */
176   int own_handles;
177
178   /**
179    * Absolute timeout value for the task, or
180    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
181    */
182   struct GNUNET_TIME_Absolute timeout;
183
184 #if PROFILE_DELAYS
185   /**
186    * When was the task scheduled?
187    */
188   struct GNUNET_TIME_Absolute start_time;
189 #endif
190
191   /**
192    * Why is the task ready?  Set after task is added to ready queue.
193    * Initially set to zero.  All reasons that have already been
194    * satisfied (i.e.  read or write ready) will be set over time.
195    */
196   enum GNUNET_SCHEDULER_Reason reason;
197
198   /**
199    * Task priority.
200    */
201   enum GNUNET_SCHEDULER_Priority priority;
202
203   /**
204    * Set if we only wait for reading from a single FD, otherwise -1.
205    */
206   int read_fd;
207
208   /**
209    * Set if we only wait for writing to a single FD, otherwise -1.
210    */
211   int write_fd;
212
213   /**
214    * Should the existence of this task in the queue be counted as
215    * reason to not shutdown the scheduler?
216    */
217   int lifeness;
218
219   /**
220    * Is this task run on shutdown?
221    */
222   int on_shutdown;
223
224   /**
225    * Is this task in the ready list?
226    */
227   int in_ready_list;
228
229 #if EXECINFO
230   /**
231    * Array of strings which make up a backtrace from the point when this
232    * task was scheduled (essentially, who scheduled the task?)
233    */
234   char **backtrace_strings;
235
236   /**
237    * Size of the backtrace_strings array
238    */
239   int num_backtrace_strings;
240 #endif
241
242   /**
243    * Asynchronous scope of the task that scheduled this scope,
244    */
245   struct GNUNET_AsyncScopeSave scope;
246 };
247
248
249 /**
250  * A struct representing an event the select driver is waiting for
251  */
252 struct Scheduled {
253   struct Scheduled *prev;
254
255   struct Scheduled *next;
256
257   /**
258    * the task, the event is related to
259    */
260   struct GNUNET_SCHEDULER_Task *task;
261
262   /**
263    * information about the network socket / file descriptor where
264    * the event is expected to occur
265    */
266   struct GNUNET_SCHEDULER_FdInfo *fdi;
267
268   /**
269    * the event types (multiple event types can be ORed) the select
270    * driver is expected to wait for
271    */
272   enum GNUNET_SCHEDULER_EventType et;
273 };
274
275
276 /**
277  * Driver context used by GNUNET_SCHEDULER_run
278  */
279 struct DriverContext {
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   GNUNET_async_scope_get(&t->scope);
1109   t->callback = task;
1110   t->callback_cls = task_cls;
1111   t->read_fd = -1;
1112   t->write_fd = -1;
1113 #if PROFILE_DELAYS
1114   t->start_time = GNUNET_TIME_absolute_get();
1115 #endif
1116   t->timeout = at;
1117   t->priority = check_priority(priority);
1118   t->lifeness = current_lifeness;
1119   /* try tail first (optimization in case we are
1120    * appending to a long list of tasks with timeouts) */
1121   if ((NULL == pending_timeout_head) ||
1122       (at.abs_value_us < pending_timeout_head->timeout.abs_value_us))
1123     {
1124       GNUNET_CONTAINER_DLL_insert(pending_timeout_head,
1125                                   pending_timeout_tail,
1126                                   t);
1127     }
1128   else
1129     {
1130       /* first move from heuristic start backwards to before start time */
1131       prev = pending_timeout_last;
1132       while ((NULL != prev) &&
1133              (prev->timeout.abs_value_us > t->timeout.abs_value_us))
1134         prev = prev->prev;
1135       /* now, move from heuristic start (or head of list) forward to insertion point */
1136       if (NULL == prev)
1137         pos = pending_timeout_head;
1138       else
1139         pos = prev->next;
1140       while ((NULL != pos) && (pos->timeout.abs_value_us <= t->timeout.abs_value_us))
1141         {
1142           prev = pos;
1143           pos = pos->next;
1144         }
1145       GNUNET_CONTAINER_DLL_insert_after(pending_timeout_head,
1146                                         pending_timeout_tail,
1147                                         prev,
1148                                         t);
1149     }
1150   /* finally, update heuristic insertion point to last insertion... */
1151   pending_timeout_last = t;
1152
1153   LOG(GNUNET_ERROR_TYPE_DEBUG,
1154       "Adding task %p\n",
1155       t);
1156   init_backtrace(t);
1157   return t;
1158 }
1159
1160
1161 /**
1162  * Schedule a new task to be run with a specified delay.  The task
1163  * will be scheduled for execution once the delay has expired.
1164  *
1165  * @param delay when should this operation time out?
1166  * @param priority priority to use for the task
1167  * @param task main function of the task
1168  * @param task_cls closure of @a task
1169  * @return unique task identifier for the job
1170  *         only valid until @a task is started!
1171  */
1172 struct GNUNET_SCHEDULER_Task *
1173 GNUNET_SCHEDULER_add_delayed_with_priority(struct GNUNET_TIME_Relative delay,
1174                                            enum GNUNET_SCHEDULER_Priority priority,
1175                                            GNUNET_SCHEDULER_TaskCallback task,
1176                                            void *task_cls)
1177 {
1178   return GNUNET_SCHEDULER_add_at_with_priority(GNUNET_TIME_relative_to_absolute(delay),
1179                                                priority,
1180                                                task,
1181                                                task_cls);
1182 }
1183
1184
1185 /**
1186  * Schedule a new task to be run with a specified priority.
1187  *
1188  * @param prio how important is the new task?
1189  * @param task main function of the task
1190  * @param task_cls closure of @a task
1191  * @return unique task identifier for the job
1192  *         only valid until @a task is started!
1193  */
1194 struct GNUNET_SCHEDULER_Task *
1195 GNUNET_SCHEDULER_add_with_priority(enum GNUNET_SCHEDULER_Priority prio,
1196                                    GNUNET_SCHEDULER_TaskCallback task,
1197                                    void *task_cls)
1198 {
1199   return GNUNET_SCHEDULER_add_delayed_with_priority(GNUNET_TIME_UNIT_ZERO,
1200                                                     prio,
1201                                                     task,
1202                                                     task_cls);
1203 }
1204
1205
1206 /**
1207  * Schedule a new task to be run at the specified time.  The task
1208  * will be scheduled for execution once specified time has been
1209  * reached. It will be run with the DEFAULT priority.
1210  *
1211  * @param at time at which this operation should run
1212  * @param task main function of the task
1213  * @param task_cls closure of @a task
1214  * @return unique task identifier for the job
1215  *         only valid until @a task is started!
1216  */
1217 struct GNUNET_SCHEDULER_Task *
1218 GNUNET_SCHEDULER_add_at(struct GNUNET_TIME_Absolute at,
1219                         GNUNET_SCHEDULER_TaskCallback task,
1220                         void *task_cls)
1221 {
1222   return GNUNET_SCHEDULER_add_at_with_priority(at,
1223                                                GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1224                                                task,
1225                                                task_cls);
1226 }
1227
1228
1229 /**
1230  * Schedule a new task to be run with a specified delay.  The task
1231  * will be scheduled for execution once the delay has expired. It
1232  * will be run with the DEFAULT priority.
1233  *
1234  * @param delay when should this operation time out?
1235  * @param task main function of the task
1236  * @param task_cls closure of @a task
1237  * @return unique task identifier for the job
1238  *         only valid until @a task is started!
1239  */
1240 struct GNUNET_SCHEDULER_Task *
1241 GNUNET_SCHEDULER_add_delayed(struct GNUNET_TIME_Relative delay,
1242                              GNUNET_SCHEDULER_TaskCallback task,
1243                              void *task_cls)
1244 {
1245   return GNUNET_SCHEDULER_add_delayed_with_priority(delay,
1246                                                     GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1247                                                     task,
1248                                                     task_cls);
1249 }
1250
1251
1252 /**
1253  * Schedule a new task to be run as soon as possible.  Note that this
1254  * does not guarantee that this will be the next task that is being
1255  * run, as other tasks with higher priority (or that are already ready
1256  * to run) might get to run first.  Just as with delays, clients must
1257  * not rely on any particular order of execution between tasks
1258  * scheduled concurrently.
1259  *
1260  * The task will be run with the DEFAULT priority.
1261  *
1262  * @param task main function of the task
1263  * @param task_cls closure of @a task
1264  * @return unique task identifier for the job
1265  *         only valid until @a task is started!
1266  */
1267 struct GNUNET_SCHEDULER_Task *
1268 GNUNET_SCHEDULER_add_now(GNUNET_SCHEDULER_TaskCallback task,
1269                          void *task_cls)
1270 {
1271   return GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_ZERO,
1272                                       task,
1273                                       task_cls);
1274 }
1275
1276
1277 /**
1278  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1279  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1280  * invoked.
1281  *
1282  * @param task main function of the task
1283  * @param task_cls closure of @a task
1284  * @return unique task identifier for the job
1285  *         only valid until @a task is started!
1286  */
1287 struct GNUNET_SCHEDULER_Task *
1288 GNUNET_SCHEDULER_add_shutdown(GNUNET_SCHEDULER_TaskCallback task,
1289                               void *task_cls)
1290 {
1291   struct GNUNET_SCHEDULER_Task *t;
1292
1293   /* scheduler must be running */
1294   GNUNET_assert(NULL != scheduler_driver);
1295   GNUNET_assert(NULL != task);
1296   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1297   GNUNET_async_scope_get(&t->scope);
1298   t->callback = task;
1299   t->callback_cls = task_cls;
1300   t->read_fd = -1;
1301   t->write_fd = -1;
1302 #if PROFILE_DELAYS
1303   t->start_time = GNUNET_TIME_absolute_get();
1304 #endif
1305   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1306   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1307   t->on_shutdown = GNUNET_YES;
1308   t->lifeness = GNUNET_NO;
1309   GNUNET_CONTAINER_DLL_insert(shutdown_head,
1310                               shutdown_tail,
1311                               t);
1312   LOG(GNUNET_ERROR_TYPE_DEBUG,
1313       "Adding shutdown task %p\n",
1314       t);
1315   init_backtrace(t);
1316   return t;
1317 }
1318
1319
1320 /**
1321  * Schedule a new task to be run as soon as possible with the
1322  * (transitive) ignore-shutdown flag either explicitly set or
1323  * explicitly enabled.  This task (and all tasks created from it,
1324  * other than by another call to this function) will either count or
1325  * not count for the "lifeness" of the process.  This API is only
1326  * useful in a few special cases.
1327  *
1328  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1329  * @param task main function of the task
1330  * @param task_cls closure of @a task
1331  * @return unique task identifier for the job
1332  *         only valid until @a task is started!
1333  */
1334 struct GNUNET_SCHEDULER_Task *
1335 GNUNET_SCHEDULER_add_now_with_lifeness(int lifeness,
1336                                        GNUNET_SCHEDULER_TaskCallback task,
1337                                        void *task_cls)
1338 {
1339   struct GNUNET_SCHEDULER_Task *ret;
1340
1341   ret = GNUNET_SCHEDULER_add_now(task, task_cls);
1342   ret->lifeness = lifeness;
1343   return ret;
1344 }
1345
1346
1347 #if DEBUG_FDS
1348 /**
1349  * check a raw file descriptor and abort if it is bad (for debugging purposes)
1350  *
1351  * @param t the task related to the file descriptor
1352  * @param raw_fd the raw file descriptor to check
1353  */
1354 void
1355 check_fd(struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1356 {
1357   if (-1 != raw_fd)
1358     {
1359       int flags = fcntl(raw_fd, F_GETFD);
1360
1361       if ((flags == -1) && (errno == EBADF))
1362         {
1363           LOG(GNUNET_ERROR_TYPE_ERROR,
1364               "Got invalid file descriptor %d!\n",
1365               raw_fd);
1366           init_backtrace(t);
1367           GNUNET_assert(0);
1368         }
1369     }
1370 }
1371 #endif
1372
1373
1374 /**
1375  * Schedule a new task to be run with a specified delay or when any of
1376  * the specified file descriptor sets is ready.  The delay can be used
1377  * as a timeout on the socket(s) being ready.  The task will be
1378  * scheduled for execution once either the delay has expired or any of
1379  * the socket operations is ready.  This is the most general
1380  * function of the "add" family.  Note that the "prerequisite_task"
1381  * must be satisfied in addition to any of the other conditions.  In
1382  * other words, the task will be started when
1383  * <code>
1384  * (prerequisite-run)
1385  * && (delay-ready
1386  *     || any-rs-ready
1387  *     || any-ws-ready)
1388  * </code>
1389  *
1390  * @param delay how long should we wait?
1391  * @param priority priority to use
1392  * @param rfd file descriptor we want to read (can be -1)
1393  * @param wfd file descriptors we want to write (can be -1)
1394  * @param task main function of the task
1395  * @param task_cls closure of @a task
1396  * @return unique task identifier for the job
1397  *         only valid until @a task is started!
1398  */
1399 #ifndef MINGW
1400 static struct GNUNET_SCHEDULER_Task *
1401 add_without_sets(struct GNUNET_TIME_Relative delay,
1402                  enum GNUNET_SCHEDULER_Priority priority,
1403                  const struct GNUNET_NETWORK_Handle *read_nh,
1404                  const struct GNUNET_NETWORK_Handle *write_nh,
1405                  const struct GNUNET_DISK_FileHandle *read_fh,
1406                  const struct GNUNET_DISK_FileHandle *write_fh,
1407                  GNUNET_SCHEDULER_TaskCallback task,
1408                  void *task_cls)
1409 {
1410   struct GNUNET_SCHEDULER_Task *t;
1411
1412   /* scheduler must be running */
1413   GNUNET_assert(NULL != scheduler_driver);
1414   GNUNET_assert(NULL != task);
1415   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1416   GNUNET_async_scope_get(&t->scope);
1417   init_fd_info(t,
1418                &read_nh,
1419                read_nh ? 1 : 0,
1420                &write_nh,
1421                write_nh ? 1 : 0,
1422                &read_fh,
1423                read_fh ? 1 : 0,
1424                &write_fh,
1425                write_fh ? 1 : 0);
1426   t->callback = task;
1427   t->callback_cls = task_cls;
1428 #if DEBUG_FDS
1429   check_fd(t, NULL != read_nh ? GNUNET_NETWORK_get_fd(read_nh) : -1);
1430   check_fd(t, NULL != write_nh ? GNUNET_NETWORK_get_fd(write_nh) : -1);
1431   check_fd(t, NULL != read_fh ? read_fh->fd : -1);
1432   check_fd(t, NULL != write_fh ? write_fh->fd : -1);
1433 #endif
1434 #if PROFILE_DELAYS
1435   t->start_time = GNUNET_TIME_absolute_get();
1436 #endif
1437   t->timeout = GNUNET_TIME_relative_to_absolute(delay);
1438   t->priority = check_priority((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1439   t->lifeness = current_lifeness;
1440   GNUNET_CONTAINER_DLL_insert(pending_head,
1441                               pending_tail,
1442                               t);
1443   driver_add_multiple(t);
1444   max_priority_added = GNUNET_MAX(max_priority_added,
1445                                   t->priority);
1446   init_backtrace(t);
1447   return t;
1448 }
1449 #endif
1450
1451
1452 /**
1453  * Schedule a new task to be run with a specified delay or when the
1454  * specified file descriptor is ready for reading.  The delay can be
1455  * used as a timeout on the socket being ready.  The task will be
1456  * scheduled for execution once either the delay has expired or the
1457  * socket operation is ready.  It will be run with the DEFAULT priority.
1458  * Only allowed to be called as long as the scheduler is running, that
1459  * is one of the following conditions is met:
1460  *
1461  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1462  * - #GNUNET_SCHEDULER_driver_init has been run and
1463  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1464  *
1465  * @param delay when should this operation time out?
1466  * @param rfd read file-descriptor
1467  * @param task main function of the task
1468  * @param task_cls closure of @a task
1469  * @return unique task identifier for the job
1470  *         only valid until @a task is started!
1471  */
1472 struct GNUNET_SCHEDULER_Task *
1473 GNUNET_SCHEDULER_add_read_net(struct GNUNET_TIME_Relative delay,
1474                               struct GNUNET_NETWORK_Handle *rfd,
1475                               GNUNET_SCHEDULER_TaskCallback task,
1476                               void *task_cls)
1477 {
1478   return GNUNET_SCHEDULER_add_read_net_with_priority(delay,
1479                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1480                                                      rfd, task, task_cls);
1481 }
1482
1483
1484 /**
1485  * Schedule a new task to be run with a specified priority and to be
1486  * run after the specified delay or when the specified file descriptor
1487  * is ready for reading.  The delay can be used as a timeout on the
1488  * socket being ready.  The task will be scheduled for execution once
1489  * either the delay has expired or the socket operation is ready.  It
1490  * will be run with the DEFAULT priority.
1491  * Only allowed to be called as long as the scheduler is running, that
1492  * is one of the following conditions is met:
1493  *
1494  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1495  * - #GNUNET_SCHEDULER_driver_init has been run and
1496  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1497  *
1498  * @param delay when should this operation time out?
1499  * @param priority priority to use for the task
1500  * @param rfd read file-descriptor
1501  * @param task main function of the task
1502  * @param task_cls closure of @a task
1503  * @return unique task identifier for the job
1504  *         only valid until @a task is started!
1505  */
1506 struct GNUNET_SCHEDULER_Task *
1507 GNUNET_SCHEDULER_add_read_net_with_priority(struct GNUNET_TIME_Relative delay,
1508                                             enum GNUNET_SCHEDULER_Priority priority,
1509                                             struct GNUNET_NETWORK_Handle *rfd,
1510                                             GNUNET_SCHEDULER_TaskCallback task,
1511                                             void *task_cls)
1512 {
1513   return GNUNET_SCHEDULER_add_net_with_priority(delay, priority,
1514                                                 rfd,
1515                                                 GNUNET_YES,
1516                                                 GNUNET_NO,
1517                                                 task, task_cls);
1518 }
1519
1520
1521 /**
1522  * Schedule a new task to be run with a specified delay or when the
1523  * specified file descriptor is ready for writing.  The delay can be
1524  * used as a timeout on the socket being ready.  The task will be
1525  * scheduled for execution once either the delay has expired or the
1526  * socket operation is ready.  It will be run with the priority of
1527  * the calling task.
1528  * Only allowed to be called as long as the scheduler is running, that
1529  * is one of the following conditions is met:
1530  *
1531  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1532  * - #GNUNET_SCHEDULER_driver_init has been run and
1533  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1534  *
1535  * @param delay when should this operation time out?
1536  * @param wfd write file-descriptor
1537  * @param task main function of the task
1538  * @param task_cls closure of @a task
1539  * @return unique task identifier for the job
1540  *         only valid until @a task is started!
1541  */
1542 struct GNUNET_SCHEDULER_Task *
1543 GNUNET_SCHEDULER_add_write_net(struct GNUNET_TIME_Relative delay,
1544                                struct GNUNET_NETWORK_Handle *wfd,
1545                                GNUNET_SCHEDULER_TaskCallback task,
1546                                void *task_cls)
1547 {
1548   return GNUNET_SCHEDULER_add_net_with_priority(delay,
1549                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1550                                                 wfd,
1551                                                 GNUNET_NO, GNUNET_YES,
1552                                                 task, task_cls);
1553 }
1554
1555 /**
1556  * Schedule a new task to be run with a specified delay or when the
1557  * specified file descriptor is ready.  The delay can be
1558  * used as a timeout on the socket being ready.  The task will be
1559  * scheduled for execution once either the delay has expired or the
1560  * socket operation is ready.
1561  * Only allowed to be called as long as the scheduler is running, that
1562  * is one of the following conditions is met:
1563  *
1564  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1565  * - #GNUNET_SCHEDULER_driver_init has been run and
1566  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1567  *
1568  * @param delay when should this operation time out?
1569  * @param priority priority of the task
1570  * @param fd file-descriptor
1571  * @param on_read whether to poll the file-descriptor for readability
1572  * @param on_write whether to poll the file-descriptor for writability
1573  * @param task main function of the task
1574  * @param task_cls closure of task
1575  * @return unique task identifier for the job
1576  *         only valid until "task" is started!
1577  */
1578 struct GNUNET_SCHEDULER_Task *
1579 GNUNET_SCHEDULER_add_net_with_priority(struct GNUNET_TIME_Relative delay,
1580                                        enum GNUNET_SCHEDULER_Priority priority,
1581                                        struct GNUNET_NETWORK_Handle *fd,
1582                                        int on_read,
1583                                        int on_write,
1584                                        GNUNET_SCHEDULER_TaskCallback task,
1585                                        void *task_cls)
1586 {
1587   /* scheduler must be running */
1588   GNUNET_assert(NULL != scheduler_driver);
1589
1590 #if MINGW
1591   struct GNUNET_NETWORK_FDSet *s;
1592   struct GNUNET_SCHEDULER_Task * ret;
1593
1594   GNUNET_assert(NULL != fd);
1595   s = GNUNET_NETWORK_fdset_create();
1596   GNUNET_NETWORK_fdset_set(s, fd);
1597   ret = GNUNET_SCHEDULER_add_select(
1598     priority, delay,
1599     on_read  ? s : NULL,
1600     on_write ? s : NULL,
1601     task, task_cls);
1602   GNUNET_NETWORK_fdset_destroy(s);
1603   return ret;
1604 #else
1605   GNUNET_assert(on_read || on_write);
1606   GNUNET_assert(GNUNET_NETWORK_get_fd(fd) >= 0);
1607   return add_without_sets(delay, priority,
1608                           on_read  ? fd : NULL,
1609                           on_write ? fd : NULL,
1610                           NULL,
1611                           NULL,
1612                           task, task_cls);
1613 #endif
1614 }
1615
1616
1617 /**
1618  * Schedule a new task to be run with a specified delay or when the
1619  * specified file descriptor is ready for reading.  The delay can be
1620  * used as a timeout on the socket being ready.  The task will be
1621  * scheduled for execution once either the delay has expired or the
1622  * socket operation is ready. It will be run with the DEFAULT priority.
1623  * Only allowed to be called as long as the scheduler is running, that
1624  * is one of the following conditions is met:
1625  *
1626  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1627  * - #GNUNET_SCHEDULER_driver_init has been run and
1628  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1629  *
1630  * @param delay when should this operation time out?
1631  * @param rfd read file-descriptor
1632  * @param task main function of the task
1633  * @param task_cls closure of @a task
1634  * @return unique task identifier for the job
1635  *         only valid until @a task is started!
1636  */
1637 struct GNUNET_SCHEDULER_Task *
1638 GNUNET_SCHEDULER_add_read_file(struct GNUNET_TIME_Relative delay,
1639                                const struct GNUNET_DISK_FileHandle *rfd,
1640                                GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1641 {
1642   return GNUNET_SCHEDULER_add_file_with_priority(
1643     delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1644     rfd, GNUNET_YES, GNUNET_NO,
1645     task, task_cls);
1646 }
1647
1648
1649 /**
1650  * Schedule a new task to be run with a specified delay or when the
1651  * specified file descriptor is ready for writing.  The delay can be
1652  * used as a timeout on the socket being ready.  The task will be
1653  * scheduled for execution once either the delay has expired or the
1654  * socket operation is ready. It will be run with the DEFAULT priority.
1655  * Only allowed to be called as long as the scheduler is running, that
1656  * is one of the following conditions is met:
1657  *
1658  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1659  * - #GNUNET_SCHEDULER_driver_init has been run and
1660  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1661  *
1662  * @param delay when should this operation time out?
1663  * @param wfd write file-descriptor
1664  * @param task main function of the task
1665  * @param task_cls closure of @a task
1666  * @return unique task identifier for the job
1667  *         only valid until @a task is started!
1668  */
1669 struct GNUNET_SCHEDULER_Task *
1670 GNUNET_SCHEDULER_add_write_file(struct GNUNET_TIME_Relative delay,
1671                                 const struct GNUNET_DISK_FileHandle *wfd,
1672                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1673 {
1674   return GNUNET_SCHEDULER_add_file_with_priority(
1675     delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1676     wfd, GNUNET_NO, GNUNET_YES,
1677     task, task_cls);
1678 }
1679
1680
1681 /**
1682  * Schedule a new task to be run with a specified delay or when the
1683  * specified file descriptor is ready.  The delay can be
1684  * used as a timeout on the socket being ready.  The task will be
1685  * scheduled for execution once either the delay has expired or the
1686  * socket operation is ready.
1687  * Only allowed to be called as long as the scheduler is running, that
1688  * is one of the following conditions is met:
1689  *
1690  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1691  * - #GNUNET_SCHEDULER_driver_init has been run and
1692  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1693  *
1694  * @param delay when should this operation time out?
1695  * @param priority priority of the task
1696  * @param fd file-descriptor
1697  * @param on_read whether to poll the file-descriptor for readability
1698  * @param on_write whether to poll the file-descriptor for writability
1699  * @param task main function of the task
1700  * @param task_cls closure of @a task
1701  * @return unique task identifier for the job
1702  *         only valid until @a task is started!
1703  */
1704 struct GNUNET_SCHEDULER_Task *
1705 GNUNET_SCHEDULER_add_file_with_priority(struct GNUNET_TIME_Relative delay,
1706                                         enum GNUNET_SCHEDULER_Priority priority,
1707                                         const struct GNUNET_DISK_FileHandle *fd,
1708                                         int on_read, int on_write,
1709                                         GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1710 {
1711   /* scheduler must be running */
1712   GNUNET_assert(NULL != scheduler_driver);
1713
1714 #if MINGW
1715   struct GNUNET_NETWORK_FDSet *s;
1716   struct GNUNET_SCHEDULER_Task * ret;
1717
1718   GNUNET_assert(NULL != fd);
1719   s = GNUNET_NETWORK_fdset_create();
1720   GNUNET_NETWORK_fdset_handle_set(s, fd);
1721   ret = GNUNET_SCHEDULER_add_select(
1722     priority, delay,
1723     on_read  ? s : NULL,
1724     on_write ? s : NULL,
1725     task, task_cls);
1726   GNUNET_NETWORK_fdset_destroy(s);
1727   return ret;
1728 #else
1729   GNUNET_assert(on_read || on_write);
1730   GNUNET_assert(fd->fd >= 0);
1731   return add_without_sets(delay, priority,
1732                           NULL,
1733                           NULL,
1734                           on_read ? fd : NULL,
1735                           on_write ? fd : NULL,
1736                           task, task_cls);
1737 #endif
1738 }
1739
1740
1741 void
1742 extract_handles(const struct GNUNET_NETWORK_FDSet *fdset,
1743                 const struct GNUNET_NETWORK_Handle ***ntarget,
1744                 unsigned int *extracted_nhandles,
1745                 const struct GNUNET_DISK_FileHandle ***ftarget,
1746                 unsigned int *extracted_fhandles)
1747 {
1748   // FIXME: this implementation only works for unix, for WIN32 the file handles
1749   // in fdset must be handled separately
1750   const struct GNUNET_NETWORK_Handle **nhandles;
1751   const struct GNUNET_DISK_FileHandle **fhandles;
1752   unsigned int nhandles_len;
1753   unsigned int fhandles_len;
1754
1755   nhandles = NULL;
1756   fhandles = NULL;
1757   nhandles_len = 0;
1758   fhandles_len = 0;
1759   for (int sock = 0; sock != fdset->nsds; ++sock)
1760     {
1761       if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native(fdset, sock))
1762         {
1763           struct GNUNET_NETWORK_Handle *nhandle;
1764           struct GNUNET_DISK_FileHandle *fhandle;
1765
1766           nhandle = GNUNET_NETWORK_socket_box_native(sock);
1767           if (NULL != nhandle)
1768             {
1769               GNUNET_array_append(nhandles, nhandles_len, nhandle);
1770             }
1771           else
1772             {
1773               fhandle = GNUNET_DISK_get_handle_from_int_fd(sock);
1774               if (NULL != fhandle)
1775                 {
1776                   GNUNET_array_append(fhandles, fhandles_len, fhandle);
1777                 }
1778               else
1779                 {
1780                   GNUNET_assert(0);
1781                 }
1782             }
1783         }
1784     }
1785   *ntarget = nhandles_len > 0 ? nhandles : NULL;
1786   *ftarget = fhandles_len > 0 ? fhandles : NULL;
1787   *extracted_nhandles = nhandles_len;
1788   *extracted_fhandles = fhandles_len;
1789 }
1790
1791
1792 /**
1793  * Schedule a new task to be run with a specified delay or when any of
1794  * the specified file descriptor sets is ready.  The delay can be used
1795  * as a timeout on the socket(s) being ready.  The task will be
1796  * scheduled for execution once either the delay has expired or any of
1797  * the socket operations is ready.  This is the most general
1798  * function of the "add" family.  Note that the "prerequisite_task"
1799  * must be satisfied in addition to any of the other conditions.  In
1800  * other words, the task will be started when
1801  * <code>
1802  * (prerequisite-run)
1803  * && (delay-ready
1804  *     || any-rs-ready
1805  *     || any-ws-ready) )
1806  * </code>
1807  * Only allowed to be called as long as the scheduler is running, that
1808  * is one of the following conditions is met:
1809  *
1810  * - #GNUNET_SCHEDULER_run has been called and has not returned yet
1811  * - #GNUNET_SCHEDULER_driver_init has been run and
1812  *   #GNUNET_SCHEDULER_driver_done has not been called yet
1813  *
1814  * @param prio how important is this task?
1815  * @param delay how long should we wait?
1816  * @param rs set of file descriptors we want to read (can be NULL)
1817  * @param ws set of file descriptors we want to write (can be NULL)
1818  * @param task main function of the task
1819  * @param task_cls closure of @a task
1820  * @return unique task identifier for the job
1821  *         only valid until @a task is started!
1822  */
1823 struct GNUNET_SCHEDULER_Task *
1824 GNUNET_SCHEDULER_add_select(enum GNUNET_SCHEDULER_Priority prio,
1825                             struct GNUNET_TIME_Relative delay,
1826                             const struct GNUNET_NETWORK_FDSet *rs,
1827                             const struct GNUNET_NETWORK_FDSet *ws,
1828                             GNUNET_SCHEDULER_TaskCallback task,
1829                             void *task_cls)
1830 {
1831   struct GNUNET_SCHEDULER_Task *t;
1832   const struct GNUNET_NETWORK_Handle **read_nhandles = NULL;
1833   const struct GNUNET_NETWORK_Handle **write_nhandles = NULL;
1834   const struct GNUNET_DISK_FileHandle **read_fhandles = NULL;
1835   const struct GNUNET_DISK_FileHandle **write_fhandles = NULL;
1836   unsigned int read_nhandles_len = 0;
1837   unsigned int write_nhandles_len = 0;
1838   unsigned int read_fhandles_len = 0;
1839   unsigned int write_fhandles_len = 0;
1840
1841   /* scheduler must be running */
1842   GNUNET_assert(NULL != scheduler_driver);
1843   GNUNET_assert(NULL != task);
1844   int no_rs = (NULL == rs);
1845   int no_ws = (NULL == ws);
1846   int empty_rs = (NULL != rs) && (0 == rs->nsds);
1847   int empty_ws = (NULL != ws) && (0 == ws->nsds);
1848   int no_fds = (no_rs && no_ws) ||
1849                (empty_rs && empty_ws) ||
1850                (no_rs && empty_ws) ||
1851                (no_ws && empty_rs);
1852   if (!no_fds)
1853     {
1854       if (NULL != rs)
1855         {
1856           extract_handles(rs,
1857                           &read_nhandles,
1858                           &read_nhandles_len,
1859                           &read_fhandles,
1860                           &read_fhandles_len);
1861         }
1862       if (NULL != ws)
1863         {
1864           extract_handles(ws,
1865                           &write_nhandles,
1866                           &write_nhandles_len,
1867                           &write_fhandles,
1868                           &write_fhandles_len);
1869         }
1870     }
1871   /**
1872    * here we consider the case that a GNUNET_NETWORK_FDSet might be empty
1873    * although its maximum FD number (nsds) is greater than 0. We handle
1874    * this case gracefully because some libraries such as libmicrohttpd
1875    * only provide a hint what the maximum FD number in an FD set might be
1876    * and not the exact FD number (see e.g. gnunet-rest-service.c)
1877    */
1878   int no_fds_extracted = (0 == read_nhandles_len) &&
1879                          (0 == read_fhandles_len) &&
1880                          (0 == write_nhandles_len) &&
1881                          (0 == write_fhandles_len);
1882   if (no_fds || no_fds_extracted)
1883     return GNUNET_SCHEDULER_add_delayed_with_priority(delay,
1884                                                       prio,
1885                                                       task,
1886                                                       task_cls);
1887   t = GNUNET_new(struct GNUNET_SCHEDULER_Task);
1888   GNUNET_async_scope_get(&t->scope);
1889   init_fd_info(t,
1890                read_nhandles,
1891                read_nhandles_len,
1892                write_nhandles,
1893                write_nhandles_len,
1894                read_fhandles,
1895                read_fhandles_len,
1896                write_fhandles,
1897                write_fhandles_len);
1898   t->callback = task;
1899   t->callback_cls = task_cls;
1900   t->own_handles = GNUNET_YES;
1901   /* free the arrays of pointers to network / file handles, the actual
1902    * handles will be freed in destroy_task */
1903   GNUNET_array_grow(read_nhandles, read_nhandles_len, 0);
1904   GNUNET_array_grow(write_nhandles, write_nhandles_len, 0);
1905   GNUNET_array_grow(read_fhandles, read_fhandles_len, 0);
1906   GNUNET_array_grow(write_fhandles, write_fhandles_len, 0);
1907 #if PROFILE_DELAYS
1908   t->start_time = GNUNET_TIME_absolute_get();
1909 #endif
1910   t->timeout = GNUNET_TIME_relative_to_absolute(delay);
1911   t->priority =
1912     check_priority((prio ==
1913                     GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1914                    prio);
1915   t->lifeness = current_lifeness;
1916   GNUNET_CONTAINER_DLL_insert(pending_head,
1917                               pending_tail,
1918                               t);
1919   driver_add_multiple(t);
1920   max_priority_added = GNUNET_MAX(max_priority_added,
1921                                   t->priority);
1922   LOG(GNUNET_ERROR_TYPE_DEBUG,
1923       "Adding task %p\n",
1924       t);
1925   init_backtrace(t);
1926   return t;
1927 }
1928
1929
1930 /**
1931  * Function used by event-loop implementations to signal the scheduler
1932  * that a particular @a task is ready due to an event specified in the
1933  * et field of @a fdi.
1934  *
1935  * This function will then queue the task to notify the application
1936  * that the task is ready (with the respective priority).
1937  *
1938  * @param task the task that is ready
1939  * @param fdi information about the related FD
1940  */
1941 void
1942 GNUNET_SCHEDULER_task_ready(struct GNUNET_SCHEDULER_Task *task,
1943                             struct GNUNET_SCHEDULER_FdInfo *fdi)
1944 {
1945   enum GNUNET_SCHEDULER_Reason reason;
1946
1947   reason = task->reason;
1948   if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1949       (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)))
1950     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1951   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1952       (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)))
1953     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1954   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1955   task->reason = reason;
1956   if (GNUNET_NO == task->in_ready_list)
1957     {
1958       GNUNET_CONTAINER_DLL_remove(pending_head,
1959                                   pending_tail,
1960                                   task);
1961       queue_ready_task(task);
1962     }
1963 }
1964
1965
1966 /**
1967  * Function called by external event loop implementations to tell the
1968  * scheduler to run some of the tasks that are ready. Must be called
1969  * only after #GNUNET_SCHEDULER_driver_init has been called and before
1970  * #GNUNET_SCHEDULER_driver_done is called.
1971  * This function may return even though there are tasks left to run
1972  * just to give other tasks a chance as well.  If we return #GNUNET_YES,
1973  * the event loop implementation should call this function again as
1974  * soon as possible, while if we return #GNUNET_NO it must block until
1975  * either the operating system has more work (the scheduler has no more
1976  * work to do right now) or the timeout set by the scheduler (using the
1977  * set_wakeup callback) is reached.
1978  *
1979  * @param sh scheduler handle that was returned by
1980  *        #GNUNET_SCHEDULER_driver_init
1981  * @return #GNUNET_YES if there are more tasks that are ready,
1982  *         and thus we would like to run more (yield to avoid
1983  *         blocking other activities for too long) #GNUNET_NO
1984  *         if we are done running tasks (yield to block)
1985  */
1986 int
1987 GNUNET_SCHEDULER_do_work(struct GNUNET_SCHEDULER_Handle *sh)
1988 {
1989   enum GNUNET_SCHEDULER_Priority p;
1990   struct GNUNET_SCHEDULER_Task *pos;
1991   struct GNUNET_TIME_Absolute now;
1992
1993   /* check for tasks that reached the timeout! */
1994   now = GNUNET_TIME_absolute_get();
1995   pos = pending_timeout_head;
1996   while (NULL != pos)
1997     {
1998       struct GNUNET_SCHEDULER_Task *next = pos->next;
1999       if (now.abs_value_us >= pos->timeout.abs_value_us)
2000         pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2001       if (0 == pos->reason)
2002         break;
2003       GNUNET_CONTAINER_DLL_remove(pending_timeout_head,
2004                                   pending_timeout_tail,
2005                                   pos);
2006       if (pending_timeout_last == pos)
2007         pending_timeout_last = NULL;
2008       queue_ready_task(pos);
2009       pos = next;
2010     }
2011   pos = pending_head;
2012   while (NULL != pos)
2013     {
2014       struct GNUNET_SCHEDULER_Task *next = pos->next;
2015       if (now.abs_value_us >= pos->timeout.abs_value_us)
2016         {
2017           pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
2018           GNUNET_CONTAINER_DLL_remove(pending_head,
2019                                       pending_tail,
2020                                       pos);
2021           queue_ready_task(pos);
2022         }
2023       pos = next;
2024     }
2025
2026   if (0 == ready_count)
2027     {
2028       struct GNUNET_TIME_Absolute timeout = get_timeout();
2029
2030       if (timeout.abs_value_us > now.abs_value_us)
2031         {
2032           /**
2033            * The event loop called this function before the current timeout was
2034            * reached (and no FD tasks are ready). This is acceptable if
2035            *
2036            * - the system time was changed while the driver was waiting for
2037            *   the timeout
2038            * - an external event loop called GNUnet API functions outside of
2039            *   the callbacks called in GNUNET_SCHEDULER_do_work and thus
2040            *   wasn't notified about the new timeout
2041            *
2042            * It might also mean we are busy-waiting because of a programming
2043            * error in the external event loop.
2044            */
2045           LOG(GNUNET_ERROR_TYPE_DEBUG,
2046               "GNUNET_SCHEDULER_do_work did not find any ready "
2047               "tasks and timeout has not been reached yet.\n");
2048         }
2049       else
2050         {
2051           /**
2052            * the current timeout was reached but no ready tasks were found,
2053            * internal scheduler error!
2054            */
2055           GNUNET_assert(0);
2056         }
2057     }
2058   else
2059     {
2060       /* find out which task priority level we are going to
2061          process this time */
2062       max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
2063       GNUNET_assert(NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
2064       /* yes, p>0 is correct, 0 is "KEEP" which should
2065        * always be an empty queue (see assertion)! */
2066       for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
2067         {
2068           pos = ready_head[p];
2069           if (NULL != pos)
2070             break;
2071         }
2072       GNUNET_assert(NULL != pos);       /* ready_count wrong? */
2073
2074       /* process all tasks at this priority level, then yield */
2075       while (NULL != (pos = ready_head[p]))
2076         {
2077           GNUNET_CONTAINER_DLL_remove(ready_head[p],
2078                                       ready_tail[p],
2079                                       pos);
2080           ready_count--;
2081           current_priority = pos->priority;
2082           current_lifeness = pos->lifeness;
2083           active_task = pos;
2084 #if PROFILE_DELAYS
2085           if (GNUNET_TIME_absolute_get_duration(pos->start_time).rel_value_us >
2086               DELAY_THRESHOLD.rel_value_us)
2087             {
2088               LOG(GNUNET_ERROR_TYPE_DEBUG,
2089                   "Task %p took %s to be scheduled\n",
2090                   pos,
2091                   GNUNET_STRINGS_relative_time_to_string(GNUNET_TIME_absolute_get_duration(pos->start_time),
2092                                                          GNUNET_YES));
2093             }
2094 #endif
2095           tc.reason = pos->reason;
2096           GNUNET_NETWORK_fdset_zero(sh->rs);
2097           GNUNET_NETWORK_fdset_zero(sh->ws);
2098           // FIXME: do we have to remove FdInfos from fds if they are not ready?
2099           tc.fds_len = pos->fds_len;
2100           tc.fds = pos->fds;
2101           for (unsigned int i = 0; i != pos->fds_len; ++i)
2102             {
2103               struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
2104               if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
2105                 {
2106                   GNUNET_NETWORK_fdset_set_native(sh->rs,
2107                                                   fdi->sock);
2108                 }
2109               if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
2110                 {
2111                   GNUNET_NETWORK_fdset_set_native(sh->ws,
2112                                                   fdi->sock);
2113                 }
2114             }
2115           tc.read_ready = sh->rs;
2116           tc.write_ready = sh->ws;
2117           LOG(GNUNET_ERROR_TYPE_DEBUG,
2118               "Running task %p\n",
2119               pos);
2120           GNUNET_assert(NULL != pos->callback);
2121           {
2122             struct GNUNET_AsyncScopeSave old_scope;
2123             if (pos->scope.have_scope)
2124               GNUNET_async_scope_enter(&pos->scope.scope_id, &old_scope);
2125             else
2126               GNUNET_async_scope_get(&old_scope);
2127             pos->callback(pos->callback_cls);
2128             GNUNET_async_scope_restore(&old_scope);
2129           }
2130           if (NULL != pos->fds)
2131             {
2132               int del_result = scheduler_driver->del(scheduler_driver->cls, pos);
2133               if (GNUNET_OK != del_result)
2134                 {
2135                   LOG(GNUNET_ERROR_TYPE_ERROR,
2136                       "driver could not delete task %p\n", pos);
2137                   GNUNET_assert(0);
2138                 }
2139             }
2140           active_task = NULL;
2141           dump_backtrace(pos);
2142           destroy_task(pos);
2143         }
2144     }
2145   shutdown_if_no_lifeness();
2146   if (0 == ready_count)
2147     {
2148       scheduler_driver->set_wakeup(scheduler_driver->cls,
2149                                    get_timeout());
2150       return GNUNET_NO;
2151     }
2152   scheduler_driver->set_wakeup(scheduler_driver->cls,
2153                                GNUNET_TIME_absolute_get());
2154   return GNUNET_YES;
2155 }
2156
2157
2158 /**
2159  * Function called by external event loop implementations to initialize
2160  * the scheduler. An external implementation has to provide @a driver
2161  * which contains callbacks for the scheduler (see definition of struct
2162  * #GNUNET_SCHEDULER_Driver). The callbacks are used to instruct the
2163  * external implementation to watch for events. If it detects any of
2164  * those events it is expected to call #GNUNET_SCHEDULER_do_work to let
2165  * the scheduler handle it. If an event is related to a specific task
2166  * (e.g. the scheduler gave instructions to watch a file descriptor),
2167  * the external implementation is expected to mark that task ready
2168  * before by calling #GNUNET_SCHEDULER_task_ready.
2169
2170  * This function has to be called before any tasks are scheduled and
2171  * before GNUNET_SCHEDULER_do_work is called for the first time. It
2172  * allocates resources that have to be freed again by calling
2173  * #GNUNET_SCHEDULER_driver_done.
2174  *
2175  * This function installs the same signal handlers as
2176  * #GNUNET_SCHEDULER_run. This means SIGTERM (and other similar signals)
2177  * will induce a call to #GNUNET_SCHEDULER_shutdown during the next
2178  * call to #GNUNET_SCHEDULER_do_work. As a result, SIGTERM causes all
2179  * active tasks to be scheduled with reason
2180  * #GNUNET_SCHEDULER_REASON_SHUTDOWN. (However, tasks added afterwards
2181  * will execute normally!). Note that any particular signal will only
2182  * shut down one scheduler; applications should always only create a
2183  * single scheduler.
2184  *
2185  * @param driver to use for the event loop
2186  * @return handle to be passed to #GNUNET_SCHEDULER_do_work and
2187  *         #GNUNET_SCHEDULER_driver_done
2188  */
2189 struct GNUNET_SCHEDULER_Handle *
2190 GNUNET_SCHEDULER_driver_init(const struct GNUNET_SCHEDULER_Driver *driver)
2191 {
2192   struct GNUNET_SCHEDULER_Handle *sh;
2193   const struct GNUNET_DISK_FileHandle *pr;
2194
2195   /* scheduler must not be running */
2196   GNUNET_assert(NULL == scheduler_driver);
2197   GNUNET_assert(NULL == shutdown_pipe_handle);
2198   /* general set-up */
2199   sh = GNUNET_new(struct GNUNET_SCHEDULER_Handle);
2200   shutdown_pipe_handle = GNUNET_DISK_pipe(GNUNET_NO,
2201                                           GNUNET_NO,
2202                                           GNUNET_NO,
2203                                           GNUNET_NO);
2204   GNUNET_assert(NULL != shutdown_pipe_handle);
2205   pr = GNUNET_DISK_pipe_handle(shutdown_pipe_handle,
2206                                GNUNET_DISK_PIPE_END_READ);
2207   my_pid = getpid();
2208   scheduler_driver = driver;
2209
2210   /* install signal handlers */
2211   LOG(GNUNET_ERROR_TYPE_DEBUG,
2212       "Registering signal handlers\n");
2213   sh->shc_int = GNUNET_SIGNAL_handler_install(SIGINT,
2214                                               &sighandler_shutdown);
2215   sh->shc_term = GNUNET_SIGNAL_handler_install(SIGTERM,
2216                                                &sighandler_shutdown);
2217 #if (SIGTERM != GNUNET_TERM_SIG)
2218   sh->shc_gterm = GNUNET_SIGNAL_handler_install(GNUNET_TERM_SIG,
2219                                                 &sighandler_shutdown);
2220 #endif
2221 #ifndef MINGW
2222   sh->shc_pipe = GNUNET_SIGNAL_handler_install(SIGPIPE,
2223                                                &sighandler_pipe);
2224   sh->shc_quit = GNUNET_SIGNAL_handler_install(SIGQUIT,
2225                                                &sighandler_shutdown);
2226   sh->shc_hup = GNUNET_SIGNAL_handler_install(SIGHUP,
2227                                               &sighandler_shutdown);
2228 #endif
2229
2230   /* Setup initial tasks */
2231   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2232   current_lifeness = GNUNET_NO;
2233   install_parent_control_task =
2234     GNUNET_SCHEDULER_add_now(&install_parent_control_handler,
2235                              NULL);
2236   shutdown_pipe_task =
2237     GNUNET_SCHEDULER_add_read_file(GNUNET_TIME_UNIT_FOREVER_REL,
2238                                    pr,
2239                                    &shutdown_pipe_cb,
2240                                    NULL);
2241   current_lifeness = GNUNET_YES;
2242   scheduler_driver->set_wakeup(scheduler_driver->cls,
2243                                get_timeout());
2244   /* begin main event loop */
2245   sh->rs = GNUNET_NETWORK_fdset_create();
2246   sh->ws = GNUNET_NETWORK_fdset_create();
2247   GNUNET_NETWORK_fdset_handle_set(sh->rs, pr);
2248   return sh;
2249 }
2250
2251
2252 /**
2253  * Counter-part of #GNUNET_SCHEDULER_driver_init. Has to be called
2254  * by external event loop implementations after the scheduler has
2255  * shut down. This is the case if both of the following conditions
2256  * are met:
2257  *
2258  * - all tasks the scheduler has added through the driver's add
2259  *   callback have been removed again through the driver's del
2260  *   callback
2261  * - the timeout the scheduler has set through the driver's
2262  *   add_wakeup callback is FOREVER
2263  *
2264  * @param sh the handle returned by #GNUNET_SCHEDULER_driver_init
2265  */
2266 void
2267 GNUNET_SCHEDULER_driver_done(struct GNUNET_SCHEDULER_Handle *sh)
2268 {
2269   GNUNET_assert(NULL == pending_head);
2270   GNUNET_assert(NULL == pending_timeout_head);
2271   GNUNET_assert(NULL == shutdown_head);
2272   for (int i = 0; i != GNUNET_SCHEDULER_PRIORITY_COUNT; ++i)
2273     {
2274       GNUNET_assert(NULL == ready_head[i]);
2275     }
2276   GNUNET_NETWORK_fdset_destroy(sh->rs);
2277   GNUNET_NETWORK_fdset_destroy(sh->ws);
2278
2279   /* uninstall signal handlers */
2280   GNUNET_SIGNAL_handler_uninstall(sh->shc_int);
2281   GNUNET_SIGNAL_handler_uninstall(sh->shc_term);
2282 #if (SIGTERM != GNUNET_TERM_SIG)
2283   GNUNET_SIGNAL_handler_uninstall(sh->shc_gterm);
2284 #endif
2285 #ifndef MINGW
2286   GNUNET_SIGNAL_handler_uninstall(sh->shc_pipe);
2287   GNUNET_SIGNAL_handler_uninstall(sh->shc_quit);
2288   GNUNET_SIGNAL_handler_uninstall(sh->shc_hup);
2289 #endif
2290   GNUNET_DISK_pipe_close(shutdown_pipe_handle);
2291   shutdown_pipe_handle = NULL;
2292   scheduler_driver = NULL;
2293   GNUNET_free(sh);
2294 }
2295
2296
2297 static int
2298 select_loop(struct GNUNET_SCHEDULER_Handle *sh,
2299             struct DriverContext *context)
2300 {
2301   struct GNUNET_NETWORK_FDSet *rs;
2302   struct GNUNET_NETWORK_FDSet *ws;
2303   int select_result;
2304
2305   GNUNET_assert(NULL != context);
2306   rs = GNUNET_NETWORK_fdset_create();
2307   ws = GNUNET_NETWORK_fdset_create();
2308   while ((NULL != context->scheduled_head) ||
2309          (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != context->timeout.abs_value_us))
2310     {
2311       LOG(GNUNET_ERROR_TYPE_DEBUG,
2312           "select timeout = %s\n",
2313           GNUNET_STRINGS_absolute_time_to_string(context->timeout));
2314
2315       GNUNET_NETWORK_fdset_zero(rs);
2316       GNUNET_NETWORK_fdset_zero(ws);
2317
2318       for (struct Scheduled *pos = context->scheduled_head;
2319            NULL != pos;
2320            pos = pos->next)
2321         {
2322           if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2323             {
2324               GNUNET_NETWORK_fdset_set_native(rs, pos->fdi->sock);
2325             }
2326           if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2327             {
2328               GNUNET_NETWORK_fdset_set_native(ws, pos->fdi->sock);
2329             }
2330         }
2331       struct GNUNET_TIME_Relative time_remaining =
2332         GNUNET_TIME_absolute_get_remaining(context->timeout);
2333       if (NULL == scheduler_select)
2334         {
2335           select_result = GNUNET_NETWORK_socket_select(rs,
2336                                                        ws,
2337                                                        NULL,
2338                                                        time_remaining);
2339         }
2340       else
2341         {
2342           select_result = scheduler_select(scheduler_select_cls,
2343                                            rs,
2344                                            ws,
2345                                            NULL,
2346                                            time_remaining);
2347         }
2348       if (select_result == GNUNET_SYSERR)
2349         {
2350           if (errno == EINTR)
2351             continue;
2352
2353           LOG_STRERROR(GNUNET_ERROR_TYPE_ERROR,
2354                        "select");
2355 #ifndef MINGW
2356 #if USE_LSOF
2357           char lsof[512];
2358
2359           snprintf(lsof,
2360                    sizeof(lsof),
2361                    "lsof -p %d",
2362                    getpid());
2363           (void)close(1);
2364           (void)dup2(2, 1);
2365           if (0 != system(lsof))
2366             LOG_STRERROR(GNUNET_ERROR_TYPE_WARNING,
2367                          "system");
2368 #endif
2369 #endif
2370 #if DEBUG_FDS
2371           for (struct Scheduled *s = context->scheduled_head;
2372                NULL != s;
2373                s = s->next)
2374             {
2375               int flags = fcntl(s->fdi->sock,
2376                                 F_GETFD);
2377
2378               if ((flags == -1) &&
2379                   (EBADF == errno))
2380                 {
2381                   LOG(GNUNET_ERROR_TYPE_ERROR,
2382                       "Got invalid file descriptor %d!\n",
2383                       s->fdi->sock);
2384 #if EXECINFO
2385                   dump_backtrace(s->task);
2386 #endif
2387                 }
2388             }
2389 #endif
2390           GNUNET_assert(0);
2391           GNUNET_NETWORK_fdset_destroy(rs);
2392           GNUNET_NETWORK_fdset_destroy(ws);
2393           return GNUNET_SYSERR;
2394         }
2395       if (select_result > 0)
2396         {
2397           for (struct Scheduled *pos = context->scheduled_head;
2398                NULL != pos;
2399                pos = pos->next)
2400             {
2401               int is_ready = GNUNET_NO;
2402
2403               if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2404                   GNUNET_YES ==
2405                   GNUNET_NETWORK_fdset_test_native(rs,
2406                                                    pos->fdi->sock))
2407                 {
2408                   pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2409                   is_ready = GNUNET_YES;
2410                 }
2411               if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2412                   GNUNET_YES ==
2413                   GNUNET_NETWORK_fdset_test_native(ws,
2414                                                    pos->fdi->sock))
2415                 {
2416                   pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2417                   is_ready = GNUNET_YES;
2418                 }
2419               if (GNUNET_YES == is_ready)
2420                 {
2421                   GNUNET_SCHEDULER_task_ready(pos->task,
2422                                               pos->fdi);
2423                 }
2424             }
2425         }
2426       if (GNUNET_YES == GNUNET_SCHEDULER_do_work(sh))
2427         {
2428           LOG(GNUNET_ERROR_TYPE_DEBUG,
2429               "scheduler has more tasks ready!\n");
2430         }
2431     }
2432   GNUNET_NETWORK_fdset_destroy(rs);
2433   GNUNET_NETWORK_fdset_destroy(ws);
2434   return GNUNET_OK;
2435 }
2436
2437
2438 static int
2439 select_add(void *cls,
2440            struct GNUNET_SCHEDULER_Task *task,
2441            struct GNUNET_SCHEDULER_FdInfo *fdi)
2442 {
2443   struct DriverContext *context = cls;
2444
2445   GNUNET_assert(NULL != context);
2446   GNUNET_assert(NULL != task);
2447   GNUNET_assert(NULL != fdi);
2448   GNUNET_assert(0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2449                 0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2450
2451   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2452     {
2453       /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2454       return GNUNET_SYSERR;
2455     }
2456
2457   struct Scheduled *scheduled = GNUNET_new(struct Scheduled);
2458   scheduled->task = task;
2459   scheduled->fdi = fdi;
2460   scheduled->et = fdi->et;
2461
2462   GNUNET_CONTAINER_DLL_insert(context->scheduled_head,
2463                               context->scheduled_tail,
2464                               scheduled);
2465   return GNUNET_OK;
2466 }
2467
2468
2469 static int
2470 select_del(void *cls,
2471            struct GNUNET_SCHEDULER_Task *task)
2472 {
2473   struct DriverContext *context;
2474   struct Scheduled *pos;
2475   int ret;
2476
2477   GNUNET_assert(NULL != cls);
2478
2479   context = cls;
2480   ret = GNUNET_SYSERR;
2481   pos = context->scheduled_head;
2482   while (NULL != pos)
2483     {
2484       struct Scheduled *next = pos->next;
2485       if (pos->task == task)
2486         {
2487           GNUNET_CONTAINER_DLL_remove(context->scheduled_head,
2488                                       context->scheduled_tail,
2489                                       pos);
2490           GNUNET_free(pos);
2491           ret = GNUNET_OK;
2492         }
2493       pos = next;
2494     }
2495   return ret;
2496 }
2497
2498
2499 static void
2500 select_set_wakeup(void *cls,
2501                   struct GNUNET_TIME_Absolute dt)
2502 {
2503   struct DriverContext *context = cls;
2504
2505   GNUNET_assert(NULL != context);
2506   context->timeout = dt;
2507 }
2508
2509
2510 /**
2511  * Obtain the driver for using select() as the event loop.
2512  *
2513  * @return NULL on error
2514  */
2515 struct GNUNET_SCHEDULER_Driver *
2516 GNUNET_SCHEDULER_driver_select()
2517 {
2518   struct GNUNET_SCHEDULER_Driver *select_driver;
2519
2520   select_driver = GNUNET_new(struct GNUNET_SCHEDULER_Driver);
2521
2522   select_driver->add = &select_add;
2523   select_driver->del = &select_del;
2524   select_driver->set_wakeup = &select_set_wakeup;
2525
2526   return select_driver;
2527 }
2528
2529
2530 /**
2531  * Change the async scope for the currently executing task and (transitively)
2532  * for all tasks scheduled by the current task after calling this function.
2533  * Nested tasks can begin their own nested async scope.
2534  *
2535  * Once the current task is finished, the async scope ID is reset to
2536  * its previous value.
2537  *
2538  * Must only be called from a running task.
2539  *
2540  * @param aid the asynchronous scope id to enter
2541  */
2542 void
2543 GNUNET_SCHEDULER_begin_async_scope(struct GNUNET_AsyncScopeId *aid)
2544 {
2545   struct GNUNET_AsyncScopeSave dummy_old_scope;
2546
2547   GNUNET_assert(NULL != active_task);
2548   /* Since we're in a task, the context will be automatically
2549      restored by the scheduler. */
2550   GNUNET_async_scope_enter(aid, &dummy_old_scope);
2551 }
2552
2553
2554 /* end of scheduler.c */