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