dcf5aeeaa4e188eb0a24c26faa1e8f5f59788943
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2009-2013 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 /**
22  * @file util/scheduler.c
23  * @brief schedule computations using continuation passing style
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "disk.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
31
32 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
33
34
35 #if HAVE_EXECINFO_H
36 #include "execinfo.h"
37
38 /**
39  * Use lsof to generate file descriptor reports on select error?
40  * (turn off for stable releases).
41  */
42 #define USE_LSOF GNUNET_NO
43
44 /**
45  * Obtain trace information for all scheduler calls that schedule tasks.
46  */
47 #define EXECINFO GNUNET_NO
48
49 /**
50  * Check each file descriptor before adding
51  */
52 #define DEBUG_FDS GNUNET_NO
53
54 /**
55  * Depth of the traces collected via EXECINFO.
56  */
57 #define MAX_TRACE_DEPTH 50
58 #endif
59
60 /**
61  * Should we figure out which tasks are delayed for a while
62  * before they are run? (Consider using in combination with EXECINFO).
63  */
64 #define PROFILE_DELAYS GNUNET_NO
65
66 /**
67  * Task that were in the queue for longer than this are reported if
68  * PROFILE_DELAYS is active.
69  */
70 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
71
72
73 /**
74  * Entry in list of pending tasks.
75  */
76 struct GNUNET_SCHEDULER_Task
77 {
78   /**
79    * This is a linked list.
80    */
81   struct GNUNET_SCHEDULER_Task *next;
82
83   /**
84    * This is a linked list.
85    */
86   struct GNUNET_SCHEDULER_Task *prev;
87
88   /**
89    * Function to run when ready.
90    */
91   GNUNET_SCHEDULER_TaskCallback callback;
92
93   /**
94    * Closure for the @e callback.
95    */
96   void *callback_cls;
97
98   /**
99    * Set of file descriptors this task is waiting
100    * for for reading.  Once ready, this is updated
101    * to reflect the set of file descriptors ready
102    * for operation.
103    */
104   struct GNUNET_NETWORK_FDSet *read_set;
105
106   /**
107    * Set of file descriptors this task is waiting for for writing.
108    * Once ready, this is updated to reflect the set of file
109    * descriptors ready for operation.
110    */
111   struct GNUNET_NETWORK_FDSet *write_set;
112
113   /**
114    * Absolute timeout value for the task, or
115    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
116    */
117   struct GNUNET_TIME_Absolute timeout;
118
119 #if PROFILE_DELAYS
120   /**
121    * When was the task scheduled?
122    */
123   struct GNUNET_TIME_Absolute start_time;
124 #endif
125
126   /**
127    * Why is the task ready?  Set after task is added to ready queue.
128    * Initially set to zero.  All reasons that have already been
129    * satisfied (i.e.  read or write ready) will be set over time.
130    */
131   enum GNUNET_SCHEDULER_Reason reason;
132
133   /**
134    * Task priority.
135    */
136   enum GNUNET_SCHEDULER_Priority priority;
137
138   /**
139    * Set if we only wait for reading from a single FD, otherwise -1.
140    */
141   int read_fd;
142
143   /**
144    * Set if we only wait for writing to a single FD, otherwise -1.
145    */
146   int write_fd;
147
148   /**
149    * Should the existence of this task in the queue be counted as
150    * reason to not shutdown the scheduler?
151    */
152   int lifeness;
153
154   /**
155    * Is this task in the ready list?
156    */
157   int in_ready_list;
158
159 #if EXECINFO
160   /**
161    * Array of strings which make up a backtrace from the point when this
162    * task was scheduled (essentially, who scheduled the task?)
163    */
164   char **backtrace_strings;
165
166   /**
167    * Size of the backtrace_strings array
168    */
169   int num_backtrace_strings;
170 #endif
171
172
173 };
174
175
176 /**
177  * Head of list of tasks waiting for an event.
178  */
179 static struct GNUNET_SCHEDULER_Task *pending_head;
180
181 /**
182  * Tail of list of tasks waiting for an event.
183  */
184 static struct GNUNET_SCHEDULER_Task *pending_tail;
185
186 /**
187  * List of tasks waiting ONLY for a timeout event.
188  * Sorted by timeout (earliest first).  Used so that
189  * we do not traverse the list of these tasks when
190  * building select sets (we just look at the head
191  * to determine the respective timeout ONCE).
192  */
193 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
194
195 /**
196  * List of tasks waiting ONLY for a timeout event.
197  * Sorted by timeout (earliest first).  Used so that
198  * we do not traverse the list of these tasks when
199  * building select sets (we just look at the head
200  * to determine the respective timeout ONCE).
201  */
202 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
203
204 /**
205  * Last inserted task waiting ONLY for a timeout event.
206  * Used to (heuristically) speed up insertion.
207  */
208 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
209
210 /**
211  * ID of the task that is running right now.
212  */
213 static struct GNUNET_SCHEDULER_Task *active_task;
214
215 /**
216  * Head of list of tasks ready to run right now, grouped by importance.
217  */
218 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
219
220 /**
221  * Tail of list of tasks ready to run right now, grouped by importance.
222  */
223 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
224
225 /**
226  * Number of tasks on the ready list.
227  */
228 static unsigned int ready_count;
229
230 /**
231  * How many tasks have we run so far?
232  */
233 static unsigned long long tasks_run;
234
235 /**
236  * Priority of the task running right now.  Only
237  * valid while a task is running.
238  */
239 static enum GNUNET_SCHEDULER_Priority current_priority;
240
241 /**
242  * Priority of the highest task added in the current select
243  * iteration.
244  */
245 static enum GNUNET_SCHEDULER_Priority max_priority_added;
246
247 /**
248  * Value of the 'lifeness' flag for the current task.
249  */
250 static int current_lifeness;
251
252 /**
253  * Function to use as a select() in the scheduler.
254  * If NULL, we use GNUNET_NETWORK_socket_select().
255  */
256 static GNUNET_SCHEDULER_select scheduler_select;
257
258 /**
259  * Task context of the current task.
260  */
261 static struct GNUNET_SCHEDULER_TaskContext tc;
262
263 /**
264  * Closure for #scheduler_select.
265  */
266 static void *scheduler_select_cls;
267
268
269 /**
270  * Sets the select function to use in the scheduler (scheduler_select).
271  *
272  * @param new_select new select function to use
273  * @param new_select_cls closure for @a new_select
274  * @return previously used select function, NULL for default
275  */
276 void
277 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
278                              void *new_select_cls)
279 {
280   scheduler_select = new_select;
281   scheduler_select_cls = new_select_cls;
282 }
283
284
285 /**
286  * Check that the given priority is legal (and return it).
287  *
288  * @param p priority value to check
289  * @return p on success, 0 on error
290  */
291 static enum GNUNET_SCHEDULER_Priority
292 check_priority (enum GNUNET_SCHEDULER_Priority p)
293 {
294   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
295     return p;
296   GNUNET_assert (0);
297   return 0;                     /* make compiler happy */
298 }
299
300
301 /**
302  * Update all sets and timeout for select.
303  *
304  * @param rs read-set, set to all FDs we would like to read (updated)
305  * @param ws write-set, set to all FDs we would like to write (updated)
306  * @param timeout next timeout (updated)
307  */
308 static void
309 update_sets (struct GNUNET_NETWORK_FDSet *rs,
310              struct GNUNET_NETWORK_FDSet *ws,
311              struct GNUNET_TIME_Relative *timeout)
312 {
313   struct GNUNET_SCHEDULER_Task *pos;
314   struct GNUNET_TIME_Absolute now;
315   struct GNUNET_TIME_Relative to;
316
317   now = GNUNET_TIME_absolute_get ();
318   pos = pending_timeout_head;
319   if (NULL != pos)
320   {
321     to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
322     if (timeout->rel_value_us > to.rel_value_us)
323       *timeout = to;
324     if (0 != pos->reason)
325       *timeout = GNUNET_TIME_UNIT_ZERO;
326   }
327   for (pos = pending_head; NULL != pos; pos = pos->next)
328   {
329     if (pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
330     {
331       to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
332       if (timeout->rel_value_us > to.rel_value_us)
333         *timeout = to;
334     }
335     if (-1 != pos->read_fd)
336       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
337     if (-1 != pos->write_fd)
338       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
339     if (NULL != pos->read_set)
340       GNUNET_NETWORK_fdset_add (rs, pos->read_set);
341     if (NULL != pos->write_set)
342       GNUNET_NETWORK_fdset_add (ws, pos->write_set);
343     if (0 != pos->reason)
344       *timeout = GNUNET_TIME_UNIT_ZERO;
345   }
346 }
347
348
349 /**
350  * Check if the ready set overlaps with the set we want to have ready.
351  * If so, update the want set (set all FDs that are ready).  If not,
352  * return #GNUNET_NO.
353  *
354  * @param ready set that is ready
355  * @param want set that we want to be ready
356  * @return #GNUNET_YES if there was some overlap
357  */
358 static int
359 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
360               struct GNUNET_NETWORK_FDSet *want)
361 {
362   if ((NULL == want) || (NULL == ready))
363     return GNUNET_NO;
364   if (GNUNET_NETWORK_fdset_overlap (ready, want))
365   {
366     /* copy all over (yes, there maybe unrelated bits,
367      * but this should not hurt well-written clients) */
368     GNUNET_NETWORK_fdset_copy (want, ready);
369     return GNUNET_YES;
370   }
371   return GNUNET_NO;
372 }
373
374
375 /**
376  * Check if the given task is eligible to run now.
377  * Also set the reason why it is eligible.
378  *
379  * @param task task to check if it is ready
380  * @param now the current time
381  * @param rs set of FDs ready for reading
382  * @param ws set of FDs ready for writing
383  * @return #GNUNET_YES if we can run it, #GNUNET_NO if not.
384  */
385 static int
386 is_ready (struct GNUNET_SCHEDULER_Task *task,
387           struct GNUNET_TIME_Absolute now,
388           const struct GNUNET_NETWORK_FDSet *rs,
389           const struct GNUNET_NETWORK_FDSet *ws)
390 {
391   enum GNUNET_SCHEDULER_Reason reason;
392
393   reason = task->reason;
394   if (now.abs_value_us >= task->timeout.abs_value_us)
395     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
396   if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
397       (((task->read_fd != -1) &&
398         (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd))) ||
399        (set_overlaps (rs, task->read_set))))
400     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
401   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
402       (((task->write_fd != -1) &&
403         (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)))
404        || (set_overlaps (ws, task->write_set))))
405     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
406   if (0 == reason)
407     return GNUNET_NO;           /* not ready */
408   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
409   task->reason = reason;
410   return GNUNET_YES;
411 }
412
413
414 /**
415  * Put a task that is ready for execution into the ready queue.
416  *
417  * @param task task ready for execution
418  */
419 static void
420 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
421 {
422   enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
423
424   if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
425     p = task->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
426   GNUNET_CONTAINER_DLL_insert (ready_head[p],
427                                ready_tail[p],
428                                task);
429   task->in_ready_list = GNUNET_YES;
430   ready_count++;
431 }
432
433
434 /**
435  * Check which tasks are ready and move them
436  * to the respective ready queue.
437  *
438  * @param rs FDs ready for reading
439  * @param ws FDs ready for writing
440  */
441 static void
442 check_ready (const struct GNUNET_NETWORK_FDSet *rs,
443              const struct GNUNET_NETWORK_FDSet *ws)
444 {
445   struct GNUNET_SCHEDULER_Task *pos;
446   struct GNUNET_SCHEDULER_Task *next;
447   struct GNUNET_TIME_Absolute now;
448
449   now = GNUNET_TIME_absolute_get ();
450   while (NULL != (pos = pending_timeout_head))
451   {
452     if (now.abs_value_us >= pos->timeout.abs_value_us)
453       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
454     if (0 == pos->reason)
455       break;
456     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
457                                  pending_timeout_tail,
458                                  pos);
459     if (pending_timeout_last == pos)
460       pending_timeout_last = NULL;
461     queue_ready_task (pos);
462   }
463   pos = pending_head;
464   while (NULL != pos)
465   {
466     next = pos->next;
467     if (GNUNET_YES == is_ready (pos, now, rs, ws))
468     {
469       GNUNET_CONTAINER_DLL_remove (pending_head,
470                                    pending_tail,
471                                    pos);
472       queue_ready_task (pos);
473     }
474     pos = next;
475   }
476 }
477
478
479 /**
480  * Request the shutdown of a scheduler.  Marks all currently
481  * pending tasks as ready because of shutdown.  This will
482  * cause all tasks to run (as soon as possible, respecting
483  * priorities and prerequisite tasks).  Note that tasks
484  * scheduled AFTER this call may still be delayed arbitrarily.
485  *
486  * Note that we don't move the tasks into the ready queue yet;
487  * check_ready() will do that later, possibly adding additional
488  * readiness-factors
489  */
490 void
491 GNUNET_SCHEDULER_shutdown ()
492 {
493   struct GNUNET_SCHEDULER_Task *pos;
494   int i;
495
496   for (pos = pending_timeout_head; NULL != pos; pos = pos->next)
497     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
498   for (pos = pending_head; NULL != pos; pos = pos->next)
499     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
500   for (i = 0; i < GNUNET_SCHEDULER_PRIORITY_COUNT; i++)
501     for (pos = ready_head[i]; NULL != pos; pos = pos->next)
502       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
503 }
504
505
506 /**
507  * Destroy a task (release associated resources)
508  *
509  * @param t task to destroy
510  */
511 static void
512 destroy_task (struct GNUNET_SCHEDULER_Task *t)
513 {
514   if (NULL != t->read_set)
515     GNUNET_NETWORK_fdset_destroy (t->read_set);
516   if (NULL != t->write_set)
517     GNUNET_NETWORK_fdset_destroy (t->write_set);
518 #if EXECINFO
519   GNUNET_free (t->backtrace_strings);
520 #endif
521   GNUNET_free (t);
522 }
523
524
525 /**
526  * Run at least one task in the highest-priority queue that is not
527  * empty.  Keep running tasks until we are either no longer running
528  * "URGENT" tasks or until we have at least one "pending" task (which
529  * may become ready, hence we should select on it).  Naturally, if
530  * there are no more ready tasks, we also return.
531  *
532  * @param rs FDs ready for reading
533  * @param ws FDs ready for writing
534  */
535 static void
536 run_ready (struct GNUNET_NETWORK_FDSet *rs,
537            struct GNUNET_NETWORK_FDSet *ws)
538 {
539   enum GNUNET_SCHEDULER_Priority p;
540   struct GNUNET_SCHEDULER_Task *pos;
541
542   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
543   do
544   {
545     if (0 == ready_count)
546       return;
547     GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
548     /* yes, p>0 is correct, 0 is "KEEP" which should
549      * always be an empty queue (see assertion)! */
550     for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
551     {
552       pos = ready_head[p];
553       if (NULL != pos)
554         break;
555     }
556     GNUNET_assert (NULL != pos);        /* ready_count wrong? */
557     GNUNET_CONTAINER_DLL_remove (ready_head[p],
558                                  ready_tail[p],
559                                  pos);
560     ready_count--;
561     current_priority = pos->priority;
562     current_lifeness = pos->lifeness;
563     active_task = pos;
564 #if PROFILE_DELAYS
565     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
566         DELAY_THRESHOLD.rel_value_us)
567     {
568       LOG (GNUNET_ERROR_TYPE_DEBUG,
569            "Task %p took %s to be scheduled\n",
570            pos,
571            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
572                                                    GNUNET_YES));
573     }
574 #endif
575     tc.reason = pos->reason;
576     tc.read_ready = (NULL == pos->read_set) ? rs : pos->read_set;
577     if ((-1 != pos->read_fd) &&
578         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)))
579       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
580     tc.write_ready = (NULL == pos->write_set) ? ws : pos->write_set;
581     if ((-1 != pos->write_fd) &&
582         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
583       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
584     if ((0 != (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
585         (-1 != pos->write_fd) &&
586         (!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
587       GNUNET_assert (0);          // added to ready in previous select loop!
588     LOG (GNUNET_ERROR_TYPE_DEBUG,
589          "Running task: %p\n",
590          pos);
591     pos->callback (pos->callback_cls);
592 #if EXECINFO
593     unsigned int i;
594
595     for (i = 0; i < pos->num_backtrace_strings; i++)
596       LOG (GNUNET_ERROR_TYPE_DEBUG,
597            "Task %p trace %u: %s\n",
598            pos,
599            i,
600            pos->backtrace_strings[i]);
601 #endif
602     active_task = NULL;
603     destroy_task (pos);
604     tasks_run++;
605   }
606   while ((NULL == pending_head) || (p >= max_priority_added));
607 }
608
609
610 /**
611  * Pipe used to communicate shutdown via signal.
612  */
613 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
614
615 /**
616  * Process ID of this process at the time we installed the various
617  * signal handlers.
618  */
619 static pid_t my_pid;
620
621 /**
622  * Signal handler called for SIGPIPE.
623  */
624 #ifndef MINGW
625 static void
626 sighandler_pipe ()
627 {
628   return;
629 }
630 #endif
631
632
633 /**
634  * Wait for a short time.
635  * Sleeps for @a ms ms (as that should be long enough for virtually all
636  * modern systems to context switch and allow another process to do
637  * some 'real' work).
638  *
639  * @param ms how many ms to wait
640  */
641 static void
642 short_wait (unsigned int ms)
643 {
644   struct GNUNET_TIME_Relative timeout;
645
646   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
647   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
648 }
649
650
651 /**
652  * Signal handler called for signals that should cause us to shutdown.
653  */
654 static void
655 sighandler_shutdown ()
656 {
657   static char c;
658   int old_errno = errno;        /* backup errno */
659
660   if (getpid () != my_pid)
661     exit (1);                   /* we have fork'ed since the signal handler was created,
662                                  * ignore the signal, see https://gnunet.org/vfork discussion */
663   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
664                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
665                           &c, sizeof (c));
666   errno = old_errno;
667 }
668
669
670 /**
671  * Check if the system is still life. Trigger shutdown if we
672  * have tasks, but none of them give us lifeness.
673  *
674  * @return #GNUNET_OK to continue the main loop,
675  *         #GNUNET_NO to exit
676  */
677 static int
678 check_lifeness ()
679 {
680   struct GNUNET_SCHEDULER_Task *t;
681
682   if (ready_count > 0)
683     return GNUNET_OK;
684   for (t = pending_head; NULL != t; t = t->next)
685     if (t->lifeness == GNUNET_YES)
686       return GNUNET_OK;
687   for (t = pending_timeout_head; NULL != t; t = t->next)
688     if (t->lifeness == GNUNET_YES)
689       return GNUNET_OK;
690   if ((NULL != pending_head) || (NULL != pending_timeout_head))
691   {
692     GNUNET_SCHEDULER_shutdown ();
693     return GNUNET_OK;
694   }
695   return GNUNET_NO;
696 }
697
698
699 /**
700  * Initialize and run scheduler.  This function will return when all
701  * tasks have completed.  On systems with signals, receiving a SIGTERM
702  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
703  * to be run after the active task is complete.  As a result, SIGTERM
704  * causes all active tasks to be scheduled with reason
705  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
706  * afterwards will execute normally!). Note that any particular signal
707  * will only shut down one scheduler; applications should always only
708  * create a single scheduler.
709  *
710  * @param task task to run immediately
711  * @param task_cls closure of @a task
712  */
713 void
714 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
715                       void *task_cls)
716 {
717   struct GNUNET_NETWORK_FDSet *rs;
718   struct GNUNET_NETWORK_FDSet *ws;
719   struct GNUNET_TIME_Relative timeout;
720   int ret;
721   struct GNUNET_SIGNAL_Context *shc_int;
722   struct GNUNET_SIGNAL_Context *shc_term;
723 #if (SIGTERM != GNUNET_TERM_SIG)
724   struct GNUNET_SIGNAL_Context *shc_gterm;
725 #endif
726
727 #ifndef MINGW
728   struct GNUNET_SIGNAL_Context *shc_quit;
729   struct GNUNET_SIGNAL_Context *shc_hup;
730   struct GNUNET_SIGNAL_Context *shc_pipe;
731 #endif
732   unsigned long long last_tr;
733   unsigned int busy_wait_warning;
734   const struct GNUNET_DISK_FileHandle *pr;
735   char c;
736
737   GNUNET_assert (NULL == active_task);
738   rs = GNUNET_NETWORK_fdset_create ();
739   ws = GNUNET_NETWORK_fdset_create ();
740   GNUNET_assert (NULL == shutdown_pipe_handle);
741   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
742                                            GNUNET_NO,
743                                            GNUNET_NO,
744                                            GNUNET_NO);
745   GNUNET_assert (NULL != shutdown_pipe_handle);
746   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
747                                 GNUNET_DISK_PIPE_END_READ);
748   GNUNET_assert (NULL != pr);
749   my_pid = getpid ();
750   LOG (GNUNET_ERROR_TYPE_DEBUG,
751        "Registering signal handlers\n");
752   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
753   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
754 #if (SIGTERM != GNUNET_TERM_SIG)
755   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG, &sighandler_shutdown);
756 #endif
757 #ifndef MINGW
758   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
759   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
760   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
761 #endif
762   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
763   current_lifeness = GNUNET_YES;
764   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
765                                                  task_cls,
766                                                  GNUNET_SCHEDULER_REASON_STARTUP,
767                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
768   active_task = (void *) (long) -1;     /* force passing of sanity check */
769   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
770                                           &GNUNET_OS_install_parent_control_handler,
771                                           NULL);
772   active_task = NULL;
773   last_tr = 0;
774   busy_wait_warning = 0;
775   while (GNUNET_OK == check_lifeness ())
776   {
777     GNUNET_NETWORK_fdset_zero (rs);
778     GNUNET_NETWORK_fdset_zero (ws);
779     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
780     update_sets (rs, ws, &timeout);
781     GNUNET_NETWORK_fdset_handle_set (rs, pr);
782     if (ready_count > 0)
783     {
784       /* no blocking, more work already ready! */
785       timeout = GNUNET_TIME_UNIT_ZERO;
786     }
787     if (NULL == scheduler_select)
788       ret = GNUNET_NETWORK_socket_select (rs,
789                                           ws,
790                                           NULL,
791                                           timeout);
792     else
793       ret = scheduler_select (scheduler_select_cls,
794                               rs,
795                               ws,
796                               NULL,
797                               timeout);
798     if (ret == GNUNET_SYSERR)
799     {
800       if (errno == EINTR)
801         continue;
802
803       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
804 #ifndef MINGW
805 #if USE_LSOF
806       char lsof[512];
807
808       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
809       (void) close (1);
810       (void) dup2 (2, 1);
811       if (0 != system (lsof))
812         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
813                       "system");
814 #endif
815 #endif
816 #if DEBUG_FDS
817       struct GNUNET_SCHEDULER_Task *t;
818
819       for (t = pending_head; NULL != t; t = t->next)
820       {
821         if (-1 != t->read_fd)
822         {
823           int flags = fcntl (t->read_fd, F_GETFD);
824           if ((flags == -1) && (errno == EBADF))
825             {
826               LOG (GNUNET_ERROR_TYPE_ERROR,
827                    "Got invalid file descriptor %d!\n",
828                    t->read_fd);
829 #if EXECINFO
830               unsigned int i;
831
832               for (i = 0; i < t->num_backtrace_strings; i++)
833                 LOG (GNUNET_ERROR_TYPE_ERROR,
834                      "Trace: %s\n",
835                      t->backtrace_strings[i]);
836 #endif
837             }
838         }
839         if (-1 != t->write_fd)
840           {
841             int flags = fcntl (t->write_fd, F_GETFD);
842             if ((flags == -1) && (errno == EBADF))
843               {
844                 LOG (GNUNET_ERROR_TYPE_ERROR,
845                      "Got invalid file descriptor %d!\n",
846                      t->write_fd);
847 #if EXECINFO
848                 unsigned int i;
849
850                 for (i = 0; i < t->num_backtrace_strings; i++)
851                   LOG (GNUNET_ERROR_TYPE_DEBUG,
852                        "Trace: %s\n",
853                        t->backtrace_strings[i]);
854 #endif
855               }
856           }
857       }
858 #endif
859       GNUNET_assert (0);
860       break;
861     }
862
863     if ( (0 == ret) &&
864          (0 == timeout.rel_value_us) &&
865          (busy_wait_warning > 16) )
866     {
867       LOG (GNUNET_ERROR_TYPE_WARNING,
868            "Looks like we're busy waiting...\n");
869       short_wait (100);                /* mitigate */
870     }
871     check_ready (rs, ws);
872     run_ready (rs, ws);
873     if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
874     {
875       /* consume the signal */
876       GNUNET_DISK_file_read (pr, &c, sizeof (c));
877       /* mark all active tasks as ready due to shutdown */
878       GNUNET_SCHEDULER_shutdown ();
879     }
880     if (last_tr == tasks_run)
881     {
882       short_wait (1);
883       busy_wait_warning++;
884     }
885     else
886     {
887       last_tr = tasks_run;
888       busy_wait_warning = 0;
889     }
890   }
891   GNUNET_SIGNAL_handler_uninstall (shc_int);
892   GNUNET_SIGNAL_handler_uninstall (shc_term);
893 #if (SIGTERM != GNUNET_TERM_SIG)
894   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
895 #endif
896 #ifndef MINGW
897   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
898   GNUNET_SIGNAL_handler_uninstall (shc_quit);
899   GNUNET_SIGNAL_handler_uninstall (shc_hup);
900 #endif
901   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
902   shutdown_pipe_handle = NULL;
903   GNUNET_NETWORK_fdset_destroy (rs);
904   GNUNET_NETWORK_fdset_destroy (ws);
905 }
906
907
908 /**
909  * Obtain the task context, giving the reason why the current task was
910  * started.
911  *
912  * @return current tasks' scheduler context
913  */
914 const struct GNUNET_SCHEDULER_TaskContext *
915 GNUNET_SCHEDULER_get_task_context ()
916 {
917   GNUNET_assert (NULL != active_task);
918   return &tc;
919 }
920
921
922 /**
923  * Get information about the current load of this scheduler.  Use this
924  * function to determine if an elective task should be added or simply
925  * dropped (if the decision should be made based on the number of
926  * tasks ready to run).
927  *
928  * @param p priority level to look at
929  * @return number of tasks pending right now
930  */
931 unsigned int
932 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
933 {
934   struct GNUNET_SCHEDULER_Task *pos;
935   unsigned int ret;
936
937   GNUNET_assert (NULL != active_task);
938   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
939     return ready_count;
940   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
941     p = current_priority;
942   ret = 0;
943   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
944     ret++;
945   return ret;
946 }
947
948
949 /**
950  * Cancel the task with the specified identifier.
951  * The task must not yet have run.
952  *
953  * @param task id of the task to cancel
954  * @return original closure of the task
955  */
956 void *
957 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
958 {
959   enum GNUNET_SCHEDULER_Priority p;
960   void *ret;
961
962   GNUNET_assert (NULL != active_task);
963   if (! task->in_ready_list)
964   {
965     if ( (-1 == task->read_fd) &&
966          (-1 == task->write_fd) &&
967          (NULL == task->read_set) &&
968          (NULL == task->write_set) )
969     {
970       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
971                                    pending_timeout_tail,
972                                    task);
973       if (task == pending_timeout_last)
974         pending_timeout_last = NULL;
975     }
976     else
977     {
978       GNUNET_CONTAINER_DLL_remove (pending_head,
979                                    pending_tail,
980                                    task);
981     }
982   }
983   else
984   {
985     p = check_priority (task->priority);
986     GNUNET_CONTAINER_DLL_remove (ready_head[p],
987                                  ready_tail[p],
988                                  task);
989     ready_count--;
990   }
991   ret = task->callback_cls;
992   LOG (GNUNET_ERROR_TYPE_DEBUG,
993        "Canceling task %p\n",
994        task);
995   destroy_task (task);
996   return ret;
997 }
998
999
1000 /**
1001  * Continue the current execution with the given function.  This is
1002  * similar to the other "add" functions except that there is no delay
1003  * and the reason code can be specified.
1004  *
1005  * @param task main function of the task
1006  * @param task_cls closure for @a task
1007  * @param reason reason for task invocation
1008  * @param priority priority to use for the task
1009  */
1010 void
1011 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1012                                                void *task_cls,
1013                                                enum GNUNET_SCHEDULER_Reason reason,
1014                                                enum GNUNET_SCHEDULER_Priority priority)
1015 {
1016   struct GNUNET_SCHEDULER_Task *t;
1017
1018 #if EXECINFO
1019   void *backtrace_array[50];
1020 #endif
1021
1022   GNUNET_assert (NULL != task);
1023   GNUNET_assert ((NULL != active_task) ||
1024                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1025   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1026 #if EXECINFO
1027   t->num_backtrace_strings = backtrace (backtrace_array, 50);
1028   t->backtrace_strings =
1029       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1030 #endif
1031   t->read_fd = -1;
1032   t->write_fd = -1;
1033   t->callback = task;
1034   t->callback_cls = task_cls;
1035 #if PROFILE_DELAYS
1036   t->start_time = GNUNET_TIME_absolute_get ();
1037 #endif
1038   t->reason = reason;
1039   t->priority = priority;
1040   t->lifeness = current_lifeness;
1041   LOG (GNUNET_ERROR_TYPE_DEBUG,
1042        "Adding continuation task %p\n",
1043        t);
1044   queue_ready_task (t);
1045 }
1046
1047
1048 /**
1049  * Schedule a new task to be run with a specified delay.  The task
1050  * will be scheduled for execution once the delay has expired.
1051  *
1052  * @param delay when should this operation time out? Use
1053  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1054  * @param priority priority to use for the task
1055  * @param task main function of the task
1056  * @param task_cls closure of @a task
1057  * @return unique task identifier for the job
1058  *         only valid until @a task is started!
1059  */
1060 struct GNUNET_SCHEDULER_Task *
1061 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1062                                             enum GNUNET_SCHEDULER_Priority priority,
1063                                             GNUNET_SCHEDULER_TaskCallback task,
1064                                             void *task_cls)
1065 {
1066   struct GNUNET_SCHEDULER_Task *t;
1067   struct GNUNET_SCHEDULER_Task *pos;
1068   struct GNUNET_SCHEDULER_Task *prev;
1069
1070 #if EXECINFO
1071   void *backtrace_array[MAX_TRACE_DEPTH];
1072 #endif
1073
1074   GNUNET_assert (NULL != active_task);
1075   GNUNET_assert (NULL != task);
1076   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1077   t->callback = task;
1078   t->callback_cls = task_cls;
1079 #if EXECINFO
1080   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1081   t->backtrace_strings =
1082       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1083 #endif
1084   t->read_fd = -1;
1085   t->write_fd = -1;
1086 #if PROFILE_DELAYS
1087   t->start_time = GNUNET_TIME_absolute_get ();
1088 #endif
1089   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1090   t->priority = priority;
1091   t->lifeness = current_lifeness;
1092   /* try tail first (optimization in case we are
1093    * appending to a long list of tasks with timeouts) */
1094   if (0 == delay.rel_value_us)
1095   {
1096     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1097                                  pending_timeout_tail,
1098                                  t);
1099   }
1100   else
1101   {
1102     /* first move from heuristic start backwards to before start time */
1103     prev = pending_timeout_last;
1104     while ( (NULL != prev) &&
1105             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1106       prev = prev->prev;
1107     /* now, move from heuristic start (or head of list) forward to insertion point */
1108     if (NULL == prev)
1109       pos = pending_timeout_head;
1110     else
1111       pos = prev->next;
1112     while ( (NULL != pos) &&
1113             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1114               (0 != pos->reason) ) )
1115     {
1116       prev = pos;
1117       pos = pos->next;
1118     }
1119     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1120                                        pending_timeout_tail,
1121                                        prev,
1122                                        t);
1123     /* finally, update heuristic insertion point to last insertion... */
1124     pending_timeout_last = t;
1125   }
1126
1127   LOG (GNUNET_ERROR_TYPE_DEBUG,
1128        "Adding task: %p\n",
1129        t);
1130 #if EXECINFO
1131   unsigned int i;
1132
1133   for (i = 0; i < t->num_backtrace_strings; i++)
1134     LOG (GNUNET_ERROR_TYPE_DEBUG,
1135          "Task %p trace %d: %s\n",
1136          t,
1137          i,
1138          t->backtrace_strings[i]);
1139 #endif
1140   return t;
1141 }
1142
1143
1144 /**
1145  * Schedule a new task to be run with a specified priority.
1146  *
1147  * @param prio how important is the new task?
1148  * @param task main function of the task
1149  * @param task_cls closure of @a task
1150  * @return unique task identifier for the job
1151  *         only valid until @a task is started!
1152  */
1153 struct GNUNET_SCHEDULER_Task *
1154 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1155                                     GNUNET_SCHEDULER_TaskCallback task,
1156                                     void *task_cls)
1157 {
1158   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1159                                                      prio,
1160                                                      task,
1161                                                      task_cls);
1162 }
1163
1164
1165 /**
1166  * Schedule a new task to be run with a specified delay.  The task
1167  * will be scheduled for execution once the delay has expired. It
1168  * will be run with the DEFAULT priority.
1169  *
1170  * @param delay when should this operation time out? Use
1171  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1172  * @param task main function of the task
1173  * @param task_cls closure of task
1174  * @return unique task identifier for the job
1175  *         only valid until "task" is started!
1176  */
1177 struct GNUNET_SCHEDULER_Task *
1178 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1179                               GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1180 {
1181   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1182                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1183                                                      task, task_cls);
1184 }
1185
1186
1187 /**
1188  * Schedule a new task to be run as soon as possible.  Note that this
1189  * does not guarantee that this will be the next task that is being
1190  * run, as other tasks with higher priority (or that are already ready
1191  * to run) might get to run first.  Just as with delays, clients must
1192  * not rely on any particular order of execution between tasks
1193  * scheduled concurrently.
1194  *
1195  * The task will be run with the DEFAULT priority.
1196  *
1197  * @param task main function of the task
1198  * @param task_cls closure of @a task
1199  * @return unique task identifier for the job
1200  *         only valid until "task" is started!
1201  */
1202 struct GNUNET_SCHEDULER_Task *
1203 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1204 {
1205   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO, task, task_cls);
1206 }
1207
1208
1209 /**
1210  * Schedule a new task to be run as soon as possible with the
1211  * (transitive) ignore-shutdown flag either explicitly set or
1212  * explicitly enabled.  This task (and all tasks created from it,
1213  * other than by another call to this function) will either count or
1214  * not count for the "lifeness" of the process.  This API is only
1215  * useful in a few special cases.
1216  *
1217  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1218  * @param task main function of the task
1219  * @param task_cls closure of @a task
1220  * @return unique task identifier for the job
1221  *         only valid until @a task is started!
1222  */
1223 struct GNUNET_SCHEDULER_Task *
1224 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1225                                         GNUNET_SCHEDULER_TaskCallback task,
1226                                         void *task_cls)
1227 {
1228   struct GNUNET_SCHEDULER_Task *ret;
1229
1230   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1231   ret->lifeness = lifeness;
1232   return ret;
1233 }
1234
1235
1236 /**
1237  * Schedule a new task to be run with a specified delay or when any of
1238  * the specified file descriptor sets is ready.  The delay can be used
1239  * as a timeout on the socket(s) being ready.  The task will be
1240  * scheduled for execution once either the delay has expired or any of
1241  * the socket operations is ready.  This is the most general
1242  * function of the "add" family.  Note that the "prerequisite_task"
1243  * must be satisfied in addition to any of the other conditions.  In
1244  * other words, the task will be started when
1245  * <code>
1246  * (prerequisite-run)
1247  * && (delay-ready
1248  *     || any-rs-ready
1249  *     || any-ws-ready
1250  *     || shutdown-active )
1251  * </code>
1252  *
1253  * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1254  *        which means that the task will only be run after we receive SIGTERM
1255  * @param priority priority to use
1256  * @param rfd file descriptor we want to read (can be -1)
1257  * @param wfd file descriptors we want to write (can be -1)
1258  * @param task main function of the task
1259  * @param task_cls closure of @a task
1260  * @return unique task identifier for the job
1261  *         only valid until @a task is started!
1262  */
1263 #ifndef MINGW
1264 static struct GNUNET_SCHEDULER_Task *
1265 add_without_sets (struct GNUNET_TIME_Relative delay,
1266                   enum GNUNET_SCHEDULER_Priority priority,
1267                   int rfd,
1268                   int wfd,
1269                   GNUNET_SCHEDULER_TaskCallback task,
1270                   void *task_cls)
1271 {
1272   struct GNUNET_SCHEDULER_Task *t;
1273
1274 #if EXECINFO
1275   void *backtrace_array[MAX_TRACE_DEPTH];
1276 #endif
1277
1278   GNUNET_assert (NULL != active_task);
1279   GNUNET_assert (NULL != task);
1280   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1281   t->callback = task;
1282   t->callback_cls = task_cls;
1283 #if EXECINFO
1284   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1285   t->backtrace_strings =
1286       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1287 #endif
1288 #if DEBUG_FDS
1289   if (-1 != rfd)
1290   {
1291     int flags = fcntl (rfd, F_GETFD);
1292
1293     if ((flags == -1) && (errno == EBADF))
1294     {
1295       LOG (GNUNET_ERROR_TYPE_ERROR,
1296            "Got invalid file descriptor %d!\n",
1297            rfd);
1298 #if EXECINFO
1299       unsigned int i;
1300
1301       for (i = 0; i < t->num_backtrace_strings; i++)
1302         LOG (GNUNET_ERROR_TYPE_ERROR,
1303              "Trace: %s\n",
1304              t->backtrace_strings[i]);
1305 #endif
1306       GNUNET_assert (0);
1307     }
1308   }
1309   if (-1 != wfd)
1310   {
1311     int flags = fcntl (wfd, F_GETFD);
1312
1313     if (flags == -1 && errno == EBADF)
1314     {
1315       LOG (GNUNET_ERROR_TYPE_ERROR,
1316            "Got invalid file descriptor %d!\n",
1317            wfd);
1318 #if EXECINFO
1319       unsigned int i;
1320
1321       for (i = 0; i < t->num_backtrace_strings; i++)
1322         LOG (GNUNET_ERROR_TYPE_DEBUG,
1323              "Trace: %s\n",
1324              t->backtrace_strings[i]);
1325 #endif
1326       GNUNET_assert (0);
1327     }
1328   }
1329 #endif
1330   t->read_fd = rfd;
1331   GNUNET_assert (wfd >= -1);
1332   t->write_fd = wfd;
1333 #if PROFILE_DELAYS
1334   t->start_time = GNUNET_TIME_absolute_get ();
1335 #endif
1336   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1337   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1338   t->lifeness = current_lifeness;
1339   GNUNET_CONTAINER_DLL_insert (pending_head,
1340                                pending_tail,
1341                                t);
1342   max_priority_added = GNUNET_MAX (max_priority_added,
1343                                    t->priority);
1344   LOG (GNUNET_ERROR_TYPE_DEBUG,
1345        "Adding task %p\n",
1346        t);
1347 #if EXECINFO
1348   unsigned int i;
1349
1350   for (i = 0; i < t->num_backtrace_strings; i++)
1351     LOG (GNUNET_ERROR_TYPE_DEBUG,
1352          "Task %p trace %d: %s\n",
1353          t,
1354          i,
1355          t->backtrace_strings[i]);
1356 #endif
1357   return t;
1358 }
1359 #endif
1360
1361
1362 /**
1363  * Schedule a new task to be run with a specified delay or when the
1364  * specified file descriptor is ready for reading.  The delay can be
1365  * used as a timeout on the socket being ready.  The task will be
1366  * scheduled for execution once either the delay has expired or the
1367  * socket operation is ready.  It will be run with the DEFAULT priority.
1368  *
1369  * @param delay when should this operation time out? Use
1370  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1371  * @param rfd read file-descriptor
1372  * @param task main function of the task
1373  * @param task_cls closure of @a task
1374  * @return unique task identifier for the job
1375  *         only valid until @a task is started!
1376  */
1377 struct GNUNET_SCHEDULER_Task *
1378 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1379                                struct GNUNET_NETWORK_Handle *rfd,
1380                                GNUNET_SCHEDULER_TaskCallback task,
1381                                void *task_cls)
1382 {
1383   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1384                                                       GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1385                                                       rfd, task, task_cls);
1386 }
1387
1388
1389 /**
1390  * Schedule a new task to be run with a specified priority and to be
1391  * run after the specified delay or when the specified file descriptor
1392  * is ready for reading.  The delay can be used as a timeout on the
1393  * socket being ready.  The task will be scheduled for execution once
1394  * either the delay has expired or the socket operation is ready.  It
1395  * will be run with the DEFAULT priority.
1396  *
1397  * @param delay when should this operation time out? Use
1398  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1399  * @param priority priority to use for the task
1400  * @param rfd read file-descriptor
1401  * @param task main function of the task
1402  * @param task_cls closure of @a task
1403  * @return unique task identifier for the job
1404  *         only valid until @a task is started!
1405  */
1406 struct GNUNET_SCHEDULER_Task *
1407 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1408                                              enum GNUNET_SCHEDULER_Priority priority,
1409                                              struct GNUNET_NETWORK_Handle *rfd,
1410                                              GNUNET_SCHEDULER_TaskCallback task,
1411                                              void *task_cls)
1412 {
1413   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1414                                                  rfd,
1415                                                  GNUNET_YES,
1416                                                  GNUNET_NO,
1417                                                  task, task_cls);
1418 }
1419
1420
1421 /**
1422  * Schedule a new task to be run with a specified delay or when the
1423  * specified file descriptor is ready for writing.  The delay can be
1424  * used as a timeout on the socket being ready.  The task will be
1425  * scheduled for execution once either the delay has expired or the
1426  * socket operation is ready.  It will be run with the priority of
1427  * the calling task.
1428  *
1429  * @param delay when should this operation time out? Use
1430  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1431  * @param wfd write file-descriptor
1432  * @param task main function of the task
1433  * @param task_cls closure of @a task
1434  * @return unique task identifier for the job
1435  *         only valid until @a task is started!
1436  */
1437 struct GNUNET_SCHEDULER_Task *
1438 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1439                                 struct GNUNET_NETWORK_Handle *wfd,
1440                                 GNUNET_SCHEDULER_TaskCallback task,
1441                                 void *task_cls)
1442 {
1443   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1444                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1445                                                  wfd,
1446                                                  GNUNET_NO, GNUNET_YES,
1447                                                  task, task_cls);
1448 }
1449
1450 /**
1451  * Schedule a new task to be run with a specified delay or when the
1452  * specified file descriptor is ready.  The delay can be
1453  * used as a timeout on the socket being ready.  The task will be
1454  * scheduled for execution once either the delay has expired or the
1455  * socket operation is ready.
1456  *
1457  * @param delay when should this operation time out? Use
1458  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1459  * @param priority priority of the task
1460  * @param fd file-descriptor
1461  * @param on_read whether to poll the file-descriptor for readability
1462  * @param on_write whether to poll the file-descriptor for writability
1463  * @param task main function of the task
1464  * @param task_cls closure of task
1465  * @return unique task identifier for the job
1466  *         only valid until "task" is started!
1467  */
1468 struct GNUNET_SCHEDULER_Task *
1469 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1470                                          enum GNUNET_SCHEDULER_Priority priority,
1471                                          struct GNUNET_NETWORK_Handle *fd,
1472                                          int on_read,
1473                                          int on_write,
1474                                          GNUNET_SCHEDULER_TaskCallback task,
1475                                          void *task_cls)
1476 {
1477 #if MINGW
1478   struct GNUNET_NETWORK_FDSet *s;
1479   struct GNUNET_SCHEDULER_Task * ret;
1480
1481   GNUNET_assert (NULL != fd);
1482   s = GNUNET_NETWORK_fdset_create ();
1483   GNUNET_NETWORK_fdset_set (s, fd);
1484   ret = GNUNET_SCHEDULER_add_select (
1485       priority, delay,
1486       on_read  ? s : NULL,
1487       on_write ? s : NULL,
1488       task, task_cls);
1489   GNUNET_NETWORK_fdset_destroy (s);
1490   return ret;
1491 #else
1492   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1493   return add_without_sets (delay, priority,
1494                            on_read  ? GNUNET_NETWORK_get_fd (fd) : -1,
1495                            on_write ? GNUNET_NETWORK_get_fd (fd) : -1,
1496                            task, task_cls);
1497 #endif
1498 }
1499
1500
1501 /**
1502  * Schedule a new task to be run with a specified delay or when the
1503  * specified file descriptor is ready for reading.  The delay can be
1504  * used as a timeout on the socket being ready.  The task will be
1505  * scheduled for execution once either the delay has expired or the
1506  * socket operation is ready. It will be run with the DEFAULT priority.
1507  *
1508  * @param delay when should this operation time out? Use
1509  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1510  * @param rfd read file-descriptor
1511  * @param task main function of the task
1512  * @param task_cls closure of @a task
1513  * @return unique task identifier for the job
1514  *         only valid until @a task is started!
1515  */
1516 struct GNUNET_SCHEDULER_Task *
1517 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1518                                 const struct GNUNET_DISK_FileHandle *rfd,
1519                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1520 {
1521   return GNUNET_SCHEDULER_add_file_with_priority (
1522       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1523       rfd, GNUNET_YES, GNUNET_NO,
1524       task, task_cls);
1525 }
1526
1527
1528 /**
1529  * Schedule a new task to be run with a specified delay or when the
1530  * specified file descriptor is ready for writing.  The delay can be
1531  * used as a timeout on the socket being ready.  The task will be
1532  * scheduled for execution once either the delay has expired or the
1533  * socket operation is ready. It will be run with the DEFAULT priority.
1534  *
1535  * @param delay when should this operation time out? Use
1536  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1537  * @param wfd write file-descriptor
1538  * @param task main function of the task
1539  * @param task_cls closure of @a task
1540  * @return unique task identifier for the job
1541  *         only valid until @a task is started!
1542  */
1543 struct GNUNET_SCHEDULER_Task *
1544 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1545                                  const struct GNUNET_DISK_FileHandle *wfd,
1546                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1547 {
1548   return GNUNET_SCHEDULER_add_file_with_priority (
1549       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1550       wfd, GNUNET_NO, GNUNET_YES,
1551       task, task_cls);
1552 }
1553
1554
1555 /**
1556  * Schedule a new task to be run with a specified delay or when the
1557  * specified file descriptor is ready.  The delay can be
1558  * used as a timeout on the socket being ready.  The task will be
1559  * scheduled for execution once either the delay has expired or the
1560  * socket operation is ready.
1561  *
1562  * @param delay when should this operation time out? Use
1563  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1564  * @param priority priority of the task
1565  * @param fd file-descriptor
1566  * @param on_read whether to poll the file-descriptor for readability
1567  * @param on_write whether to poll the file-descriptor for writability
1568  * @param task main function of the task
1569  * @param task_cls closure of @a task
1570  * @return unique task identifier for the job
1571  *         only valid until @a task is started!
1572  */
1573 struct GNUNET_SCHEDULER_Task *
1574 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1575                                          enum GNUNET_SCHEDULER_Priority priority,
1576                                          const struct GNUNET_DISK_FileHandle *fd,
1577                                          int on_read, int on_write,
1578                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1579 {
1580 #if MINGW
1581   struct GNUNET_NETWORK_FDSet *s;
1582   struct GNUNET_SCHEDULER_Task * ret;
1583
1584   GNUNET_assert (NULL != fd);
1585   s = GNUNET_NETWORK_fdset_create ();
1586   GNUNET_NETWORK_fdset_handle_set (s, fd);
1587   ret = GNUNET_SCHEDULER_add_select (
1588       priority, delay,
1589       on_read  ? s : NULL,
1590       on_write ? s : NULL,
1591       task, task_cls);
1592   GNUNET_NETWORK_fdset_destroy (s);
1593   return ret;
1594 #else
1595   int real_fd;
1596
1597   GNUNET_DISK_internal_file_handle_ (fd, &real_fd, sizeof (int));
1598   GNUNET_assert (real_fd >= 0);
1599   return add_without_sets (
1600       delay, priority,
1601       on_read  ? real_fd : -1,
1602       on_write ? real_fd : -1,
1603       task, task_cls);
1604 #endif
1605 }
1606
1607
1608 /**
1609  * Schedule a new task to be run with a specified delay or when any of
1610  * the specified file descriptor sets is ready.  The delay can be used
1611  * as a timeout on the socket(s) being ready.  The task will be
1612  * scheduled for execution once either the delay has expired or any of
1613  * the socket operations is ready.  This is the most general
1614  * function of the "add" family.  Note that the "prerequisite_task"
1615  * must be satisfied in addition to any of the other conditions.  In
1616  * other words, the task will be started when
1617  * <code>
1618  * (prerequisite-run)
1619  * && (delay-ready
1620  *     || any-rs-ready
1621  *     || any-ws-ready
1622  *     || (shutdown-active && run-on-shutdown) )
1623  * </code>
1624  *
1625  * @param prio how important is this task?
1626  * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1627  *        which means that the task will only be run after we receive SIGTERM
1628  * @param rs set of file descriptors we want to read (can be NULL)
1629  * @param ws set of file descriptors we want to write (can be NULL)
1630  * @param task main function of the task
1631  * @param task_cls closure of @a task
1632  * @return unique task identifier for the job
1633  *         only valid until @a task is started!
1634  */
1635 struct GNUNET_SCHEDULER_Task *
1636 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1637                              struct GNUNET_TIME_Relative delay,
1638                              const struct GNUNET_NETWORK_FDSet *rs,
1639                              const struct GNUNET_NETWORK_FDSet *ws,
1640                              GNUNET_SCHEDULER_TaskCallback task,
1641                              void *task_cls)
1642 {
1643   struct GNUNET_SCHEDULER_Task *t;
1644 #if EXECINFO
1645   void *backtrace_array[MAX_TRACE_DEPTH];
1646 #endif
1647
1648   if ( (NULL == rs) &&
1649        (NULL == ws) )
1650     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1651                                                        prio,
1652                                                        task,
1653                                                        task_cls);
1654   GNUNET_assert (NULL != active_task);
1655   GNUNET_assert (NULL != task);
1656   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1657   t->callback = task;
1658   t->callback_cls = task_cls;
1659 #if EXECINFO
1660   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1661   t->backtrace_strings =
1662       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1663 #endif
1664   t->read_fd = -1;
1665   t->write_fd = -1;
1666   if (NULL != rs)
1667   {
1668     t->read_set = GNUNET_NETWORK_fdset_create ();
1669     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1670   }
1671   if (NULL != ws)
1672   {
1673     t->write_set = GNUNET_NETWORK_fdset_create ();
1674     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1675   }
1676 #if PROFILE_DELAYS
1677   t->start_time = GNUNET_TIME_absolute_get ();
1678 #endif
1679   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1680   t->priority =
1681       check_priority ((prio ==
1682                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1683                       prio);
1684   t->lifeness = current_lifeness;
1685   GNUNET_CONTAINER_DLL_insert (pending_head,
1686                                pending_tail,
1687                                t);
1688   max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
1689   LOG (GNUNET_ERROR_TYPE_DEBUG,
1690        "Adding task %p\n",
1691        t);
1692 #if EXECINFO
1693   int i;
1694
1695   for (i = 0; i < t->num_backtrace_strings; i++)
1696     LOG (GNUNET_ERROR_TYPE_DEBUG,
1697          "Task p trace %d: %s\n",
1698          t,
1699          i,
1700          t->backtrace_strings[i]);
1701 #endif
1702   return t;
1703 }
1704
1705 /* end of scheduler.c */