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