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