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