9bd776517263166d4d7057ab6dd635e5357a52f9
[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   unsigned int i;
517
518   for (i = 0; i < t->num_backtrace_strings; i++)
519     LOG (GNUNET_ERROR_TYPE_WARNING,
520    "Task %p trace %u: %s\n",
521    t,
522    i,
523    t->backtrace_strings[i]);
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       fdi->fd = *read_nh;
760       GNUNET_assert (NULL != fdi->fd);
761       fdi->et = GNUNET_SCHEDULER_ET_IN;
762       fdi->sock = GNUNET_NETWORK_get_fd (*read_nh);
763       t->read_fd = fdi->sock;
764       t->write_fd = -1;
765     }
766     else if (1 == write_nh_len)
767     {
768       fdi->fd = *write_nh;
769       GNUNET_assert (NULL != fdi->fd);
770       fdi->et = GNUNET_SCHEDULER_ET_OUT;
771       fdi->sock = GNUNET_NETWORK_get_fd (*write_nh);
772       t->read_fd = -1;
773       t->write_fd = fdi->sock;
774     }
775     else if (1 == read_fh_len)
776     {
777       fdi->fh = *read_fh;
778       GNUNET_assert (NULL != fdi->fh);
779       fdi->et = GNUNET_SCHEDULER_ET_IN;
780       fdi->sock = (*read_fh)->fd; // FIXME: does not work under WIN32
781       t->read_fd = fdi->sock;
782       t->write_fd = -1;
783     }
784     else
785     {
786       fdi->fh = *write_fh;
787       GNUNET_assert (NULL != fdi->fh);
788       fdi->et = GNUNET_SCHEDULER_ET_OUT;
789       fdi->sock = (*write_fh)->fd; // FIXME: does not work under WIN32
790       t->read_fd = -1;
791       t->write_fd = fdi->sock;
792     }
793   }
794   else
795   {
796     fdi = GNUNET_new_array (t->fds_len, struct GNUNET_SCHEDULER_FdInfo);
797     t->fds = fdi;
798     t->read_fd = -1;
799     t->write_fd = -1;
800     unsigned int i;
801     for (i = 0; i != read_nh_len; ++i)
802     {
803       fdi->fd = read_nh[i];
804       GNUNET_assert (NULL != fdi->fd);
805       fdi->et = GNUNET_SCHEDULER_ET_IN;
806       fdi->sock = GNUNET_NETWORK_get_fd (read_nh[i]);
807       ++fdi;
808     }
809     for (i = 0; i != write_nh_len; ++i)
810     {
811       fdi->fd = write_nh[i];
812       GNUNET_assert (NULL != fdi->fd);
813       fdi->et = GNUNET_SCHEDULER_ET_OUT;
814       fdi->sock = GNUNET_NETWORK_get_fd (write_nh[i]);
815       ++fdi;
816     }
817     for (i = 0; i != read_fh_len; ++i)
818     {
819       fdi->fh = read_fh[i];
820       GNUNET_assert (NULL != fdi->fh);
821       fdi->et = GNUNET_SCHEDULER_ET_IN;
822       fdi->sock = (read_fh[i])->fd; // FIXME: does not work under WIN32
823       ++fdi;
824     }
825     for (i = 0; i != write_fh_len; ++i)
826     {
827       fdi->fh = write_fh[i];
828       GNUNET_assert (NULL != fdi->fh);
829       fdi->et = GNUNET_SCHEDULER_ET_OUT;
830       fdi->sock = (write_fh[i])->fd; // FIXME: does not work under WIN32
831       ++fdi;
832     }
833   }
834 }
835
836
837 /**
838  * calls the given function @a func on each FdInfo related to @a t.
839  * Optionally updates the event type field in each FdInfo after calling
840  * @a func.
841  *
842  * @param t the task
843  * @param driver_func the function to call with each FdInfo contained in
844  *                    in @a t
845  * @param if_not_ready only call @a driver_func on FdInfos that are not
846  *                     ready
847  * @param et the event type to be set in each FdInfo after calling
848  *           @a driver_func on it, or -1 if no updating not desired.
849  */
850 void driver_add_multiple (struct GNUNET_SCHEDULER_Task *t,
851                           enum GNUNET_SCHEDULER_EventType et)
852 {
853   struct GNUNET_SCHEDULER_FdInfo *fdi;
854   int success = GNUNET_YES;
855
856   for (int i = 0; i != t->fds_len; ++i)
857   {
858     fdi = &t->fds[i];
859     success = scheduler_driver->add (scheduler_driver->cls, t, fdi) && success;
860     if (et != -1)
861     {
862       fdi->et = et;
863     }
864   }
865   if (GNUNET_YES != success)
866   {
867     LOG (GNUNET_ERROR_TYPE_ERROR,
868          "driver could not add task\n");
869   }
870 }
871
872
873 void
874 shutdown_cb (void *cls)
875 {
876   char c;
877   const struct GNUNET_DISK_FileHandle *pr;
878
879   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
880                                 GNUNET_DISK_PIPE_END_READ);
881   GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
882   /* consume the signal */
883   GNUNET_DISK_file_read (pr, &c, sizeof (c));
884   /* mark all active tasks as ready due to shutdown */
885   GNUNET_SCHEDULER_shutdown ();
886 }
887
888
889 /**
890  * Cancel the task with the specified identifier.
891  * The task must not yet have run. Only allowed to be called as long as the
892  * scheduler is running (#GNUNET_SCHEDULER_run or
893  * #GNUNET_SCHEDULER_run_with_driver has been called and has not returned yet).
894  *
895  * @param task id of the task to cancel
896  * @return original closure of the task
897  */
898 void *
899 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
900 {
901   enum GNUNET_SCHEDULER_Priority p;
902   int is_fd_task;
903   void *ret;
904
905   LOG (GNUNET_ERROR_TYPE_DEBUG,
906        "canceling task %p\n",
907        task);
908
909   /* scheduler must be running */
910   GNUNET_assert (NULL != scheduler_driver);
911   GNUNET_assert ( (NULL != active_task) ||
912       (GNUNET_NO == task->lifeness) );
913   is_fd_task = (NULL != task->fds);
914   if (is_fd_task)
915   {
916     int del_result = scheduler_driver->del (scheduler_driver->cls, task);
917     if (GNUNET_OK != del_result)
918     {
919       LOG (GNUNET_ERROR_TYPE_ERROR,
920            "driver could not delete task\n");
921       GNUNET_assert (0);
922     }
923   }
924   if (! task->in_ready_list)
925   {
926     if (is_fd_task)
927     {
928       GNUNET_CONTAINER_DLL_remove (pending_head,
929                                    pending_tail,
930                                    task);
931     }
932     else if (GNUNET_YES == task->on_shutdown)
933     {
934       GNUNET_CONTAINER_DLL_remove (shutdown_head,
935                                    shutdown_tail,
936                                    task);
937     }
938     else
939     {
940       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
941                                    pending_timeout_tail,
942                                    task);
943       if (pending_timeout_last == task)
944         pending_timeout_last = NULL;
945     }
946   }
947   else
948   {
949     p = check_priority (task->priority);
950     GNUNET_CONTAINER_DLL_remove (ready_head[p],
951                                  ready_tail[p],
952                                  task);
953     ready_count--;
954   }
955   ret = task->callback_cls;
956   destroy_task (task);
957   return ret;
958 }
959
960
961 /**
962  * Initialize backtrace data for task @a t
963  *
964  * @param t task to initialize
965  */
966 static void
967 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
968 {
969 #if EXECINFO
970   void *backtrace_array[MAX_TRACE_DEPTH];
971
972   t->num_backtrace_strings
973     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
974   t->backtrace_strings =
975       backtrace_symbols (backtrace_array,
976        t->num_backtrace_strings);
977   dump_backtrace (t);
978 #endif
979 }
980
981
982 /**
983  * Continue the current execution with the given function.  This is
984  * similar to the other "add" functions except that there is no delay
985  * and the reason code can be specified.
986  *
987  * @param task main function of the task
988  * @param task_cls closure for @a task
989  * @param reason reason for task invocation
990  * @param priority priority to use for the task
991  */
992 void
993 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
994                                                void *task_cls,
995                                                enum GNUNET_SCHEDULER_Reason reason,
996                                                enum GNUNET_SCHEDULER_Priority priority)
997 {
998   struct GNUNET_SCHEDULER_Task *t;
999
1000   GNUNET_assert (NULL != task);
1001   GNUNET_assert ((NULL != active_task) ||
1002                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1003   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1004   t->read_fd = -1;
1005   t->write_fd = -1;
1006   t->callback = task;
1007   t->callback_cls = task_cls;
1008 #if PROFILE_DELAYS
1009   t->start_time = GNUNET_TIME_absolute_get ();
1010 #endif
1011   t->reason = reason;
1012   t->priority = priority;
1013   t->lifeness = current_lifeness;
1014   LOG (GNUNET_ERROR_TYPE_DEBUG,
1015        "Adding continuation task %p\n",
1016        t);
1017   init_backtrace (t);
1018   queue_ready_task (t);
1019 }
1020
1021
1022 /**
1023  * Schedule a new task to be run at the specified time.  The task
1024  * will be scheduled for execution at time @a at.
1025  *
1026  * @param at time when the operation should run
1027  * @param priority priority to use for the task
1028  * @param task main function of the task
1029  * @param task_cls closure of @a task
1030  * @return unique task identifier for the job
1031  *         only valid until @a task is started!
1032  */
1033 struct GNUNET_SCHEDULER_Task *
1034 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
1035                                        enum GNUNET_SCHEDULER_Priority priority,
1036                                        GNUNET_SCHEDULER_TaskCallback task,
1037                                        void *task_cls)
1038 {
1039   struct GNUNET_SCHEDULER_Task *t;
1040   struct GNUNET_SCHEDULER_Task *pos;
1041   struct GNUNET_SCHEDULER_Task *prev;
1042
1043   GNUNET_assert (NULL != active_task);
1044   GNUNET_assert (NULL != task);
1045   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1046   t->callback = task;
1047   t->callback_cls = task_cls;
1048   t->read_fd = -1;
1049   t->write_fd = -1;
1050 #if PROFILE_DELAYS
1051   t->start_time = GNUNET_TIME_absolute_get ();
1052 #endif
1053   t->timeout = at;
1054   t->priority = priority;
1055   t->lifeness = current_lifeness;
1056   /* try tail first (optimization in case we are
1057    * appending to a long list of tasks with timeouts) */
1058   if ( (NULL == pending_timeout_head) ||
1059        (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
1060   {
1061     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1062                                  pending_timeout_tail,
1063                                  t);
1064   }
1065   else
1066   {
1067     /* first move from heuristic start backwards to before start time */
1068     prev = pending_timeout_last;
1069     while ( (NULL != prev) &&
1070             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1071       prev = prev->prev;
1072     /* now, move from heuristic start (or head of list) forward to insertion point */
1073     if (NULL == prev)
1074       pos = pending_timeout_head;
1075     else
1076       pos = prev->next;
1077     while ( (NULL != pos) &&
1078             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1079               (0 != pos->reason) ) )
1080     {
1081       prev = pos;
1082       pos = pos->next;
1083     }
1084     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1085                                        pending_timeout_tail,
1086                                        prev,
1087                                        t);
1088   }
1089   /* finally, update heuristic insertion point to last insertion... */
1090   pending_timeout_last = t;
1091
1092   LOG (GNUNET_ERROR_TYPE_DEBUG,
1093        "Adding task %p\n",
1094        t);
1095   init_backtrace (t);
1096   return t;
1097 }
1098
1099
1100 /**
1101  * Schedule a new task to be run with a specified delay.  The task
1102  * will be scheduled for execution once the delay has expired.
1103  *
1104  * @param delay when should this operation time out?
1105  * @param priority priority to use for the task
1106  * @param task main function of the task
1107  * @param task_cls closure of @a task
1108  * @return unique task identifier for the job
1109  *         only valid until @a task is started!
1110  */
1111 struct GNUNET_SCHEDULER_Task *
1112 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1113               enum GNUNET_SCHEDULER_Priority priority,
1114               GNUNET_SCHEDULER_TaskCallback task,
1115                                             void *task_cls)
1116 {
1117   return GNUNET_SCHEDULER_add_at_with_priority (GNUNET_TIME_relative_to_absolute (delay),
1118                                                 priority,
1119                                                 task,
1120                                                 task_cls);
1121 }
1122
1123
1124 /**
1125  * Schedule a new task to be run with a specified priority.
1126  *
1127  * @param prio how important is the new task?
1128  * @param task main function of the task
1129  * @param task_cls closure of @a task
1130  * @return unique task identifier for the job
1131  *         only valid until @a task is started!
1132  */
1133 struct GNUNET_SCHEDULER_Task *
1134 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1135                                     GNUNET_SCHEDULER_TaskCallback task,
1136                                     void *task_cls)
1137 {
1138   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1139                                                      prio,
1140                                                      task,
1141                                                      task_cls);
1142 }
1143
1144
1145 /**
1146  * Schedule a new task to be run at the specified time.  The task
1147  * will be scheduled for execution once specified time has been
1148  * reached. It will be run with the DEFAULT priority.
1149  *
1150  * @param at time at which this operation should run
1151  * @param task main function of the task
1152  * @param task_cls closure of @a task
1153  * @return unique task identifier for the job
1154  *         only valid until @a task is started!
1155  */
1156 struct GNUNET_SCHEDULER_Task *
1157 GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1158                          GNUNET_SCHEDULER_TaskCallback task,
1159                          void *task_cls)
1160 {
1161   return GNUNET_SCHEDULER_add_at_with_priority (at,
1162                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1163                                                 task,
1164                                                 task_cls);
1165 }
1166
1167
1168 /**
1169  * Schedule a new task to be run with a specified delay.  The task
1170  * will be scheduled for execution once the delay has expired. It
1171  * will be run with the DEFAULT priority.
1172  *
1173  * @param delay when should this operation time out?
1174  * @param task main function of the task
1175  * @param task_cls closure of @a task
1176  * @return unique task identifier for the job
1177  *         only valid until @a task is started!
1178  */
1179 struct GNUNET_SCHEDULER_Task *
1180 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1181                               GNUNET_SCHEDULER_TaskCallback task,
1182                               void *task_cls)
1183 {
1184   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1185                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1186                  task,
1187                  task_cls);
1188 }
1189
1190
1191 /**
1192  * Schedule a new task to be run as soon as possible.  Note that this
1193  * does not guarantee that this will be the next task that is being
1194  * run, as other tasks with higher priority (or that are already ready
1195  * to run) might get to run first.  Just as with delays, clients must
1196  * not rely on any particular order of execution between tasks
1197  * scheduled concurrently.
1198  *
1199  * The task will be run with the DEFAULT priority.
1200  *
1201  * @param task main function of the task
1202  * @param task_cls closure of @a task
1203  * @return unique task identifier for the job
1204  *         only valid until @a task is started!
1205  */
1206 struct GNUNET_SCHEDULER_Task *
1207 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1208                           void *task_cls)
1209 {
1210   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
1211                                        task,
1212                                        task_cls);
1213 }
1214
1215
1216 /**
1217  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1218  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1219  * invoked.
1220  *
1221  * @param task main function of the task
1222  * @param task_cls closure of @a task
1223  * @return unique task identifier for the job
1224  *         only valid until @a task is started!
1225  */
1226 struct GNUNET_SCHEDULER_Task *
1227 GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1228                                void *task_cls)
1229 {
1230   struct GNUNET_SCHEDULER_Task *t;
1231
1232   GNUNET_assert (NULL != active_task);
1233   GNUNET_assert (NULL != task);
1234   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1235   t->callback = task;
1236   t->callback_cls = task_cls;
1237   t->read_fd = -1;
1238   t->write_fd = -1;
1239 #if PROFILE_DELAYS
1240   t->start_time = GNUNET_TIME_absolute_get ();
1241 #endif
1242   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1243   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1244   t->on_shutdown = GNUNET_YES;
1245   t->lifeness = GNUNET_NO;
1246   GNUNET_CONTAINER_DLL_insert (shutdown_head,
1247                                shutdown_tail,
1248                                t);
1249   LOG (GNUNET_ERROR_TYPE_DEBUG,
1250        "Adding shutdown task %p\n",
1251        t);
1252   init_backtrace (t);
1253   return t;
1254 }
1255
1256
1257 /**
1258  * Schedule a new task to be run as soon as possible with the
1259  * (transitive) ignore-shutdown flag either explicitly set or
1260  * explicitly enabled.  This task (and all tasks created from it,
1261  * other than by another call to this function) will either count or
1262  * not count for the "lifeness" of the process.  This API is only
1263  * useful in a few special cases.
1264  *
1265  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1266  * @param task main function of the task
1267  * @param task_cls closure of @a task
1268  * @return unique task identifier for the job
1269  *         only valid until @a task is started!
1270  */
1271 struct GNUNET_SCHEDULER_Task *
1272 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1273                                         GNUNET_SCHEDULER_TaskCallback task,
1274                                         void *task_cls)
1275 {
1276   struct GNUNET_SCHEDULER_Task *ret;
1277
1278   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1279   ret->lifeness = lifeness;
1280   return ret;
1281 }
1282
1283
1284 #if DEBUG_FDS
1285 /**
1286  * check a raw file descriptor and abort if it is bad (for debugging purposes)
1287  *
1288  * @param t the task related to the file descriptor
1289  * @param raw_fd the raw file descriptor to check
1290  */
1291 void
1292 check_fd (struct GNUNET_SCHEDULER_Task *t, int raw_fd)
1293 {
1294   if (-1 != raw_fd)
1295   {
1296     int flags = fcntl (raw_fd, F_GETFD);
1297
1298     if ((flags == -1) && (errno == EBADF))
1299     {
1300       LOG (GNUNET_ERROR_TYPE_ERROR,
1301            "Got invalid file descriptor %d!\n",
1302            raw_fd);
1303       init_backtrace (t);
1304       GNUNET_assert (0);
1305     }
1306   }
1307 }
1308 #endif
1309
1310
1311 /**
1312  * Schedule a new task to be run with a specified delay or when any of
1313  * the specified file descriptor sets is ready.  The delay can be used
1314  * as a timeout on the socket(s) being ready.  The task will be
1315  * scheduled for execution once either the delay has expired or any of
1316  * the socket operations is ready.  This is the most general
1317  * function of the "add" family.  Note that the "prerequisite_task"
1318  * must be satisfied in addition to any of the other conditions.  In
1319  * other words, the task will be started when
1320  * <code>
1321  * (prerequisite-run)
1322  * && (delay-ready
1323  *     || any-rs-ready
1324  *     || any-ws-ready)
1325  * </code>
1326  *
1327  * @param delay how long should we wait?
1328  * @param priority priority to use
1329  * @param rfd file descriptor we want to read (can be -1)
1330  * @param wfd file descriptors we want to write (can be -1)
1331  * @param task main function of the task
1332  * @param task_cls closure of @a task
1333  * @return unique task identifier for the job
1334  *         only valid until @a task is started!
1335  */
1336 #ifndef MINGW
1337 static struct GNUNET_SCHEDULER_Task *
1338 add_without_sets (struct GNUNET_TIME_Relative delay,
1339                   enum GNUNET_SCHEDULER_Priority priority,
1340                   const struct GNUNET_NETWORK_Handle *read_nh,
1341                   const struct GNUNET_NETWORK_Handle *write_nh,
1342                   const struct GNUNET_DISK_FileHandle *read_fh,
1343                   const struct GNUNET_DISK_FileHandle *write_fh,
1344                   GNUNET_SCHEDULER_TaskCallback task,
1345                   void *task_cls)
1346 {
1347   struct GNUNET_SCHEDULER_Task *t;
1348
1349   GNUNET_assert (NULL != active_task);
1350   GNUNET_assert (NULL != task);
1351   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1352   init_fd_info (t,
1353                 &read_nh,
1354                 read_nh ? 1 : 0,
1355                 &write_nh,
1356                 write_nh ? 1 : 0,
1357                 &read_fh,
1358                 read_fh ? 1 : 0,
1359                 &write_fh,
1360                 write_fh ? 1 : 0);
1361   t->callback = task;
1362   t->callback_cls = task_cls;
1363 #if DEBUG_FDS
1364   check_fd (t, NULL != read_nh ? GNUNET_NETWORK_get_fd (read_nh) : -1);
1365   check_fd (t, NULL != write_nh ? GNUNET_NETWORK_get_fd (write_nh) : -1);
1366   check_fd (t, NULL != read_fh ? read_fh->fd : -1);
1367   check_fd (t, NULL != write_fh ? write_fh->fd : -1);
1368 #endif
1369 #if PROFILE_DELAYS
1370   t->start_time = GNUNET_TIME_absolute_get ();
1371 #endif
1372   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1373   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1374   t->lifeness = current_lifeness;
1375   GNUNET_CONTAINER_DLL_insert (pending_head,
1376                                pending_tail,
1377                                t);
1378   driver_add_multiple (t, GNUNET_SCHEDULER_ET_NONE);
1379   max_priority_added = GNUNET_MAX (max_priority_added,
1380                                    t->priority);
1381   init_backtrace (t);
1382   return t;
1383 }
1384 #endif
1385
1386
1387 /**
1388  * Schedule a new task to be run with a specified delay or when the
1389  * specified file descriptor is ready for reading.  The delay can be
1390  * used as a timeout on the socket being ready.  The task will be
1391  * scheduled for execution once either the delay has expired or the
1392  * socket operation is ready.  It will be run with the DEFAULT priority.
1393  * Only allowed to be called as long as the scheduler is running
1394  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1395  * called and has not returned yet).
1396  *
1397  * @param delay when should this operation time out?
1398  * @param rfd read file-descriptor
1399  * @param task main function of the task
1400  * @param task_cls closure of @a task
1401  * @return unique task identifier for the job
1402  *         only valid until @a task is started!
1403  */
1404 struct GNUNET_SCHEDULER_Task *
1405 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1406                                struct GNUNET_NETWORK_Handle *rfd,
1407                                GNUNET_SCHEDULER_TaskCallback task,
1408                                void *task_cls)
1409 {
1410   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1411                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1412                   rfd, task, task_cls);
1413 }
1414
1415
1416 /**
1417  * Schedule a new task to be run with a specified priority and to be
1418  * run after the specified delay or when the specified file descriptor
1419  * is ready for reading.  The delay can be used as a timeout on the
1420  * socket being ready.  The task will be scheduled for execution once
1421  * either the delay has expired or the socket operation is ready.  It
1422  * will be run with the DEFAULT priority.
1423  * Only allowed to be called as long as the scheduler is running
1424  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1425  * called and has not returned yet).
1426  *
1427  * @param delay when should this operation time out?
1428  * @param priority priority to use for the task
1429  * @param rfd read file-descriptor
1430  * @param task main function of the task
1431  * @param task_cls closure of @a task
1432  * @return unique task identifier for the job
1433  *         only valid until @a task is started!
1434  */
1435 struct GNUNET_SCHEDULER_Task *
1436 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1437                enum GNUNET_SCHEDULER_Priority priority,
1438                struct GNUNET_NETWORK_Handle *rfd,
1439                GNUNET_SCHEDULER_TaskCallback task,
1440                                              void *task_cls)
1441 {
1442   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1443                                                  rfd,
1444                                                  GNUNET_YES,
1445                                                  GNUNET_NO,
1446                                                  task, task_cls);
1447 }
1448
1449
1450 /**
1451  * Schedule a new task to be run with a specified delay or when the
1452  * specified file descriptor is ready for writing.  The delay can be
1453  * used as a timeout on the socket being ready.  The task will be
1454  * scheduled for execution once either the delay has expired or the
1455  * socket operation is ready.  It will be run with the priority of
1456  * the calling task.
1457  * Only allowed to be called as long as the scheduler is running
1458  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1459  * called and has not returned yet).
1460  *
1461  * @param delay when should this operation time out?
1462  * @param wfd write file-descriptor
1463  * @param task main function of the task
1464  * @param task_cls closure of @a task
1465  * @return unique task identifier for the job
1466  *         only valid until @a task is started!
1467  */
1468 struct GNUNET_SCHEDULER_Task *
1469 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1470                                 struct GNUNET_NETWORK_Handle *wfd,
1471                                 GNUNET_SCHEDULER_TaskCallback task,
1472                                 void *task_cls)
1473 {
1474   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1475                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1476                                                  wfd,
1477                                                  GNUNET_NO, GNUNET_YES,
1478                                                  task, task_cls);
1479 }
1480
1481 /**
1482  * Schedule a new task to be run with a specified delay or when the
1483  * specified file descriptor is ready.  The delay can be
1484  * used as a timeout on the socket being ready.  The task will be
1485  * scheduled for execution once either the delay has expired or the
1486  * socket operation is ready.
1487  * Only allowed to be called as long as the scheduler is running
1488  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1489  * called and has not returned yet).
1490  *
1491  * @param delay when should this operation time out?
1492  * @param priority priority of the task
1493  * @param fd file-descriptor
1494  * @param on_read whether to poll the file-descriptor for readability
1495  * @param on_write whether to poll the file-descriptor for writability
1496  * @param task main function of the task
1497  * @param task_cls closure of task
1498  * @return unique task identifier for the job
1499  *         only valid until "task" is started!
1500  */
1501 struct GNUNET_SCHEDULER_Task *
1502 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1503                                          enum GNUNET_SCHEDULER_Priority priority,
1504                                          struct GNUNET_NETWORK_Handle *fd,
1505                                          int on_read,
1506                                          int on_write,
1507                                          GNUNET_SCHEDULER_TaskCallback task,
1508                                          void *task_cls)
1509 {
1510   /* scheduler must be running */
1511   GNUNET_assert (NULL != scheduler_driver);
1512
1513 #if MINGW
1514   struct GNUNET_NETWORK_FDSet *s;
1515   struct GNUNET_SCHEDULER_Task * ret;
1516
1517   GNUNET_assert (NULL != fd);
1518   s = GNUNET_NETWORK_fdset_create ();
1519   GNUNET_NETWORK_fdset_set (s, fd);
1520   ret = GNUNET_SCHEDULER_add_select (
1521       priority, delay,
1522       on_read  ? s : NULL,
1523       on_write ? s : NULL,
1524       task, task_cls);
1525   GNUNET_NETWORK_fdset_destroy (s);
1526   return ret;
1527 #else
1528   GNUNET_assert (on_read || on_write);
1529   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1530   return add_without_sets (delay, priority,
1531                            on_read  ? fd : NULL,
1532                            on_write ? fd : NULL,
1533                            NULL,
1534                            NULL,
1535                            task, task_cls);
1536 #endif
1537 }
1538
1539
1540 /**
1541  * Schedule a new task to be run with a specified delay or when the
1542  * specified file descriptor is ready for reading.  The delay can be
1543  * used as a timeout on the socket being ready.  The task will be
1544  * scheduled for execution once either the delay has expired or the
1545  * socket operation is ready. It will be run with the DEFAULT priority.
1546  * Only allowed to be called as long as the scheduler is running
1547  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1548  * called and has not returned yet).
1549  *
1550  * @param delay when should this operation time out?
1551  * @param rfd read file-descriptor
1552  * @param task main function of the task
1553  * @param task_cls closure of @a task
1554  * @return unique task identifier for the job
1555  *         only valid until @a task is started!
1556  */
1557 struct GNUNET_SCHEDULER_Task *
1558 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1559                                 const struct GNUNET_DISK_FileHandle *rfd,
1560                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1561 {
1562   return GNUNET_SCHEDULER_add_file_with_priority (
1563       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1564       rfd, GNUNET_YES, GNUNET_NO,
1565       task, task_cls);
1566 }
1567
1568
1569 /**
1570  * Schedule a new task to be run with a specified delay or when the
1571  * specified file descriptor is ready for writing.  The delay can be
1572  * used as a timeout on the socket being ready.  The task will be
1573  * scheduled for execution once either the delay has expired or the
1574  * socket operation is ready. It will be run with the DEFAULT priority.
1575  * Only allowed to be called as long as the scheduler is running
1576  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1577  * called and has not returned yet).
1578  *
1579  * @param delay when should this operation time out?
1580  * @param wfd write file-descriptor
1581  * @param task main function of the task
1582  * @param task_cls closure of @a task
1583  * @return unique task identifier for the job
1584  *         only valid until @a task is started!
1585  */
1586 struct GNUNET_SCHEDULER_Task *
1587 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1588                                  const struct GNUNET_DISK_FileHandle *wfd,
1589                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1590 {
1591   return GNUNET_SCHEDULER_add_file_with_priority (
1592       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1593       wfd, GNUNET_NO, GNUNET_YES,
1594       task, task_cls);
1595 }
1596
1597
1598 /**
1599  * Schedule a new task to be run with a specified delay or when the
1600  * specified file descriptor is ready.  The delay can be
1601  * used as a timeout on the socket being ready.  The task will be
1602  * scheduled for execution once either the delay has expired or the
1603  * socket operation is ready.
1604  * Only allowed to be called as long as the scheduler is running
1605  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1606  * called and has not returned yet).
1607  *
1608  * @param delay when should this operation time out?
1609  * @param priority priority of the task
1610  * @param fd file-descriptor
1611  * @param on_read whether to poll the file-descriptor for readability
1612  * @param on_write whether to poll the file-descriptor for writability
1613  * @param task main function of the task
1614  * @param task_cls closure of @a task
1615  * @return unique task identifier for the job
1616  *         only valid until @a task is started!
1617  */
1618 struct GNUNET_SCHEDULER_Task *
1619 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1620                                          enum GNUNET_SCHEDULER_Priority priority,
1621                                          const struct GNUNET_DISK_FileHandle *fd,
1622                                          int on_read, int on_write,
1623                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1624 {
1625   /* scheduler must be running */
1626   GNUNET_assert (NULL != scheduler_driver);
1627
1628 #if MINGW
1629   struct GNUNET_NETWORK_FDSet *s;
1630   struct GNUNET_SCHEDULER_Task * ret;
1631
1632   GNUNET_assert (NULL != fd);
1633   s = GNUNET_NETWORK_fdset_create ();
1634   GNUNET_NETWORK_fdset_handle_set (s, fd);
1635   ret = GNUNET_SCHEDULER_add_select (
1636       priority, delay,
1637       on_read  ? s : NULL,
1638       on_write ? s : NULL,
1639       task, task_cls);
1640   GNUNET_NETWORK_fdset_destroy (s);
1641   return ret;
1642 #else
1643   GNUNET_assert (on_read || on_write);
1644   GNUNET_assert (fd->fd >= 0);
1645   return add_without_sets (delay, priority,
1646                            NULL,
1647                            NULL,
1648                            on_read ? fd : NULL,
1649                            on_write ? fd : NULL,
1650                            task, task_cls);
1651 #endif
1652 }
1653
1654
1655 void
1656 extract_handles (struct GNUNET_SCHEDULER_Task *t,
1657                  const struct GNUNET_NETWORK_FDSet *fdset,
1658                  const struct GNUNET_NETWORK_Handle ***ntarget,
1659                  unsigned int *extracted_nhandles,
1660                  const struct GNUNET_DISK_FileHandle ***ftarget,
1661                  unsigned int *extracted_fhandles)
1662 {
1663   // FIXME: this implementation only works for unix, for WIN32 the file handles
1664   // in fdset must be handled separately
1665   const struct GNUNET_NETWORK_Handle **nhandles;
1666   const struct GNUNET_DISK_FileHandle **fhandles;
1667   unsigned int nhandles_len, fhandles_len;
1668   int sock;
1669
1670   nhandles = NULL;
1671   fhandles = NULL;
1672   nhandles_len = 0;
1673   fhandles_len = 0;
1674   for (sock = 0; sock != fdset->nsds; ++sock)
1675   {
1676     if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (fdset, sock))
1677     {
1678       struct GNUNET_NETWORK_Handle *nhandle;
1679       struct GNUNET_DISK_FileHandle *fhandle;
1680
1681       nhandle = GNUNET_NETWORK_socket_box_native (sock);
1682       if (NULL != nhandle)
1683       {
1684         GNUNET_array_append (nhandles, nhandles_len, nhandle);
1685       }
1686       else
1687       {
1688         fhandle = GNUNET_DISK_get_handle_from_int_fd (sock);
1689         if (NULL != fhandle)
1690         {
1691           GNUNET_array_append (fhandles, fhandles_len, fhandle);
1692         }
1693         else
1694         {
1695           GNUNET_assert (0);
1696         }
1697       }
1698     }
1699   }
1700   *ntarget = nhandles_len > 0 ? nhandles : NULL;
1701   *ftarget = fhandles_len > 0 ? fhandles : NULL;
1702   *extracted_nhandles = nhandles_len;
1703   *extracted_fhandles = fhandles_len;
1704 }
1705
1706
1707 /**
1708  * Schedule a new task to be run with a specified delay or when any of
1709  * the specified file descriptor sets is ready.  The delay can be used
1710  * as a timeout on the socket(s) being ready.  The task will be
1711  * scheduled for execution once either the delay has expired or any of
1712  * the socket operations is ready.  This is the most general
1713  * function of the "add" family.  Note that the "prerequisite_task"
1714  * must be satisfied in addition to any of the other conditions.  In
1715  * other words, the task will be started when
1716  * <code>
1717  * (prerequisite-run)
1718  * && (delay-ready
1719  *     || any-rs-ready
1720  *     || any-ws-ready) )
1721  * </code>
1722  * Only allowed to be called as long as the scheduler is running
1723  * (#GNUNET_SCHEDULER_run or #GNUNET_SCHEDULER_run_with_driver has been
1724  * called and has not returned yet).
1725  *
1726  * @param prio how important is this task?
1727  * @param delay how long should we wait?
1728  * @param rs set of file descriptors we want to read (can be NULL)
1729  * @param ws set of file descriptors we want to write (can be NULL)
1730  * @param task main function of the task
1731  * @param task_cls closure of @a task
1732  * @return unique task identifier for the job
1733  *         only valid until @a task is started!
1734  */
1735 struct GNUNET_SCHEDULER_Task *
1736 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1737                              struct GNUNET_TIME_Relative delay,
1738                              const struct GNUNET_NETWORK_FDSet *rs,
1739                              const struct GNUNET_NETWORK_FDSet *ws,
1740                              GNUNET_SCHEDULER_TaskCallback task,
1741                              void *task_cls)
1742 {
1743   struct GNUNET_SCHEDULER_Task *t;
1744   const struct GNUNET_NETWORK_Handle **read_nhandles;
1745   const struct GNUNET_NETWORK_Handle **write_nhandles;
1746   const struct GNUNET_DISK_FileHandle **read_fhandles;
1747   const struct GNUNET_DISK_FileHandle **write_fhandles;
1748   unsigned int read_nhandles_len, write_nhandles_len,
1749                read_fhandles_len, write_fhandles_len;
1750
1751   if (((NULL == rs) && (NULL == ws)) || ((0 == rs->nsds) && (0 == ws->nsds)))
1752     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1753                                                        prio,
1754                                                        task,
1755                                                        task_cls);
1756   /* scheduler must be running */
1757   GNUNET_assert (NULL != scheduler_driver);
1758   GNUNET_assert (NULL != active_task);
1759   GNUNET_assert (NULL != task);
1760   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1761   t->callback = task;
1762   t->callback_cls = task_cls;
1763   t->read_fd = -1;
1764   t->write_fd = -1;
1765   t->own_handles = GNUNET_YES;
1766   read_nhandles = NULL;
1767   write_nhandles = NULL;
1768   read_fhandles = NULL;
1769   write_fhandles = NULL;
1770   read_nhandles_len = 0;
1771   write_nhandles_len = 0;
1772   read_fhandles_len = 0;
1773   write_fhandles_len = 0;
1774   if (NULL != rs)
1775   {
1776     extract_handles (t,
1777                      rs,
1778                      &read_nhandles,
1779                      &read_nhandles_len,
1780                      &read_fhandles,
1781                      &read_fhandles_len);
1782   }
1783   if (NULL != ws)
1784   {
1785     extract_handles (t,
1786                      ws,
1787                      &write_nhandles,
1788                      &write_nhandles_len,
1789                      &write_fhandles,
1790                      &write_fhandles_len);
1791   }
1792   init_fd_info (t,
1793                 read_nhandles,
1794                 read_nhandles_len,
1795                 write_nhandles,
1796                 write_nhandles_len,
1797                 read_fhandles,
1798                 read_fhandles_len,
1799                 write_fhandles,
1800                 write_fhandles_len);
1801   /* free the arrays of pointers to network / file handles, the actual
1802    * handles will be freed in destroy_task */
1803   GNUNET_array_grow (read_nhandles, read_nhandles_len, 0);
1804   GNUNET_array_grow (write_nhandles, write_nhandles_len, 0);
1805   GNUNET_array_grow (read_fhandles, read_fhandles_len, 0);
1806   GNUNET_array_grow (write_fhandles, write_fhandles_len, 0);
1807 #if PROFILE_DELAYS
1808   t->start_time = GNUNET_TIME_absolute_get ();
1809 #endif
1810   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1811   t->priority =
1812       check_priority ((prio ==
1813                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1814                       prio);
1815   t->lifeness = current_lifeness;
1816   GNUNET_CONTAINER_DLL_insert (pending_head,
1817                                pending_tail,
1818                                t);
1819   driver_add_multiple (t, GNUNET_SCHEDULER_ET_NONE);
1820   max_priority_added = GNUNET_MAX (max_priority_added,
1821            t->priority);
1822   LOG (GNUNET_ERROR_TYPE_DEBUG,
1823        "Adding task %p\n",
1824        t);
1825   init_backtrace (t);
1826   return t;
1827 }
1828
1829
1830 /**
1831  * Function used by event-loop implementations to signal the scheduler
1832  * that a particular @a task is ready due to an event specified in the
1833  * et field of @a fdi.
1834  *
1835  * This function will then queue the task to notify the application
1836  * that the task is ready (with the respective priority).
1837  *
1838  * @param task the task that is ready
1839  * @param fdi information about the related FD
1840  */
1841 void
1842 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1843                              struct GNUNET_SCHEDULER_FdInfo *fdi)
1844 {
1845   enum GNUNET_SCHEDULER_Reason reason;
1846   struct GNUNET_TIME_Absolute now;
1847
1848   now = GNUNET_TIME_absolute_get ();
1849   reason = task->reason;
1850   if (now.abs_value_us >= task->timeout.abs_value_us)
1851     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1852   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1853        (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et)) )
1854     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1855   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1856        (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et)) )
1857     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1858   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1859   task->reason = reason;
1860   if (GNUNET_NO == task->in_ready_list)
1861   {
1862     GNUNET_CONTAINER_DLL_remove (pending_head,
1863                                  pending_tail,
1864                                  task);
1865     queue_ready_task (task);
1866   }
1867 }
1868
1869
1870 /**
1871  * Function called by the driver to tell the scheduler to run some of
1872  * the tasks that are ready.  This function may return even though
1873  * there are tasks left to run just to give other tasks a chance as
1874  * well.  If we return #GNUNET_YES, the driver should call this
1875  * function again as soon as possible, while if we return #GNUNET_NO
1876  * it must block until either the operating system has more work (the
1877  * scheduler has no more work to do right now) or the timeout set by
1878  * the scheduler (using the set_wakeup callback) is reached.
1879  *
1880  * @param sh scheduler handle that was given to the `loop`
1881  * @return #GNUNET_OK if there are more tasks that are ready,
1882  *          and thus we would like to run more (yield to avoid
1883  *          blocking other activities for too long)
1884  *         #GNUNET_NO if we are done running tasks (yield to block)
1885  *         #GNUNET_SYSERR on error, e.g. no tasks were ready
1886  */
1887 int
1888 GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh)
1889 {
1890   enum GNUNET_SCHEDULER_Priority p;
1891   struct GNUNET_SCHEDULER_Task *pos;
1892   struct GNUNET_TIME_Absolute now;
1893
1894   /* check for tasks that reached the timeout! */
1895   now = GNUNET_TIME_absolute_get ();
1896   while (NULL != (pos = pending_timeout_head))
1897   {
1898     if (now.abs_value_us >= pos->timeout.abs_value_us)
1899       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1900     if (0 == pos->reason)
1901       break;
1902     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1903                                  pending_timeout_tail,
1904                                  pos);
1905     if (pending_timeout_last == pos)
1906       pending_timeout_last = NULL;
1907     queue_ready_task (pos);
1908   }
1909   pos = pending_head;
1910   while (NULL != pos)
1911   {
1912     struct GNUNET_SCHEDULER_Task *next = pos->next;
1913     if (now.abs_value_us >= pos->timeout.abs_value_us)
1914     {
1915       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1916       GNUNET_CONTAINER_DLL_remove (pending_head,
1917                                    pending_tail,
1918                                    pos);
1919       queue_ready_task (pos);
1920     }
1921     pos = next;
1922   }
1923
1924   if (0 == ready_count)
1925   {
1926     LOG (GNUNET_ERROR_TYPE_ERROR,
1927          "GNUNET_SCHEDULER_run_from_driver was called, but no tasks are ready!\n");
1928     return GNUNET_SYSERR;
1929   }
1930
1931   /* find out which task priority level we are going to
1932      process this time */
1933   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
1934   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
1935   /* yes, p>0 is correct, 0 is "KEEP" which should
1936    * always be an empty queue (see assertion)! */
1937   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
1938   {
1939     pos = ready_head[p];
1940     if (NULL != pos)
1941       break;
1942   }
1943   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
1944
1945   /* process all tasks at this priority level, then yield */
1946   while (NULL != (pos = ready_head[p]))
1947   {
1948     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1949          ready_tail[p],
1950          pos);
1951     ready_count--;
1952     current_priority = pos->priority;
1953     current_lifeness = pos->lifeness;
1954     active_task = pos;
1955 #if PROFILE_DELAYS
1956     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
1957         DELAY_THRESHOLD.rel_value_us)
1958     {
1959       LOG (GNUNET_ERROR_TYPE_DEBUG,
1960            "Task %p took %s to be scheduled\n",
1961            pos,
1962            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
1963                                                    GNUNET_YES));
1964     }
1965 #endif
1966     tc.reason = pos->reason;
1967     GNUNET_NETWORK_fdset_zero (sh->rs);
1968     GNUNET_NETWORK_fdset_zero (sh->ws);
1969     // FIXME: do we have to remove FdInfos from fds if they are not ready?
1970     tc.fds_len = pos->fds_len;
1971     tc.fds = pos->fds;
1972     for (int i = 0; i != pos->fds_len; ++i)
1973     {
1974       struct GNUNET_SCHEDULER_FdInfo *fdi = &pos->fds[i];
1975       if (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et))
1976       {
1977         GNUNET_NETWORK_fdset_set_native (sh->rs,
1978                                          fdi->sock);
1979       }
1980       if (0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et))
1981       {
1982         GNUNET_NETWORK_fdset_set_native (sh->ws,
1983                                          fdi->sock);
1984       }
1985     }
1986     tc.read_ready = sh->rs;
1987     tc.write_ready = sh->ws;
1988     LOG (GNUNET_ERROR_TYPE_DEBUG,
1989          "Running task %p\n",
1990          pos);
1991     GNUNET_assert (NULL != pos->callback);
1992     pos->callback (pos->callback_cls);
1993     if (NULL != pos->fds)
1994     {
1995       int del_result = scheduler_driver->del (scheduler_driver->cls, pos);
1996       if (GNUNET_OK != del_result)
1997       {
1998         LOG (GNUNET_ERROR_TYPE_ERROR,
1999              "driver could not delete task\n");
2000         GNUNET_assert (0);
2001       }
2002     }
2003     active_task = NULL;
2004     dump_backtrace (pos);
2005     destroy_task (pos);
2006     tasks_run++;
2007   }
2008   shutdown_if_no_lifeness ();
2009   if (0 == ready_count)
2010   {
2011     scheduler_driver->set_wakeup (scheduler_driver->cls,
2012                                   get_timeout ());
2013     return GNUNET_NO;
2014   }
2015   scheduler_driver->set_wakeup (scheduler_driver->cls,
2016                                 GNUNET_TIME_absolute_get ()); 
2017   return GNUNET_OK;
2018 }
2019
2020
2021 /**
2022  * Initialize and run scheduler.  This function will return when all
2023  * tasks have completed.  On systems with signals, receiving a SIGTERM
2024  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown
2025  * to be run after the active task is complete.  As a result, SIGTERM
2026  * causes all shutdown tasks to be scheduled with reason
2027  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
2028  * afterwards will execute normally!).  Note that any particular
2029  * signal will only shut down one scheduler; applications should
2030  * always only create a single scheduler.
2031  *
2032  * @param driver drive to use for the event loop
2033  * @param task task to run first (and immediately)
2034  * @param task_cls closure of @a task
2035  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
2036  */
2037 int
2038 GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver,
2039                                   GNUNET_SCHEDULER_TaskCallback task,
2040                                   void *task_cls)
2041 {
2042   int ret;
2043   struct GNUNET_SIGNAL_Context *shc_int;
2044   struct GNUNET_SIGNAL_Context *shc_term;
2045 #if (SIGTERM != GNUNET_TERM_SIG)
2046   struct GNUNET_SIGNAL_Context *shc_gterm;
2047 #endif
2048 #ifndef MINGW
2049   struct GNUNET_SIGNAL_Context *shc_quit;
2050   struct GNUNET_SIGNAL_Context *shc_hup;
2051   struct GNUNET_SIGNAL_Context *shc_pipe;
2052 #endif
2053   struct GNUNET_SCHEDULER_Task tsk;
2054   const struct GNUNET_DISK_FileHandle *pr;
2055
2056   /* general set-up */
2057   GNUNET_assert (NULL == active_task);
2058   GNUNET_assert (NULL == shutdown_pipe_handle);
2059   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
2060                                            GNUNET_NO,
2061                                            GNUNET_NO,
2062                                            GNUNET_NO);
2063   GNUNET_assert (NULL != shutdown_pipe_handle);
2064   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
2065                                 GNUNET_DISK_PIPE_END_READ);
2066   my_pid = getpid ();
2067   scheduler_driver = driver;
2068
2069   /* install signal handlers */
2070   LOG (GNUNET_ERROR_TYPE_DEBUG,
2071        "Registering signal handlers\n");
2072   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
2073              &sighandler_shutdown);
2074   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
2075               &sighandler_shutdown);
2076 #if (SIGTERM != GNUNET_TERM_SIG)
2077   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
2078                &sighandler_shutdown);
2079 #endif
2080 #ifndef MINGW
2081   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
2082               &sighandler_pipe);
2083   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
2084               &sighandler_shutdown);
2085   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
2086              &sighandler_shutdown);
2087 #endif
2088
2089   /* Setup initial tasks */
2090   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
2091   current_lifeness = GNUNET_NO;
2092   memset (&tsk,
2093     0,
2094     sizeof (tsk));
2095   active_task = &tsk;
2096   GNUNET_SCHEDULER_add_now (&GNUNET_OS_install_parent_control_handler,
2097                             NULL);
2098   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
2099                                   pr,
2100                                   &shutdown_cb,
2101                                   NULL);
2102   current_lifeness = GNUNET_YES;
2103   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
2104                                                  task_cls,
2105                                                  GNUNET_SCHEDULER_REASON_STARTUP,
2106                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
2107   active_task = NULL;
2108   scheduler_driver->set_wakeup (scheduler_driver->cls,
2109                                 get_timeout ());
2110   /* begin main event loop */
2111   sh.rs = GNUNET_NETWORK_fdset_create ();
2112   sh.ws = GNUNET_NETWORK_fdset_create ();
2113   GNUNET_NETWORK_fdset_handle_set (sh.rs, pr);
2114   ret = driver->loop (driver->cls,
2115                       &sh);
2116   GNUNET_NETWORK_fdset_destroy (sh.rs);
2117   GNUNET_NETWORK_fdset_destroy (sh.ws);
2118
2119   /* uninstall signal handlers */
2120   GNUNET_SIGNAL_handler_uninstall (shc_int);
2121   GNUNET_SIGNAL_handler_uninstall (shc_term);
2122 #if (SIGTERM != GNUNET_TERM_SIG)
2123   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
2124 #endif
2125 #ifndef MINGW
2126   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
2127   GNUNET_SIGNAL_handler_uninstall (shc_quit);
2128   GNUNET_SIGNAL_handler_uninstall (shc_hup);
2129 #endif
2130   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
2131   shutdown_pipe_handle = NULL;
2132   scheduler_driver = NULL;
2133   return ret;
2134 }
2135
2136
2137 int
2138 select_add (void *cls,
2139             struct GNUNET_SCHEDULER_Task *task,
2140             struct GNUNET_SCHEDULER_FdInfo *fdi)
2141 {
2142   struct DriverContext *context = cls;
2143   GNUNET_assert (NULL != context);
2144   GNUNET_assert (NULL != task);
2145   GNUNET_assert (NULL != fdi);
2146   GNUNET_assert (0 != (GNUNET_SCHEDULER_ET_IN & fdi->et) ||
2147                  0 != (GNUNET_SCHEDULER_ET_OUT & fdi->et));
2148
2149   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (fdi->sock < 0))
2150   {
2151     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
2152     return GNUNET_SYSERR;
2153   }
2154
2155   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
2156   scheduled->task = task;
2157   scheduled->fdi = fdi;
2158   scheduled->et = fdi->et;
2159
2160   GNUNET_CONTAINER_DLL_insert (context->scheduled_head,
2161                                context->scheduled_tail,
2162                                scheduled);
2163   return GNUNET_OK;
2164 }
2165
2166
2167 int
2168 select_del (void *cls,
2169             struct GNUNET_SCHEDULER_Task *task)
2170 {
2171   struct DriverContext *context;
2172   struct Scheduled *pos;
2173   int ret;
2174
2175   GNUNET_assert (NULL != cls);
2176
2177   context = cls;
2178   ret = GNUNET_SYSERR;
2179   pos = context->scheduled_head;
2180   while (NULL != pos)
2181   {
2182     struct Scheduled *next = pos->next;
2183     if (pos->task == task)
2184     {
2185       GNUNET_CONTAINER_DLL_remove (context->scheduled_head,
2186                                    context->scheduled_tail,
2187                                    pos);
2188       GNUNET_free (pos);
2189       ret = GNUNET_OK;
2190     }
2191     pos = next;
2192   }
2193   return ret;
2194 }
2195
2196
2197 int
2198 select_loop (void *cls,
2199              struct GNUNET_SCHEDULER_Handle *sh)
2200 {
2201   struct GNUNET_NETWORK_FDSet *rs;
2202   struct GNUNET_NETWORK_FDSet *ws;
2203   struct DriverContext *context;
2204   int select_result;
2205   int tasks_ready;
2206  
2207   context = cls;
2208   GNUNET_assert (NULL != context);
2209   rs = GNUNET_NETWORK_fdset_create ();
2210   ws = GNUNET_NETWORK_fdset_create ();
2211   tasks_ready = GNUNET_NO;
2212   while (NULL != context->scheduled_head ||
2213          GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us != context->timeout.rel_value_us)
2214   {
2215     LOG (GNUNET_ERROR_TYPE_DEBUG,
2216          "select timeout = %s\n",
2217          GNUNET_STRINGS_relative_time_to_string (context->timeout, GNUNET_NO));
2218
2219     GNUNET_NETWORK_fdset_zero (rs);
2220     GNUNET_NETWORK_fdset_zero (ws);
2221     struct Scheduled *pos;
2222     for (pos = context->scheduled_head; NULL != pos; pos = pos->next)
2223     {
2224       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et))
2225       {
2226         GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2227       }
2228       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et))
2229       {
2230         GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2231       }
2232     }
2233     if (NULL == scheduler_select)
2234     {
2235       select_result = GNUNET_NETWORK_socket_select (rs,
2236                                                     ws,
2237                                                     NULL,
2238                                                     context->timeout);
2239     }
2240     else
2241     {
2242       select_result = scheduler_select (scheduler_select_cls,
2243                                         rs,
2244                                         ws,
2245                                         NULL,
2246                                         context->timeout);
2247     }
2248     if (select_result == GNUNET_SYSERR)
2249     {
2250       if (errno == EINTR)
2251         continue;
2252
2253       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
2254 #ifndef MINGW
2255 #if USE_LSOF
2256       char lsof[512];
2257
2258       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
2259       (void) close (1);
2260       (void) dup2 (2, 1);
2261       if (0 != system (lsof))
2262         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2263                       "system");
2264 #endif
2265 #endif
2266 #if DEBUG_FDS
2267       struct Scheduled *s;
2268       for (s = context->scheduled_head; NULL != s; s = s->next)
2269       {
2270         int flags = fcntl (s->fdi->sock, F_GETFD);
2271         if ((flags == -1) && (errno == EBADF))
2272         {
2273           LOG (GNUNET_ERROR_TYPE_ERROR,
2274                "Got invalid file descriptor %d!\n",
2275                s->fdi->sock);
2276         }
2277       }
2278 #endif
2279       GNUNET_assert (0);
2280       return GNUNET_SYSERR;
2281     }
2282     for (pos = context->scheduled_head; NULL != pos; pos = pos->next)
2283     {
2284       int is_ready = GNUNET_NO;
2285       if (0 != (GNUNET_SCHEDULER_ET_IN & pos->et) &&
2286           GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, pos->fdi->sock))
2287       {
2288         pos->fdi->et |= GNUNET_SCHEDULER_ET_IN;
2289         is_ready = GNUNET_YES;
2290       }
2291       if (0 != (GNUNET_SCHEDULER_ET_OUT & pos->et) &&
2292           GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, pos->fdi->sock))
2293       {
2294         pos->fdi->et |= GNUNET_SCHEDULER_ET_OUT;
2295         is_ready = GNUNET_YES;
2296       }
2297       if (GNUNET_YES == is_ready)
2298       {
2299         GNUNET_SCHEDULER_task_ready (pos->task, pos->fdi);
2300       }
2301     }
2302     tasks_ready = GNUNET_SCHEDULER_run_from_driver (sh);
2303     GNUNET_assert (GNUNET_SYSERR != tasks_ready);
2304   }
2305   return GNUNET_OK; 
2306 }
2307
2308
2309 void
2310 select_set_wakeup(void *cls,
2311                   struct GNUNET_TIME_Absolute dt)
2312 {
2313   struct DriverContext *context = cls;
2314   GNUNET_assert (NULL != context);
2315  
2316   context->timeout = GNUNET_TIME_absolute_get_remaining (dt);
2317 }
2318
2319
2320 /**
2321  * Obtain the driver for using select() as the event loop.
2322  *
2323  * @return NULL on error
2324  */
2325 struct GNUNET_SCHEDULER_Driver *
2326 GNUNET_SCHEDULER_driver_select ()
2327 {
2328   struct GNUNET_SCHEDULER_Driver *select_driver;
2329   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2330
2331   select_driver->loop = &select_loop;
2332   select_driver->add = &select_add;
2333   select_driver->del = &select_del;
2334   select_driver->set_wakeup = &select_set_wakeup;
2335
2336   return select_driver;
2337 }
2338
2339
2340 /* end of scheduler.c */