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