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