Merge branch 'master' of git+ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2009-2017 GNUnet e.V.
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file util/scheduler.c
22  * @brief schedule computations using continuation passing style
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "disk.h"
28
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
30
31 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
32
33
34 #if HAVE_EXECINFO_H
35 #include "execinfo.h"
36
37 /**
38  * Use lsof to generate file descriptor reports on select error?
39  * (turn off for stable releases).
40  */
41 #define USE_LSOF GNUNET_NO
42
43 /**
44  * Obtain trace information for all scheduler calls that schedule tasks.
45  */
46 #define EXECINFO GNUNET_NO
47
48 /**
49  * Check each file descriptor before adding
50  */
51 #define DEBUG_FDS GNUNET_NO
52
53 /**
54  * Depth of the traces collected via EXECINFO.
55  */
56 #define MAX_TRACE_DEPTH 50
57 #endif
58
59 /**
60  * Should we figure out which tasks are delayed for a while
61  * before they are run? (Consider using in combination with EXECINFO).
62  */
63 #define PROFILE_DELAYS GNUNET_NO
64
65 /**
66  * Task that were in the queue for longer than this are reported if
67  * PROFILE_DELAYS is active.
68  */
69 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
70
71
72 /**
73  * Argument to be passed from the driver to
74  * #GNUNET_SCHEDULER_run_from_driver().  Contains the
75  * scheduler's internal state.
76  */
77 struct GNUNET_SCHEDULER_Handle
78 {
79   /**
80    * Passed here to avoid constantly allocating/deallocating
81    * this element, but generally we want to get rid of this.
82    * @deprecated
83    */
84   struct GNUNET_NETWORK_FDSet *rs;
85
86   /**
87    * Passed here to avoid constantly allocating/deallocating
88    * this element, but generally we want to get rid of this.
89    * @deprecated
90    */
91   struct GNUNET_NETWORK_FDSet *ws;
92 };
93
94
95 /**
96  * Entry in list of pending tasks.
97  */
98 struct GNUNET_SCHEDULER_Task
99 {
100   /**
101    * This is a linked list.
102    */
103   struct GNUNET_SCHEDULER_Task *next;
104
105   /**
106    * This is a linked list.
107    */
108   struct GNUNET_SCHEDULER_Task *prev;
109
110   /**
111    * Function to run when ready.
112    */
113   GNUNET_SCHEDULER_TaskCallback callback;
114
115   /**
116    * Closure for the @e callback.
117    */
118   void *callback_cls;
119
120   /**
121    * Information about which FDs are ready for this task (and why).
122    */
123   struct GNUNET_SCHEDULER_FdInfo *fds;
124
125   /**
126    * Storage location used for @e fds if we want to avoid
127    * a separate malloc() call in the common case that this
128    * task is only about a single FD.
129    */
130   struct GNUNET_SCHEDULER_FdInfo fdx;
131
132   /**
133    * Size of the @e fds array.
134    */
135   unsigned int fds_len;
136
137   /**
138    * 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 (struct GNUNET_SCHEDULER_Task *t,
1663                  const struct GNUNET_NETWORK_FDSet *fdset,
1664                  const struct GNUNET_NETWORK_Handle ***ntarget,
1665                  unsigned int *extracted_nhandles,
1666                  const struct GNUNET_DISK_FileHandle ***ftarget,
1667                  unsigned int *extracted_fhandles)
1668 {
1669   // FIXME: this implementation only works for unix, for WIN32 the file handles
1670   // in fdset must be handled separately
1671   const struct GNUNET_NETWORK_Handle **nhandles;
1672   const struct GNUNET_DISK_FileHandle **fhandles;
1673   unsigned int nhandles_len;
1674   unsigned int fhandles_len;
1675
1676   (void) t;
1677   nhandles = NULL;
1678   fhandles = NULL;
1679   nhandles_len = 0;
1680   fhandles_len = 0;
1681   for (int sock = 0; sock != fdset->nsds; ++sock)
1682   {
1683     if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
1684     {
1685       struct GNUNET_NETWORK_Handle *nhandle;
1686       struct GNUNET_DISK_FileHandle *fhandle;
1687
1688       nhandle = GNUNET_NETWORK_socket_box_native (sock);
1689       if (NULL != nhandle)
1690       {
1691         GNUNET_array_append (nhandles, nhandles_len, nhandle);
1692       }
1693       else
1694       {
1695         fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
1696         if (NULL != fhandle)
1697         {
1698           GNUNET_array_append (fhandles, fhandles_len, fhandle);
1699         }
1700         else
1701         {
1702           GNUNET_assert (0);
1703         }
1704       }
1705     }
1706   }
1707   *ntarget = nhandles_len > 0 ? nhandles : NULL;
1708   *ftarget = fhandles_len > 0 ? fhandles : NULL;
1709   *extracted_nhandles = nhandles_len;
1710   *extracted_fhandles = fhandles_len;
1711 }
1712
1713
1714 /**
1715  * Schedule a new task to be run with a specified delay or when any of
1716  * the specified file descriptor sets is ready.  The delay can be used
1717  * as a timeout on the socket(s) being ready.  The task will be
1718  * scheduled for execution once either the delay has expired or any of
1719  * the socket operations is ready.  This is the most general
1720  * function of the "add" family.  Note that the "prerequisite_task"
1721  * must be satisfied in addition to any of the other conditions.  In
1722  * other words, the task will be started when
1723  * <code>
1724  * (prerequisite-run)
1725  * && (delay-ready
1726  *     || any-rs-ready
1727  *     || any-ws-ready) )
1728  * </code>
1729  * Only allowed to be called as long as the scheduler is running
1730  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1731  * called and has not returned yet).
1732  *
1733  * @param prio how important is this task?
1734  * @param delay how long should we wait?
1735  * @param rs set of file descriptors we want to read (can be NULL)
1736  * @param ws set of file descriptors we want to write (can be NULL)
1737  * @param task main function of the task
1738  * @param task_cls closure of @a task
1739  * @return unique task identifier for the job
1740  *         only valid until @a task is started!
1741  */
1742 struct GNUNET_SCHEDULER_Task *
1743 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1744                              struct GNUNET_TIME_Relative delay,
1745                              const struct GNUNET_NETWORK_FDSet *rs,
1746                              const struct GNUNET_NETWORK_FDSet *ws,
1747                              GNUNET_SCHEDULER_TaskCallback task,
1748                              void *task_cls)
1749 {
1750   struct GNUNET_SCHEDULER_Task *t;
1751   const struct GNUNET_NETWORK_Handle **read_nhandles;
1752   const struct GNUNET_NETWORK_Handle **write_nhandles;
1753   const struct GNUNET_DISK_FileHandle **read_fhandles;
1754   const struct GNUNET_DISK_FileHandle **write_fhandles;
1755   unsigned int read_nhandles_len, write_nhandles_len,
1756                read_fhandles_len, write_fhandles_len;
1757   int no_fdsets = (NULL == rs) && (NULL == ws);
1758   int no_socket_descriptors =
1759     ((NULL != rs) && (0 == rs->nsds)) && ((NULL != ws) && (0 == ws->nsds));
1760
1761   if (no_fdsets || no_socket_descriptors)
1762     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1763                                                        prio,
1764                                                        task,
1765                                                        task_cls);
1766   /* scheduler must be running */
1767   GNUNET_assert (NULL != scheduler_driver);
1768   GNUNET_assert (NULL != active_task);
1769   GNUNET_assert (NULL != task);
1770   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1771   t->callback = task;
1772   t->callback_cls = task_cls;
1773   t->read_fd = -1;
1774   t->write_fd = -1;
1775   t->own_handles = GNUNET_YES;
1776   read_nhandles = NULL;
1777   write_nhandles = NULL;
1778   read_fhandles = NULL;
1779   write_fhandles = NULL;
1780   read_nhandles_len = 0;
1781   write_nhandles_len = 0;
1782   read_fhandles_len = 0;
1783   write_fhandles_len = 0;
1784   if (NULL != rs)
1785   {
1786     extract_handles (t,
1787                      rs,
1788                      &read_nhandles,
1789                      &read_nhandles_len,
1790                      &read_fhandles,
1791                      &read_fhandles_len);
1792   }
1793   if (NULL != ws)
1794   {
1795     extract_handles (t,
1796                      ws,
1797                      &write_nhandles,
1798                      &write_nhandles_len,
1799                      &write_fhandles,
1800                      &write_fhandles_len);
1801   }
1802   init_fd_info (t,
1803                 read_nhandles,
1804                 read_nhandles_len,
1805                 write_nhandles,
1806                 write_nhandles_len,
1807                 read_fhandles,
1808                 read_fhandles_len,
1809                 write_fhandles,
1810                 write_fhandles_len);
1811   /* free the arrays of pointers to network / file handles, the actual
1812    * handles will be freed in destroy_task */
1813   GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
1814   GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
1815   GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
1816   GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
1817 #if PROFILE_DELAYS
1818   t->start_time = GNUNET_TIME_absolute_get ();
1819 #endif
1820   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1821   t->priority =
1822       check_priority ((prio ==
1823                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1824                       prio);
1825   t->lifeness = current_lifeness;
1826   GNUNET_CONTAINER_DLL_insert (pending_head,
1827                                pending_tail,
1828                                t);
1829   driver_add_multiple (t);
1830   max_priority_added = GNUNET_MAX (max_priority_added,
1831            t->priority);
1832   LOG (GNUNET_ERROR_TYPE_DEBUG,
1833        "Adding task %p\n",
1834        t);
1835   init_backtrace (t);
1836   return t;
1837 }
1838
1839
1840 /**
1841  * Function used by event-loop implementations to signal the scheduler
1842  * that a particular @a task is ready due to an event specified in the
1843  * et field of @a fdi.
1844  *
1845  * This function will then queue the task to notify the application
1846  * that the task is ready (with the respective priority).
1847  *
1848  * @param task the task that is ready
1849  * @param fdi information about the related FD
1850  */
1851 void
1852 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1853                              struct GNUNET_SCHEDULER_FdInfo *fdi)
1854 {
1855   enum GNUNET_SCHEDULER_Reason reason;
1856   struct GNUNET_TIME_Absolute now;
1857
1858   now = GNUNET_TIME_absolute_get ();
1859   reason = task->reason;
1860   if (now.abs_value_us >= task->timeout.abs_value_us)
1861     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1862   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1863        (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)) )
1864     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1865   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1866        (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)) )
1867     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1868   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1869   task->reason = reason;
1870   if (GNUNET_NO == task->in_ready_list)
1871   {
1872     GNUNET_CONTAINER_DLL_remove (pending_head,
1873                                  pending_tail,
1874                                  task);
1875     queue_ready_task (task);
1876   }
1877 }
1878
1879
1880 /**
1881  * Function called by the driver to tell the scheduler to run some of
1882  * the tasks that are ready.  This function may return even though
1883  * there are tasks left to run just to give other tasks a chance as
1884  * well.  If we return #GNUNET_YES, the driver should call this
1885  * function again as soon as possible, while if we return #GNUNET_NO
1886  * it must block until either the operating system has more work (the
1887  * scheduler has no more work to do right now) or the timeout set by
1888  * the scheduler (using the set_wakeup callback) is reached.
1889  *
1890  * @param sh scheduler handle that was given to the `loop`
1891  * @return #GNUNET_OK if there are more tasks that are ready,
1892  *          and thus we would like to run more (yield to avoid
1893  *          blocking other activities for too long)
1894  *         #GNUNET_NO if we are done running tasks (yield to block)
1895  *         #GNUNET_SYSERR on error, e.g. no tasks were ready
1896  */
1897 int
1898 GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh)
1899 {
1900   enum GNUNET_SCHEDULER_Priority p;
1901   struct GNUNET_SCHEDULER_Task *pos;
1902   struct GNUNET_TIME_Absolute now;
1903
1904   /* check for tasks that reached the timeout! */
1905   now = GNUNET_TIME_absolute_get ();
1906   while (NULL != (pos = pending_timeout_head))
1907   {
1908     if (now.abs_value_us >= pos->timeout.abs_value_us)
1909       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1910     if (0 == pos->reason)
1911       break;
1912     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1913                                  pending_timeout_tail,
1914                                  pos);
1915     if (pending_timeout_last == pos)
1916       pending_timeout_last = NULL;
1917     queue_ready_task (pos);
1918   }
1919   pos = pending_head;
1920   while (NULL != pos)
1921   {
1922     struct GNUNET_SCHEDULER_Task *next = pos->next;
1923     if (now.abs_value_us >= pos->timeout.abs_value_us)
1924     {
1925       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1926       GNUNET_CONTAINER_DLL_remove (pending_head,
1927                                    pending_tail,
1928                                    pos);
1929       queue_ready_task (pos);
1930     }
1931     pos = next;
1932   }
1933
1934   if (0 == ready_count)
1935   {
1936     LOG (GNUNET_ERROR_TYPE_ERROR,
1937          "GNUNET_SCHEDULER_run_from_driver was called, but no tasks are ready!\n");
1938     return GNUNET_SYSERR;
1939   }
1940
1941   /* find out which task priority level we are going to
1942      process this time */
1943   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
1944   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
1945   /* yes, p>0 is correct, 0 is "KEEP" which should
1946    * always be an empty queue (see assertion)! */
1947   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
1948   {
1949     pos = ready_head[p];
1950     if (NULL != pos)
1951       break;
1952   }
1953   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
1954
1955   /* process all tasks at this priority level, then yield */
1956   while (NULL != (pos = ready_head[p]))
1957   {
1958     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1959          ready_tail[p],
1960          pos);
1961     ready_count--;
1962     current_priority = pos->priority;
1963     current_lifeness = pos->lifeness;
1964     active_task = pos;
1965 #if PROFILE_DELAYS
1966     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
1967         DELAY_THRESHOLD.rel_value_us)
1968     {
1969       LOG (GNUNET_ERROR_TYPE_DEBUG,
1970            "Task %p took %s to be scheduled\n",
1971            pos,
1972            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
1973                                                    GNUNET_YES));
1974     }
1975 #endif
1976     tc.reason = pos->reason;
1977     GNUNET_NETWORK_fdset_zero (sh->rs);
1978     GNUNET_NETWORK_fdset_zero (sh->ws);
1979     // FIXME: do we have to remove FdInfos from fds if they are not ready?
1980     tc.fds_len = pos->fds_len;
1981     tc.fds = pos->fds;
1982     for (unsigned int i = 0; i != pos->fds_len; ++i)
1983     {
1984       struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
1985       if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
1986       {
1987         GNUNET_NETWORK_fdset_set_native (sh->rs,
1988                                          fdi->sock);
1989       }
1990       if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
1991       {
1992         GNUNET_NETWORK_fdset_set_native (sh->ws,
1993                                          fdi->sock);
1994       }
1995     }
1996     tc.read_ready = sh->rs;
1997     tc.write_ready = sh->ws;
1998     LOG (GNUNET_ERROR_TYPE_DEBUG,
1999          "Running task %p\n",
2000          pos);
2001     GNUNET_assert (NULL != pos->callback);
2002     pos->callback (pos->callback_cls);
2003     if (NULL != pos->fds)
2004     {
2005       int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
2006       if (GNUNET_OK != del_result)
2007       {
2008         LOG (GNUNET_ERROR_TYPE_ERROR,
2009              "driver could not delete task\n");
2010         GNUNET_assert (0);
2011       }
2012     }
2013     active_task = NULL;
2014     dump_backtrace (pos);
2015     destroy_task (pos);
2016     tasks_run++;
2017   }
2018   shutdown_if_no_lifeness ();
2019   if (0 == ready_count)
2020   {
2021     scheduler_driver->set_wakeup (scheduler_driver->cls,
2022                                   get_timeout ());
2023     return GNUNET_NO;
2024   }
2025   scheduler_driver->set_wakeup (scheduler_driver->cls,
2026                                 GNUNET_TIME_absolute_get ()); 
2027   return GNUNET_OK;
2028 }
2029
2030
2031 /**
2032  * Initialize and run scheduler.  This function will return when all
2033  * tasks have completed.  On systems with signals, receiving a SIGTERM
2034  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown
2035  * to be run after the active task is complete.  As a result, SIGTERM
2036  * causes all shutdown tasks to be scheduled with reason
2037  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
2038  * afterwards will execute normally!).  Note that any particular
2039  * signal will only shut down one scheduler; applications should
2040  * always only create a single scheduler.
2041  *
2042  * @param driver drive to use for the event loop
2043  * @param task task to run first (and immediately)
2044  * @param task_cls closure of @a task
2045  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
2046  */
2047 int
2048 GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver,
2049                                   GNUNET_SCHEDULER_TaskCallback task,
2050                                   void *task_cls)
2051 {
2052   int ret;
2053   struct GNUNET_SIGNAL_Context *shc_int;
2054   struct GNUNET_SIGNAL_Context *shc_term;
2055 #if (SIGTERM != GNUNET_TERM_SIG)
2056   struct GNUNET_SIGNAL_Context *shc_gterm;
2057 #endif
2058 #ifndef MINGW
2059   struct GNUNET_SIGNAL_Context *shc_quit;
2060   struct GNUNET_SIGNAL_Context *shc_hup;
2061   struct GNUNET_SIGNAL_Context *shc_pipe;
2062 #endif
2063   struct GNUNET_SCHEDULER_Task tsk;
2064   const struct GNUNET_DISK_FileHandle *pr;
2065
2066   /* general set-up */
2067   GNUNET_assert (NULL == active_task);
2068   GNUNET_assert (NULL == shutdown_pipe_handle);
2069   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
2070                                            GNUNET_NO,
2071                                            GNUNET_NO,
2072                                            GNUNET_NO);
2073   GNUNET_assert (NULL != shutdown_pipe_handle);
2074   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2075                                 GNUNET_DISK_PIPE_END_READ);
2076   my_pid = getpid ();
2077   scheduler_driver = driver;
2078
2079   /* install signal handlers */
2080   LOG (GNUNET_ERROR_TYPE_DEBUG,
2081        "Registering signal handlers\n");
2082   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2083              &sighandler_shutdown);
2084   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2085               &sighandler_shutdown);
2086 #if (SIGTERM != GNUNET_TERM_SIG)
2087   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2088                &sighandler_shutdown);
2089 #endif
2090 #ifndef MINGW
2091   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2092               &sighandler_pipe);
2093   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2094               &sighandler_shutdown);
2095   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2096              &sighandler_shutdown);
2097 #endif
2098
2099   /* Setup initial tasks */
2100   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2101   current_lifeness = GNUNET_NO;
2102   memset (&tsk,
2103     0,
2104     sizeof (tsk));
2105   active_task = &tsk;
2106   GNUNET_SCHEDULER_add_now (&GNUNET_OS_install_parent_control_handler,
2107                             NULL);
2108   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2109                                   pr,
2110                                   &shutdown_cb,
2111                                   NULL);
2112   current_lifeness = GNUNET_YES;
2113   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
2114                                                  task_cls,
2115                                                  GNUNET_SCHEDULER_REASON_STARTUP,
2116                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
2117   active_task = NULL;
2118   scheduler_driver->set_wakeup (scheduler_driver->cls,
2119                                 get_timeout ());
2120   /* begin main event loop */
2121   sh.rs = GNUNET_NETWORK_fdset_create ();
2122   sh.ws = GNUNET_NETWORK_fdset_create ();
2123   GNUNET_NETWORK_fdset_handle_set (sh.rs, pr);
2124   ret = driver->loop (driver->cls,
2125                       &sh);
2126   GNUNET_NETWORK_fdset_destroy (sh.rs);
2127   GNUNET_NETWORK_fdset_destroy (sh.ws);
2128
2129   /* uninstall signal handlers */
2130   GNUNET_SIGNAL_handler_uninstall (shc_int);
2131   GNUNET_SIGNAL_handler_uninstall (shc_term);
2132 #if (SIGTERM != GNUNET_TERM_SIG)
2133   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
2134 #endif
2135 #ifndef MINGW
2136   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
2137   GNUNET_SIGNAL_handler_uninstall (shc_quit);
2138   GNUNET_SIGNAL_handler_uninstall (shc_hup);
2139 #endif
2140   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2141   shutdown_pipe_handle = NULL;
2142   scheduler_driver = NULL;
2143   return ret;
2144 }
2145
2146
2147 int
2148 select_add (void *cls,
2149             struct GNUNET_SCHEDULER_Task *task,
2150             struct GNUNET_SCHEDULER_FdInfo *fdi)
2151 {
2152   struct DriverContext *context = cls;
2153   GNUNET_assert (NULL != context);
2154   GNUNET_assert (NULL != task);
2155   GNUNET_assert (NULL != fdi);
2156   GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2157                  0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2158
2159   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2160   {
2161     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2162     return GNUNET_SYSERR;
2163   }
2164
2165   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2166   scheduled->task = task;
2167   scheduled->fdi = fdi;
2168   scheduled->et = fdi->et;
2169
2170   GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2171                                context->scheduled_tail,
2172                                scheduled);
2173   return GNUNET_OK;
2174 }
2175
2176
2177 int
2178 select_del (void *cls,
2179             struct GNUNET_SCHEDULER_Task *task)
2180 {
2181   struct DriverContext *context;
2182   struct Scheduled *pos;
2183   int ret;
2184
2185   GNUNET_assert (NULL != cls);
2186
2187   context = cls;
2188   ret = GNUNET_SYSERR;
2189   pos = context->scheduled_head;
2190   while (NULL != pos)
2191   {
2192     struct Scheduled *next = pos->next;
2193     if (pos->task == task)
2194     {
2195       GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2196                                    context->scheduled_tail,
2197                                    pos);
2198       GNUNET_free (pos);
2199       ret = GNUNET_OK;
2200     }
2201     pos = next;
2202   }
2203   return ret;
2204 }
2205
2206
2207 int
2208 select_loop (void *cls,
2209              struct GNUNET_SCHEDULER_Handle *sh)
2210 {
2211   struct GNUNET_NETWORK_FDSet *rs;
2212   struct GNUNET_NETWORK_FDSet *ws;
2213   struct DriverContext *context;
2214   int select_result;
2215   int tasks_ready;
2216  
2217   context = cls;
2218   GNUNET_assert (NULL != context);
2219   rs = GNUNET_NETWORK_fdset_create ();
2220   ws = GNUNET_NETWORK_fdset_create ();
2221   tasks_ready = GNUNET_NO;
2222   while (NULL != context->scheduled_head ||
2223          GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us != context->timeout.rel_value_us)
2224   {
2225     LOG (GNUNET_ERROR_TYPE_DEBUG,
2226          "select timeout = %s\n",
2227          GNUNET_STRINGS_relative_time_to_string (context->timeout, GNUNET_NO));
2228
2229     GNUNET_NETWORK_fdset_zero (rs);
2230     GNUNET_NETWORK_fdset_zero (ws);
2231     struct Scheduled *pos;
2232     for (pos = context->scheduled_head; NULL != pos; pos = pos->next)
2233     {
2234       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2235       {
2236         GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2237       }
2238       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2239       {
2240         GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2241       }
2242     }
2243     if (NULL == scheduler_select)
2244     {
2245       select_result = GNUNET_NETWORK_socket_select (rs,
2246                                                     ws,
2247                                                     NULL,
2248                                                     context->timeout);
2249     }
2250     else
2251     {
2252       select_result = scheduler_select (scheduler_select_cls,
2253                                         rs,
2254                                         ws,
2255                                         NULL,
2256                                         context->timeout);
2257     }
2258     if (select_result == GNUNET_SYSERR)
2259     {
2260       if (errno == EINTR)
2261         continue;
2262
2263       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
2264 #ifndef MINGW
2265 #if USE_LSOF
2266       char lsof[512];
2267
2268       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
2269       (void) close (1);
2270       (void) dup2 (2, 1);
2271       if (0 != system (lsof))
2272         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2273                       "system");
2274 #endif
2275 #endif
2276 #if DEBUG_FDS
2277       struct Scheduled *s;
2278       for (s = context->scheduled_head; NULL != s; s = s->next)
2279       {
2280         int flags = fcntl (s->fdi->sock, F_GETFD);
2281         if ((flags == -1) && (errno == EBADF))
2282         {
2283           LOG (GNUNET_ERROR_TYPE_ERROR,
2284                "Got invalid file descriptor %d!\n",
2285                s->fdi->sock);
2286         }
2287       }
2288 #endif
2289       GNUNET_assert (0);
2290       GNUNET_NETWORK_fdset_destroy (rs);
2291       GNUNET_NETWORK_fdset_destroy (ws);
2292       return GNUNET_SYSERR;
2293     }
2294     for (pos = context->scheduled_head; NULL != pos; pos = pos->next)
2295     {
2296       int is_ready = GNUNET_NO;
2297       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2298           GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, pos->fdi->sock))
2299       {
2300         pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2301         is_ready = GNUNET_YES;
2302       }
2303       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2304           GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, pos->fdi->sock))
2305       {
2306         pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2307         is_ready = GNUNET_YES;
2308       }
2309       if (GNUNET_YES == is_ready)
2310       {
2311         GNUNET_SCHEDULER_task_ready (pos->task, pos->fdi);
2312       }
2313     }
2314     tasks_ready = GNUNET_SCHEDULER_run_from_driver (sh);
2315     GNUNET_assert (GNUNET_SYSERR != tasks_ready);
2316   }
2317   GNUNET_NETWORK_fdset_destroy (rs);
2318   GNUNET_NETWORK_fdset_destroy (ws);
2319   return GNUNET_OK; 
2320 }
2321
2322
2323 void
2324 select_set_wakeup(void *cls,
2325                   struct GNUNET_TIME_Absolute dt)
2326 {
2327   struct DriverContext *context = cls;
2328   GNUNET_assert (NULL != context);
2329  
2330   context->timeout = GNUNET_TIME_absolute_get_remaining (dt);
2331 }
2332
2333
2334 /**
2335  * Obtain the driver for using select() as the event loop.
2336  *
2337  * @return NULL on error
2338  */
2339 struct GNUNET_SCHEDULER_Driver *
2340 GNUNET_SCHEDULER_driver_select ()
2341 {
2342   struct GNUNET_SCHEDULER_Driver *select_driver;
2343   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2344
2345   select_driver->loop = &select_loop;
2346   select_driver->add = &select_add;
2347   select_driver->del = &select_del;
2348   select_driver->set_wakeup = &select_set_wakeup;
2349
2350   return select_driver;
2351 }
2352
2353
2354 /* end of scheduler.c */