only check fdsets when select selected something (optimization)
[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
1843   reason = task->reason;
1844   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1845        (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)) )
1846     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1847   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1848        (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)) )
1849     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1850   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1851   task->reason = reason;
1852   if (GNUNET_NO == task->in_ready_list)
1853   {
1854     GNUNET_CONTAINER_DLL_remove (pending_head,
1855                                  pending_tail,
1856                                  task);
1857     queue_ready_task (task);
1858   }
1859 }
1860
1861
1862 /**
1863  * Function called by the driver to tell the scheduler to run some of
1864  * the tasks that are ready.  This function may return even though
1865  * there are tasks left to run just to give other tasks a chance as
1866  * well.  If we return #GNUNET_YES, the driver should call this
1867  * function again as soon as possible, while if we return #GNUNET_NO
1868  * it must block until either the operating system has more work (the
1869  * scheduler has no more work to do right now) or the timeout set by
1870  * the scheduler (using the set_wakeup callback) is reached.
1871  *
1872  * @param sh scheduler handle that was given to the `loop`
1873  * @return #GNUNET_OK if there are more tasks that are ready,
1874  *          and thus we would like to run more (yield to avoid
1875  *          blocking other activities for too long)
1876  *         #GNUNET_NO if we are done running tasks (yield to block)
1877  *         #GNUNET_SYSERR on error, e.g. no tasks were ready
1878  */
1879 int
1880 GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh)
1881 {
1882   enum GNUNET_SCHEDULER_Priority p;
1883   struct GNUNET_SCHEDULER_Task *pos;
1884   struct GNUNET_TIME_Absolute now;
1885
1886   /* check for tasks that reached the timeout! */
1887   now = GNUNET_TIME_absolute_get ();
1888   while (NULL != (pos = pending_timeout_head))
1889   {
1890     if (now.abs_value_us >= pos->timeout.abs_value_us)
1891       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1892     if (0 == pos->reason)
1893       break;
1894     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1895                                  pending_timeout_tail,
1896                                  pos);
1897     if (pending_timeout_last == pos)
1898       pending_timeout_last = NULL;
1899     queue_ready_task (pos);
1900   }
1901   pos = pending_head;
1902   while (NULL != pos)
1903   {
1904     struct GNUNET_SCHEDULER_Task *next = pos->next;
1905     if (now.abs_value_us >= pos->timeout.abs_value_us)
1906     {
1907       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1908       GNUNET_CONTAINER_DLL_remove (pending_head,
1909                                    pending_tail,
1910                                    pos);
1911       queue_ready_task (pos);
1912     }
1913     pos = next;
1914   }
1915
1916   if (0 == ready_count)
1917   {
1918     LOG (GNUNET_ERROR_TYPE_ERROR,
1919          "GNUNET_SCHEDULER_run_from_driver was called, but no tasks are ready!\n");
1920     return GNUNET_SYSERR;
1921   }
1922
1923   /* find out which task priority level we are going to
1924      process this time */
1925   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
1926   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
1927   /* yes, p>0 is correct, 0 is "KEEP" which should
1928    * always be an empty queue (see assertion)! */
1929   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
1930   {
1931     pos = ready_head[p];
1932     if (NULL != pos)
1933       break;
1934   }
1935   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
1936
1937   /* process all tasks at this priority level, then yield */
1938   while (NULL != (pos = ready_head[p]))
1939   {
1940     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1941          ready_tail[p],
1942          pos);
1943     ready_count--;
1944     current_priority = pos->priority;
1945     current_lifeness = pos->lifeness;
1946     active_task = pos;
1947 #if PROFILE_DELAYS
1948     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
1949         DELAY_THRESHOLD.rel_value_us)
1950     {
1951       LOG (GNUNET_ERROR_TYPE_DEBUG,
1952            "Task %p took %s to be scheduled\n",
1953            pos,
1954            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
1955                                                    GNUNET_YES));
1956     }
1957 #endif
1958     tc.reason = pos->reason;
1959     GNUNET_NETWORK_fdset_zero (sh->rs);
1960     GNUNET_NETWORK_fdset_zero (sh->ws);
1961     // FIXME: do we have to remove FdInfos from fds if they are not ready?
1962     tc.fds_len = pos->fds_len;
1963     tc.fds = pos->fds;
1964     for (unsigned int i = 0; i != pos->fds_len; ++i)
1965     {
1966       struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
1967       if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
1968       {
1969         GNUNET_NETWORK_fdset_set_native (sh->rs,
1970                                          fdi->sock);
1971       }
1972       if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
1973       {
1974         GNUNET_NETWORK_fdset_set_native (sh->ws,
1975                                          fdi->sock);
1976       }
1977     }
1978     tc.read_ready = sh->rs;
1979     tc.write_ready = sh->ws;
1980     LOG (GNUNET_ERROR_TYPE_DEBUG,
1981          "Running task %p\n",
1982          pos);
1983     GNUNET_assert (NULL != pos->callback);
1984     pos->callback (pos->callback_cls);
1985     if (NULL != pos->fds)
1986     {
1987       int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
1988       if (GNUNET_OK != del_result)
1989       {
1990         LOG (GNUNET_ERROR_TYPE_ERROR,
1991            "driver could not delete task %p\n", pos);
1992         GNUNET_assert (0);
1993       }
1994     }
1995     active_task = NULL;
1996     dump_backtrace (pos);
1997     destroy_task (pos);
1998   }
1999   shutdown_if_no_lifeness ();
2000   if (0 == ready_count)
2001   {
2002     scheduler_driver->set_wakeup (scheduler_driver->cls,
2003                                   get_timeout ());
2004     return GNUNET_NO;
2005   }
2006   scheduler_driver->set_wakeup (scheduler_driver->cls,
2007                                 GNUNET_TIME_absolute_get ()); 
2008   return GNUNET_OK;
2009 }
2010
2011
2012 /**
2013  * Initialize and run scheduler.  This function will return when all
2014  * tasks have completed.  On systems with signals, receiving a SIGTERM
2015  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown
2016  * to be run after the active task is complete.  As a result, SIGTERM
2017  * causes all shutdown tasks to be scheduled with reason
2018  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
2019  * afterwards will execute normally!).  Note that any particular
2020  * signal will only shut down one scheduler; applications should
2021  * always only create a single scheduler.
2022  *
2023  * @param driver drive to use for the event loop
2024  * @param task task to run first (and immediately)
2025  * @param task_cls closure of @a task
2026  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
2027  */
2028 int
2029 GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver,
2030                                   GNUNET_SCHEDULER_TaskCallback task,
2031                                   void *task_cls)
2032 {
2033   int ret;
2034   struct GNUNET_SIGNAL_Context *shc_int;
2035   struct GNUNET_SIGNAL_Context *shc_term;
2036 #if (SIGTERM != GNUNET_TERM_SIG)
2037   struct GNUNET_SIGNAL_Context *shc_gterm;
2038 #endif
2039 #ifndef MINGW
2040   struct GNUNET_SIGNAL_Context *shc_quit;
2041   struct GNUNET_SIGNAL_Context *shc_hup;
2042   struct GNUNET_SIGNAL_Context *shc_pipe;
2043 #endif
2044   struct GNUNET_SCHEDULER_Task tsk;
2045   const struct GNUNET_DISK_FileHandle *pr;
2046
2047   /* general set-up */
2048   GNUNET_assert (NULL == active_task);
2049   GNUNET_assert (NULL == shutdown_pipe_handle);
2050   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
2051                                            GNUNET_NO,
2052                                            GNUNET_NO,
2053                                            GNUNET_NO);
2054   GNUNET_assert (NULL != shutdown_pipe_handle);
2055   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2056                                 GNUNET_DISK_PIPE_END_READ);
2057   my_pid = getpid ();
2058   scheduler_driver = driver;
2059
2060   /* install signal handlers */
2061   LOG (GNUNET_ERROR_TYPE_DEBUG,
2062        "Registering signal handlers\n");
2063   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2064              &sighandler_shutdown);
2065   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2066               &sighandler_shutdown);
2067 #if (SIGTERM != GNUNET_TERM_SIG)
2068   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2069                &sighandler_shutdown);
2070 #endif
2071 #ifndef MINGW
2072   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2073               &sighandler_pipe);
2074   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2075               &sighandler_shutdown);
2076   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2077              &sighandler_shutdown);
2078 #endif
2079
2080   /* Setup initial tasks */
2081   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2082   current_lifeness = GNUNET_NO;
2083   memset (&tsk,
2084     0,
2085     sizeof (tsk));
2086   active_task = &tsk;
2087   GNUNET_SCHEDULER_add_now (&GNUNET_OS_install_parent_control_handler,
2088                             NULL);
2089   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2090                                   pr,
2091                                   &shutdown_cb,
2092                                   NULL);
2093   current_lifeness = GNUNET_YES;
2094   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
2095                                                  task_cls,
2096                                                  GNUNET_SCHEDULER_REASON_STARTUP,
2097                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
2098   active_task = NULL;
2099   scheduler_driver->set_wakeup (scheduler_driver->cls,
2100                                 get_timeout ());
2101   /* begin main event loop */
2102   sh.rs = GNUNET_NETWORK_fdset_create ();
2103   sh.ws = GNUNET_NETWORK_fdset_create ();
2104   GNUNET_NETWORK_fdset_handle_set (sh.rs, pr);
2105   ret = driver->loop (driver->cls,
2106                       &sh);
2107   GNUNET_NETWORK_fdset_destroy (sh.rs);
2108   GNUNET_NETWORK_fdset_destroy (sh.ws);
2109
2110   /* uninstall signal handlers */
2111   GNUNET_SIGNAL_handler_uninstall (shc_int);
2112   GNUNET_SIGNAL_handler_uninstall (shc_term);
2113 #if (SIGTERM != GNUNET_TERM_SIG)
2114   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
2115 #endif
2116 #ifndef MINGW
2117   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
2118   GNUNET_SIGNAL_handler_uninstall (shc_quit);
2119   GNUNET_SIGNAL_handler_uninstall (shc_hup);
2120 #endif
2121   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2122   shutdown_pipe_handle = NULL;
2123   scheduler_driver = NULL;
2124   return ret;
2125 }
2126
2127
2128 int
2129 select_add (void *cls,
2130             struct GNUNET_SCHEDULER_Task *task,
2131             struct GNUNET_SCHEDULER_FdInfo *fdi)
2132 {
2133   struct DriverContext *context = cls;
2134   GNUNET_assert (NULL != context);
2135   GNUNET_assert (NULL != task);
2136   GNUNET_assert (NULL != fdi);
2137   GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2138                  0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2139
2140   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2141   {
2142     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2143     return GNUNET_SYSERR;
2144   }
2145
2146   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2147   scheduled->task = task;
2148   scheduled->fdi = fdi;
2149   scheduled->et = fdi->et;
2150
2151   GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2152                                context->scheduled_tail,
2153                                scheduled);
2154   return GNUNET_OK;
2155 }
2156
2157
2158 int
2159 select_del (void *cls,
2160             struct GNUNET_SCHEDULER_Task *task)
2161 {
2162   struct DriverContext *context;
2163   struct Scheduled *pos;
2164   int ret;
2165
2166   GNUNET_assert (NULL != cls);
2167
2168   context = cls;
2169   ret = GNUNET_SYSERR;
2170   pos = context->scheduled_head;
2171   while (NULL != pos)
2172   {
2173     struct Scheduled *next = pos->next;
2174     if (pos->task == task)
2175     {
2176       GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2177                                    context->scheduled_tail,
2178                                    pos);
2179       GNUNET_free (pos);
2180       ret = GNUNET_OK;
2181     }
2182     pos = next;
2183   }
2184   return ret;
2185 }
2186
2187
2188 int
2189 select_loop (void *cls,
2190              struct GNUNET_SCHEDULER_Handle *sh)
2191 {
2192   struct GNUNET_NETWORK_FDSet *rs;
2193   struct GNUNET_NETWORK_FDSet *ws;
2194   struct DriverContext *context;
2195   int select_result;
2196   int tasks_ready;
2197  
2198   context = cls;
2199   GNUNET_assert (NULL != context);
2200   rs = GNUNET_NETWORK_fdset_create ();
2201   ws = GNUNET_NETWORK_fdset_create ();
2202   tasks_ready = GNUNET_NO;
2203   while (NULL != context->scheduled_head ||
2204          GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us != context->timeout.rel_value_us)
2205   {
2206     LOG (GNUNET_ERROR_TYPE_DEBUG,
2207          "select timeout = %s\n",
2208          GNUNET_STRINGS_relative_time_to_string (context->timeout, GNUNET_NO));
2209
2210     GNUNET_NETWORK_fdset_zero (rs);
2211     GNUNET_NETWORK_fdset_zero (ws);
2212     struct Scheduled *pos;
2213     for (pos = context->scheduled_head; NULL != pos; pos = pos->next)
2214     {
2215       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2216       {
2217         GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2218       }
2219       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2220       {
2221         GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2222       }
2223     }
2224     if (NULL == scheduler_select)
2225     {
2226       select_result = GNUNET_NETWORK_socket_select (rs,
2227                                                     ws,
2228                                                     NULL,
2229                                                     context->timeout);
2230     }
2231     else
2232     {
2233       select_result = scheduler_select (scheduler_select_cls,
2234                                         rs,
2235                                         ws,
2236                                         NULL,
2237                                         context->timeout);
2238     }
2239     if (select_result == GNUNET_SYSERR)
2240     {
2241       if (errno == EINTR)
2242         continue;
2243
2244       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
2245 #ifndef MINGW
2246 #if USE_LSOF
2247       char lsof[512];
2248
2249       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
2250       (void) close (1);
2251       (void) dup2 (2, 1);
2252       if (0 != system (lsof))
2253         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2254                       "system");
2255 #endif
2256 #endif
2257 #if DEBUG_FDS
2258       struct Scheduled *s;
2259       for (s = context->scheduled_head; NULL != s; s = s->next)
2260       {
2261         int flags = fcntl (s->fdi->sock, F_GETFD);
2262         if ((flags == -1) && (errno == EBADF))
2263         {
2264           LOG (GNUNET_ERROR_TYPE_ERROR,
2265                "Got invalid file descriptor %d!\n",
2266                s->fdi->sock);
2267         }
2268       }
2269 #endif
2270       GNUNET_assert (0);
2271       GNUNET_NETWORK_fdset_destroy (rs);
2272       GNUNET_NETWORK_fdset_destroy (ws);
2273       return GNUNET_SYSERR;
2274     }
2275     if (select_result > 0)
2276     {
2277       for (pos = context->scheduled_head; NULL != pos; pos = pos->next)
2278       {
2279         int is_ready = GNUNET_NO;
2280         if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2281             GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, pos->fdi->sock))
2282         {
2283           pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2284           is_ready = GNUNET_YES;
2285         }
2286         if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2287             GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, pos->fdi->sock))
2288         {
2289           pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2290           is_ready = GNUNET_YES;
2291         }
2292         if (GNUNET_YES == is_ready)
2293         {
2294           GNUNET_SCHEDULER_task_ready (pos->task, pos->fdi);
2295         }
2296       }
2297     }
2298     tasks_ready = GNUNET_SCHEDULER_run_from_driver (sh);
2299     GNUNET_assert (GNUNET_SYSERR != tasks_ready);
2300   }
2301   GNUNET_NETWORK_fdset_destroy (rs);
2302   GNUNET_NETWORK_fdset_destroy (ws);
2303   return GNUNET_OK; 
2304 }
2305
2306
2307 void
2308 select_set_wakeup(void *cls,
2309                   struct GNUNET_TIME_Absolute dt)
2310 {
2311   struct DriverContext *context = cls;
2312   GNUNET_assert (NULL != context);
2313  
2314   context->timeout = GNUNET_TIME_absolute_get_remaining (dt);
2315 }
2316
2317
2318 /**
2319  * Obtain the driver for using select() as the event loop.
2320  *
2321  * @return NULL on error
2322  */
2323 struct GNUNET_SCHEDULER_Driver *
2324 GNUNET_SCHEDULER_driver_select ()
2325 {
2326   struct GNUNET_SCHEDULER_Driver *select_driver;
2327   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2328
2329   select_driver->loop = &select_loop;
2330   select_driver->add = &select_add;
2331   select_driver->del = &select_del;
2332   select_driver->set_wakeup = &select_set_wakeup;
2333
2334   return select_driver;
2335 }
2336
2337
2338 /* end of scheduler.c */