-towards addressing #3047, note this causes the code to FTBFS
[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 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 (pos != NULL)
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 (pos != NULL)
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 (pos->read_fd != -1)
314       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
315     if (pos->write_fd != -1)
316       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
317     if (pos->read_set != NULL)
318       GNUNET_NETWORK_fdset_add (rs, pos->read_set);
319     if (pos->write_set != NULL)
320       GNUNET_NETWORK_fdset_add (ws, pos->write_set);
321     if (pos->reason != 0)
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 (reason == 0)
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 (pos != NULL)
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 (pos != NULL)
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 (prev == NULL)
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 (pos != NULL)
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 (pos != NULL)
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 (pos != NULL)
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, struct GNUNET_NETWORK_FDSet *ws)
542 {
543   enum GNUNET_SCHEDULER_Priority p;
544   struct Task *pos;
545   struct GNUNET_SCHEDULER_TaskContext tc;
546
547   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
548   do
549   {
550     if (ready_count == 0)
551       return;
552     GNUNET_assert (ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
553     /* yes, p>0 is correct, 0 is "KEEP" which should
554      * always be an empty queue (see assertion)! */
555     for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
556     {
557       pos = ready[p];
558       if (pos != NULL)
559         break;
560     }
561     GNUNET_assert (pos != NULL);        /* ready_count wrong? */
562     ready[p] = pos->next;
563     ready_count--;
564     current_priority = pos->priority;
565     current_lifeness = pos->lifeness;
566     active_task = pos;
567 #if PROFILE_DELAYS
568     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
569         DELAY_THRESHOLD.rel_value_us)
570     {
571       LOG (GNUNET_ERROR_TYPE_DEBUG, 
572            "Task %llu took %s to be scheduled\n",
573            (unsigned long long) pos->id,
574            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time), GNUNET_YES));
575     }
576 #endif
577     tc.reason = pos->reason;
578     tc.read_ready = (pos->read_set == NULL) ? rs : pos->read_set;
579     if ((pos->read_fd != -1) &&
580         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)))
581       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
582     tc.write_ready = (pos->write_set == NULL) ? ws : pos->write_set;
583     if ((pos->write_fd != -1) &&
584         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
585       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
586     if (((tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0) &&
587         (pos->write_fd != -1) &&
588         (!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
589       GNUNET_abort ();          // added to ready in previous select loop!
590     LOG (GNUNET_ERROR_TYPE_DEBUG, 
591          "Running task: %llu / %p\n", pos->id,
592          pos->callback_cls);
593     pos->callback (pos->callback_cls, &tc);
594 #if EXECINFO
595     int i;
596
597     for (i = 0; i < pos->num_backtrace_strings; i++)
598       LOG (GNUNET_ERROR_TYPE_ERROR, "Task %llu trace %d: %s\n", pos->id, i,
599            pos->backtrace_strings[i]);
600 #endif
601     active_task = NULL;
602     destroy_task (pos);
603     tasks_run++;
604   }
605   while ((pending == NULL) || (p >= max_priority_added));
606 }
607
608 /**
609  * Pipe used to communicate shutdown via signal.
610  */
611 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
612
613 /**
614  * Process ID of this process at the time we installed the various
615  * signal handlers.
616  */
617 static pid_t my_pid;
618
619 /**
620  * Signal handler called for SIGPIPE.
621  */
622 #ifndef MINGW
623 static void
624 sighandler_pipe ()
625 {
626   return;
627 }
628 #endif
629
630
631 /**
632  * Wait for a short time.
633  * Sleeps for @a ms ms (as that should be long enough for virtually all
634  * modern systems to context switch and allow another process to do
635  * some 'real' work).
636  *
637  * @param ms how many ms to wait
638  */
639 static void
640 short_wait (unsigned int ms)
641 {
642   struct GNUNET_TIME_Relative timeout;
643
644   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
645   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
646 }
647
648
649 /**
650  * Signal handler called for signals that should cause us to shutdown.
651  */
652 static void
653 sighandler_shutdown ()
654 {
655   static char c;
656   int old_errno = errno;        /* backup errno */
657
658   if (getpid () != my_pid)
659     exit (1);                   /* we have fork'ed since the signal handler was created,
660                                  * ignore the signal, see https://gnunet.org/vfork discussion */
661   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
662                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
663                           &c, sizeof (c));
664   errno = old_errno;
665 }
666
667
668 /**
669  * Check if the system is still life. Trigger shutdown if we
670  * have tasks, but none of them give us lifeness.
671  *
672  * @return #GNUNET_OK to continue the main loop,
673  *         #GNUNET_NO to exit
674  */
675 static int
676 check_lifeness ()
677 {
678   struct Task *t;
679
680   if (ready_count > 0)
681     return GNUNET_OK;
682   for (t = pending; NULL != t; t = t->next)
683     if (t->lifeness == GNUNET_YES)
684       return GNUNET_OK;
685   for (t = pending_timeout; NULL != t; t = t->next)
686     if (t->lifeness == GNUNET_YES)
687       return GNUNET_OK;
688   if ((NULL != pending) || (NULL != pending_timeout))
689   {
690     GNUNET_SCHEDULER_shutdown ();
691     return GNUNET_OK;
692   }
693   return GNUNET_NO;
694 }
695
696
697 /**
698  * Initialize and run scheduler.  This function will return when all
699  * tasks have completed.  On systems with signals, receiving a SIGTERM
700  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
701  * to be run after the active task is complete.  As a result, SIGTERM
702  * causes all active tasks to be scheduled with reason
703  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
704  * afterwards will execute normally!). Note that any particular signal
705  * will only shut down one scheduler; applications should always only
706  * create a single scheduler.
707  *
708  * @param task task to run immediately
709  * @param task_cls closure of task
710  */
711 void
712 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
713 {
714   struct GNUNET_NETWORK_FDSet *rs;
715   struct GNUNET_NETWORK_FDSet *ws;
716   struct GNUNET_TIME_Relative timeout;
717   int ret;
718   struct GNUNET_SIGNAL_Context *shc_int;
719   struct GNUNET_SIGNAL_Context *shc_term;
720
721 #ifndef MINGW
722   struct GNUNET_SIGNAL_Context *shc_quit;
723   struct GNUNET_SIGNAL_Context *shc_hup;
724   struct GNUNET_SIGNAL_Context *shc_pipe;
725 #endif
726   unsigned long long last_tr;
727   unsigned int busy_wait_warning;
728   const struct GNUNET_DISK_FileHandle *pr;
729   char c;
730
731   GNUNET_assert (active_task == NULL);
732   rs = GNUNET_NETWORK_fdset_create ();
733   ws = GNUNET_NETWORK_fdset_create ();
734   GNUNET_assert (shutdown_pipe_handle == NULL);
735   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
736   GNUNET_assert (shutdown_pipe_handle != NULL);
737   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
738                                 GNUNET_DISK_PIPE_END_READ);
739   GNUNET_assert (pr != NULL);
740   my_pid = getpid ();
741   LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering signal handlers\n");
742   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
743   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
744 #ifndef MINGW
745   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
746   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
747   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
748 #endif
749   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
750   current_lifeness = GNUNET_YES;
751   GNUNET_SCHEDULER_add_continuation (task, task_cls,
752                                      GNUNET_SCHEDULER_REASON_STARTUP);
753   active_task = (void *) (long) -1;     /* force passing of sanity check */
754   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
755                                           &GNUNET_OS_install_parent_control_handler,
756                                           NULL);
757   active_task = NULL;
758   last_tr = 0;
759   busy_wait_warning = 0;
760   while (GNUNET_OK == check_lifeness ())
761   {
762     GNUNET_NETWORK_fdset_zero (rs);
763     GNUNET_NETWORK_fdset_zero (ws);
764     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
765     update_sets (rs, ws, &timeout);
766     GNUNET_NETWORK_fdset_handle_set (rs, pr);
767     if (ready_count > 0)
768     {
769       /* no blocking, more work already ready! */
770       timeout = GNUNET_TIME_UNIT_ZERO;
771     }
772     if (NULL == scheduler_select)
773       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
774     else
775       ret = scheduler_select (scheduler_select_cls, rs, ws, NULL, timeout);
776     if (ret == GNUNET_SYSERR)
777     {
778       if (errno == EINTR)
779         continue;
780
781       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
782 #ifndef MINGW
783 #if USE_LSOF
784       char lsof[512];
785
786       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
787       (void) close (1);
788       (void) dup2 (2, 1);
789       if (0 != system (lsof))
790         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "system");
791 #endif
792 #endif
793       GNUNET_abort ();
794       break;
795     }
796     if ((0 == ret) && (0 == timeout.rel_value_us) && (busy_wait_warning > 16))
797     {
798       LOG (GNUNET_ERROR_TYPE_WARNING, _("Looks like we're busy waiting...\n"));
799       short_wait (100);                /* mitigate */
800     }
801     check_ready (rs, ws);
802     run_ready (rs, ws);
803     if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
804     {
805       /* consume the signal */
806       GNUNET_DISK_file_read (pr, &c, sizeof (c));
807       /* mark all active tasks as ready due to shutdown */
808       GNUNET_SCHEDULER_shutdown ();
809     }
810     if (last_tr == tasks_run)
811     {
812       short_wait (1);
813       busy_wait_warning++;
814     }
815     else
816     {
817       last_tr = tasks_run;
818       busy_wait_warning = 0;
819     }
820   }
821   GNUNET_SIGNAL_handler_uninstall (shc_int);
822   GNUNET_SIGNAL_handler_uninstall (shc_term);
823 #ifndef MINGW
824   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
825   GNUNET_SIGNAL_handler_uninstall (shc_quit);
826   GNUNET_SIGNAL_handler_uninstall (shc_hup);
827 #endif
828   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
829   shutdown_pipe_handle = NULL;
830   GNUNET_NETWORK_fdset_destroy (rs);
831   GNUNET_NETWORK_fdset_destroy (ws);
832 }
833
834
835 /**
836  * Obtain the reason code for why the current task was
837  * started.  Will return the same value as
838  * the GNUNET_SCHEDULER_TaskContext's reason field.
839  *
840  * @return reason(s) why the current task is run
841  */
842 enum GNUNET_SCHEDULER_Reason
843 GNUNET_SCHEDULER_get_reason ()
844 {
845   GNUNET_assert (active_task != NULL);
846   return active_task->reason;
847 }
848
849
850 /**
851  * Get information about the current load of this scheduler.  Use this
852  * function to determine if an elective task should be added or simply
853  * dropped (if the decision should be made based on the number of
854  * tasks ready to run).
855  *
856  * @param p priority level to look at
857  * @return number of tasks pending right now
858  */
859 unsigned int
860 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
861 {
862   struct Task *pos;
863   unsigned int ret;
864
865   GNUNET_assert (active_task != NULL);
866   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
867     return ready_count;
868   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
869     p = current_priority;
870   ret = 0;
871   pos = ready[check_priority (p)];
872   while (pos != NULL)
873   {
874     pos = pos->next;
875     ret++;
876   }
877   return ret;
878 }
879
880
881 /**
882  * Cancel the task with the specified identifier.
883  * The task must not yet have run.
884  *
885  * @param task id of the task to cancel
886  * @return original closure of the task
887  */
888 void *
889 GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_TaskIdentifier task)
890 {
891   struct Task *t;
892   struct Task *prev;
893   enum GNUNET_SCHEDULER_Priority p;
894   int to;
895   void *ret;
896
897   GNUNET_assert (active_task != NULL);
898   to = 0;
899   prev = NULL;
900   t = pending;
901   while (t != NULL)
902   {
903     if (t->id == task)
904       break;
905     prev = t;
906     t = t->next;
907   }
908   if (t == NULL)
909   {
910     prev = NULL;
911     to = 1;
912     t = pending_timeout;
913     while (t != NULL)
914     {
915       if (t->id == task)
916         break;
917       prev = t;
918       t = t->next;
919     }
920     if (pending_timeout_last == t)
921       pending_timeout_last = NULL;
922   }
923   p = 0;
924   while (t == NULL)
925   {
926     p++;
927     if (p >= GNUNET_SCHEDULER_PRIORITY_COUNT)
928     {
929       LOG (GNUNET_ERROR_TYPE_ERROR, _("Attempt to cancel dead task %llu!\n"),
930            (unsigned long long) task);
931       GNUNET_assert (0);
932     }
933     prev = NULL;
934     t = ready[p];
935     while (t != NULL)
936     {
937       if (t->id == task)
938       {
939         ready_count--;
940         break;
941       }
942       prev = t;
943       t = t->next;
944     }
945   }
946   if (prev == NULL)
947   {
948     if (p == 0)
949     {
950       if (to == 0)
951       {
952         pending = t->next;
953       }
954       else
955       {
956         pending_timeout = t->next;
957       }
958     }
959     else
960     {
961       ready[p] = t->next;
962     }
963   }
964   else
965   {
966     prev->next = t->next;
967   }
968   ret = t->callback_cls;
969   LOG (GNUNET_ERROR_TYPE_DEBUG, "Canceling task: %llu / %p\n", task,
970        t->callback_cls);
971   destroy_task (t);
972   return ret;
973 }
974
975
976 /**
977  * Continue the current execution with the given function.  This is
978  * similar to the other "add" functions except that there is no delay
979  * and the reason code can be specified.
980  *
981  * @param task main function of the task
982  * @param task_cls closure for 'main'
983  * @param reason reason for task invocation
984  * @param priority priority to use for the task
985  */
986 void
987 GNUNET_SCHEDULER_add_continuation_with_priority (GNUNET_SCHEDULER_Task task, void *task_cls,
988                                                  enum GNUNET_SCHEDULER_Reason reason,
989                                                  enum GNUNET_SCHEDULER_Priority priority)
990 {
991   struct Task *t;
992
993 #if EXECINFO
994   void *backtrace_array[50];
995 #endif
996
997   GNUNET_assert (NULL != task);
998   GNUNET_assert ((active_task != NULL) ||
999                  (reason == GNUNET_SCHEDULER_REASON_STARTUP));
1000   t = GNUNET_malloc (sizeof (struct Task));
1001 #if EXECINFO
1002   t->num_backtrace_strings = backtrace (backtrace_array, 50);
1003   t->backtrace_strings =
1004       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1005 #endif
1006   t->read_fd = -1;
1007   t->write_fd = -1;
1008   t->callback = task;
1009   t->callback_cls = task_cls;
1010   t->id = ++last_id;
1011 #if PROFILE_DELAYS
1012   t->start_time = GNUNET_TIME_absolute_get ();
1013 #endif
1014   t->reason = reason;
1015   t->priority = priority;
1016   t->lifeness = current_lifeness;
1017   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding continuation task: %llu / %p\n", t->id,
1018        t->callback_cls);
1019   queue_ready_task (t);
1020 }
1021
1022
1023 /**
1024  * Continue the current execution with the given function.  This is
1025  * similar to the other "add" functions except that there is no delay
1026  * and the reason code can be specified.
1027  *
1028  * @param task main function of the task
1029  * @param task_cls closure for 'main'
1030  * @param reason reason for task invocation
1031  */
1032 void
1033 GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_Task task, void *task_cls,
1034                                    enum GNUNET_SCHEDULER_Reason reason)
1035 {
1036   GNUNET_SCHEDULER_add_continuation_with_priority (task, task_cls,
1037                                                    reason,
1038                                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT);
1039 }
1040
1041
1042 /**
1043  * Schedule a new task to be run with a specified priority.
1044  *
1045  * @param prio how important is the new task?
1046  * @param task main function of the task
1047  * @param task_cls closure of task
1048  * @return unique task identifier for the job
1049  *         only valid until "task" is started!
1050  */
1051 GNUNET_SCHEDULER_TaskIdentifier
1052 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1053                                     GNUNET_SCHEDULER_Task task, void *task_cls)
1054 {
1055   return GNUNET_SCHEDULER_add_select (prio, 
1056                                       GNUNET_TIME_UNIT_ZERO, NULL, NULL, task,
1057                                       task_cls);
1058 }
1059
1060
1061
1062 /**
1063  * Schedule a new task to be run with a specified delay.  The task
1064  * will be scheduled for execution once the delay has expired.
1065  *
1066  * @param delay when should this operation time out? Use
1067  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1068  * @param priority priority to use for the task
1069  * @param task main function of the task
1070  * @param task_cls closure of task
1071  * @return unique task identifier for the job
1072  *         only valid until "task" is started!
1073  */
1074 GNUNET_SCHEDULER_TaskIdentifier
1075 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1076                                             enum GNUNET_SCHEDULER_Priority priority,
1077                                             GNUNET_SCHEDULER_Task task, void *task_cls)
1078 {
1079   struct Task *t;
1080   struct Task *pos;
1081   struct Task *prev;
1082
1083 #if EXECINFO
1084   void *backtrace_array[MAX_TRACE_DEPTH];
1085 #endif
1086
1087   GNUNET_assert (active_task != NULL);
1088   GNUNET_assert (NULL != task);
1089   t = GNUNET_malloc (sizeof (struct Task));
1090   t->callback = task;
1091   t->callback_cls = task_cls;
1092 #if EXECINFO
1093   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1094   t->backtrace_strings =
1095       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1096 #endif
1097   t->read_fd = -1;
1098   t->write_fd = -1;
1099   t->id = ++last_id;
1100 #if PROFILE_DELAYS
1101   t->start_time = GNUNET_TIME_absolute_get ();
1102 #endif
1103   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1104   t->priority = priority;
1105   t->lifeness = current_lifeness;
1106   /* try tail first (optimization in case we are
1107    * appending to a long list of tasks with timeouts) */
1108   prev = pending_timeout_last;
1109   if (prev != NULL)
1110   {
1111     if (prev->timeout.abs_value_us > t->timeout.abs_value_us)
1112       prev = NULL;
1113     else
1114       pos = prev->next;         /* heuristic success! */
1115   }
1116   if (prev == NULL)
1117   {
1118     /* heuristic failed, do traversal of timeout list */
1119     pos = pending_timeout;
1120   }
1121   while ((pos != NULL) &&
1122          ((pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1123           (0 != pos->reason)))
1124   {
1125     prev = pos;
1126     pos = pos->next;
1127   }
1128   if (prev == NULL)
1129     pending_timeout = t;
1130   else
1131     prev->next = t;
1132   t->next = pos;
1133   /* hyper-optimization... */
1134   pending_timeout_last = t;
1135
1136   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding task: %llu / %p\n", t->id,
1137        t->callback_cls);
1138 #if EXECINFO
1139   int i;
1140
1141   for (i = 0; i < t->num_backtrace_strings; i++)
1142     LOG (GNUNET_ERROR_TYPE_DEBUG, "Task %llu trace %d: %s\n", t->id, i,
1143          t->backtrace_strings[i]);
1144 #endif
1145   return t->id;
1146 }
1147
1148
1149 /**
1150  * Schedule a new task to be run with a specified delay.  The task
1151  * will be scheduled for execution once the delay has expired. It
1152  * will be run with the DEFAULT priority.
1153  *
1154  * @param delay when should this operation time out? Use
1155  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1156  * @param task main function of the task
1157  * @param task_cls closure of task
1158  * @return unique task identifier for the job
1159  *         only valid until "task" is started!
1160  */
1161 GNUNET_SCHEDULER_TaskIdentifier
1162 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1163                               GNUNET_SCHEDULER_Task task, void *task_cls)
1164 {
1165   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1166                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1167                                                      task, task_cls);
1168 }
1169
1170
1171 /**
1172  * Schedule a new task to be run as soon as possible.  Note that this
1173  * does not guarantee that this will be the next task that is being
1174  * run, as other tasks with higher priority (or that are already ready
1175  * to run) might get to run first.  Just as with delays, clients must
1176  * not rely on any particular order of execution between tasks
1177  * scheduled concurrently.
1178  * 
1179  * The task will be run with the DEFAULT priority.
1180  *
1181  * @param task main function of the task
1182  * @param task_cls closure of @a task
1183  * @return unique task identifier for the job
1184  *         only valid until "task" is started!
1185  */
1186 GNUNET_SCHEDULER_TaskIdentifier
1187 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task, void *task_cls)
1188 {
1189   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO, task, task_cls);
1190 }
1191
1192
1193 /**
1194  * Schedule a new task to be run as soon as possible with the
1195  * (transitive) ignore-shutdown flag either explicitly set or
1196  * explicitly enabled.  This task (and all tasks created from it,
1197  * other than by another call to this function) will either count or
1198  * not count for the 'lifeness' of the process.  This API is only
1199  * useful in a few special cases.
1200  *
1201  * @param lifeness GNUNET_YES if the task counts for lifeness, GNUNET_NO if not.
1202  * @param task main function of the task
1203  * @param task_cls closure of task
1204  * @return unique task identifier for the job
1205  *         only valid until "task" is started!
1206  */
1207 GNUNET_SCHEDULER_TaskIdentifier
1208 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1209                                         GNUNET_SCHEDULER_Task task,
1210                                         void *task_cls)
1211 {
1212   GNUNET_SCHEDULER_TaskIdentifier ret;
1213
1214   ret =
1215       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1216                                    GNUNET_TIME_UNIT_ZERO, NULL, NULL, task,
1217                                    task_cls);
1218   GNUNET_assert (pending->id == ret);
1219   pending->lifeness = lifeness;
1220   return ret;
1221 }
1222
1223
1224 /**
1225  * Schedule a new task to be run with a specified delay or when any of
1226  * the specified file descriptor sets is ready.  The delay can be used
1227  * as a timeout on the socket(s) being ready.  The task will be
1228  * scheduled for execution once either the delay has expired or any of
1229  * the socket operations is ready.  This is the most general
1230  * function of the "add" family.  Note that the "prerequisite_task"
1231  * must be satisfied in addition to any of the other conditions.  In
1232  * other words, the task will be started when
1233  * <code>
1234  * (prerequisite-run)
1235  * && (delay-ready
1236  *     || any-rs-ready
1237  *     || any-ws-ready
1238  *     || shutdown-active )
1239  * </code>
1240  *
1241  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1242  *        which means that the task will only be run after we receive SIGTERM
1243  * @param priority priority to use
1244  * @param rfd file descriptor we want to read (can be -1)
1245  * @param wfd file descriptors we want to write (can be -1)
1246  * @param task main function of the task
1247  * @param task_cls closure of task
1248  * @return unique task identifier for the job
1249  *         only valid until "task" is started!
1250  */
1251 #ifndef MINGW
1252 static GNUNET_SCHEDULER_TaskIdentifier
1253 add_without_sets (struct GNUNET_TIME_Relative delay, 
1254                   enum GNUNET_SCHEDULER_Priority priority,
1255                   int rfd, int wfd,
1256                   GNUNET_SCHEDULER_Task task, void *task_cls)
1257 {
1258   struct Task *t;
1259
1260 #if EXECINFO
1261   void *backtrace_array[MAX_TRACE_DEPTH];
1262 #endif
1263
1264   GNUNET_assert (active_task != NULL);
1265   GNUNET_assert (NULL != task);
1266   t = GNUNET_malloc (sizeof (struct Task));
1267   t->callback = task;
1268   t->callback_cls = task_cls;
1269 #if EXECINFO
1270   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1271   t->backtrace_strings =
1272       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1273 #endif
1274 #if DEBUG_FDS
1275   if (-1 != rfd)
1276   {
1277     int flags = fcntl (rfd, F_GETFD);
1278
1279     if ((flags == -1) && (errno == EBADF))
1280     {
1281       LOG (GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", rfd);
1282 #if EXECINFO
1283       int i;
1284
1285       for (i = 0; i < t->num_backtrace_strings; i++)
1286         LOG (GNUNET_ERROR_TYPE_DEBUG, "Trace: %s\n", t->backtrace_strings[i]);
1287 #endif
1288       GNUNET_assert (0);
1289     }
1290   }
1291   if (-1 != wfd)
1292   {
1293     int flags = fcntl (wfd, F_GETFD);
1294
1295     if (flags == -1 && errno == EBADF)
1296     {
1297       LOG (GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", wfd);
1298 #if EXECINFO
1299       int i;
1300
1301       for (i = 0; i < t->num_backtrace_strings; i++)
1302         LOG (GNUNET_ERROR_TYPE_DEBUG, "Trace: %s\n", t->backtrace_strings[i]);
1303 #endif
1304       GNUNET_assert (0);
1305     }
1306   }
1307 #endif
1308   t->read_fd = rfd;
1309   GNUNET_assert (wfd >= -1);
1310   t->write_fd = wfd;
1311   t->id = ++last_id;
1312 #if PROFILE_DELAYS
1313   t->start_time = GNUNET_TIME_absolute_get ();
1314 #endif
1315   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1316   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1317   t->lifeness = current_lifeness;
1318   t->next = pending;
1319   pending = t;
1320   max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
1321   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding task: %llu / %p\n", t->id,
1322        t->callback_cls);
1323 #if EXECINFO
1324   int i;
1325
1326   for (i = 0; i < t->num_backtrace_strings; i++)
1327     LOG (GNUNET_ERROR_TYPE_DEBUG, "Task %llu trace %d: %s\n", t->id, i,
1328          t->backtrace_strings[i]);
1329 #endif
1330   return t->id;
1331 }
1332 #endif
1333
1334
1335
1336 /**
1337  * Schedule a new task to be run with a specified delay or when the
1338  * specified file descriptor is ready for reading.  The delay can be
1339  * used as a timeout on the socket being ready.  The task will be
1340  * scheduled for execution once either the delay has expired or the
1341  * socket operation is ready.  It will be run with the DEFAULT priority.
1342  *
1343  * @param delay when should this operation time out? Use
1344  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1345  * @param rfd read file-descriptor
1346  * @param task main function of the task
1347  * @param task_cls closure of task
1348  * @return unique task identifier for the job
1349  *         only valid until "task" is started!
1350  */
1351 GNUNET_SCHEDULER_TaskIdentifier
1352 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1353                                struct GNUNET_NETWORK_Handle *rfd,
1354                                GNUNET_SCHEDULER_Task task, void *task_cls)
1355 {
1356   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1357                                                       GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1358                                                       rfd, task, task_cls);
1359 }
1360
1361
1362 /**
1363  * Schedule a new task to be run with a specified priority and to be
1364  * run after the specified delay or when the specified file descriptor
1365  * is ready for reading.  The delay can be used as a timeout on the
1366  * socket being ready.  The task will be scheduled for execution once
1367  * either the delay has expired or the socket operation is ready.  It
1368  * will be run with the DEFAULT priority.
1369  *
1370  * @param delay when should this operation time out? Use
1371  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1372  * @param priority priority to use for the task
1373  * @param rfd read file-descriptor
1374  * @param task main function of the task
1375  * @param task_cls closure of task
1376  * @return unique task identifier for the job
1377  *         only valid until "task" is started!
1378  */
1379 GNUNET_SCHEDULER_TaskIdentifier
1380 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1381                                              enum GNUNET_SCHEDULER_Priority priority,
1382                                              struct GNUNET_NETWORK_Handle *rfd,
1383                                              GNUNET_SCHEDULER_Task task, void *task_cls)
1384 {
1385 #if MINGW
1386   struct GNUNET_NETWORK_FDSet *rs;
1387   GNUNET_SCHEDULER_TaskIdentifier ret;
1388
1389   GNUNET_assert (rfd != NULL);
1390   rs = GNUNET_NETWORK_fdset_create ();
1391   GNUNET_NETWORK_fdset_set (rs, rfd);
1392   ret =
1393     GNUNET_SCHEDULER_add_select (priority,
1394                                  delay, rs, NULL,
1395                                  task, task_cls);
1396   GNUNET_NETWORK_fdset_destroy (rs);
1397   return ret;
1398 #else
1399   return add_without_sets (delay, 
1400                            priority,
1401                            GNUNET_NETWORK_get_fd (rfd), -1, task,
1402                            task_cls);
1403 #endif
1404 }
1405
1406
1407
1408 /**
1409  * Schedule a new task to be run with a specified delay or when the
1410  * specified file descriptor is ready for writing.  The delay can be
1411  * used as a timeout on the socket being ready.  The task will be
1412  * scheduled for execution once either the delay has expired or the
1413  * socket operation is ready.  It will be run with the priority of
1414  * the calling task.
1415  *
1416  * @param delay when should this operation time out? Use
1417  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1418  * @param wfd write file-descriptor
1419  * @param task main function of the task
1420  * @param task_cls closure of task
1421  * @return unique task identifier for the job
1422  *         only valid until "task" is started!
1423  */
1424 GNUNET_SCHEDULER_TaskIdentifier
1425 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1426                                 struct GNUNET_NETWORK_Handle *wfd,
1427                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1428 {
1429 #if MINGW
1430   struct GNUNET_NETWORK_FDSet *ws;
1431   GNUNET_SCHEDULER_TaskIdentifier ret;
1432
1433   GNUNET_assert (wfd != NULL);
1434   ws = GNUNET_NETWORK_fdset_create ();
1435   GNUNET_NETWORK_fdset_set (ws, wfd);
1436   ret =
1437     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1438                                  delay, NULL, ws,
1439                                    task, task_cls);
1440   GNUNET_NETWORK_fdset_destroy (ws);
1441   return ret;
1442 #else
1443   GNUNET_assert (GNUNET_NETWORK_get_fd (wfd) >= 0);
1444   return add_without_sets (delay, 
1445                            GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1446                            -1, GNUNET_NETWORK_get_fd (wfd), task,
1447                            task_cls);
1448 #endif
1449 }
1450
1451
1452 /**
1453  * Schedule a new task to be run with a specified delay or when the
1454  * specified file descriptor is ready for reading.  The delay can be
1455  * used as a timeout on the socket being ready.  The task will be
1456  * scheduled for execution once either the delay has expired or the
1457  * socket operation is ready. It will be run with the DEFAULT priority.
1458  *
1459  * @param delay when should this operation time out? Use
1460  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1461  * @param rfd read file-descriptor
1462  * @param task main function of the task
1463  * @param task_cls closure of task
1464  * @return unique task identifier for the job
1465  *         only valid until "task" is started!
1466  */
1467 GNUNET_SCHEDULER_TaskIdentifier
1468 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1469                                 const struct GNUNET_DISK_FileHandle *rfd,
1470                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1471 {
1472 #if MINGW
1473   struct GNUNET_NETWORK_FDSet *rs;
1474   GNUNET_SCHEDULER_TaskIdentifier ret;
1475
1476   GNUNET_assert (rfd != NULL);
1477   rs = GNUNET_NETWORK_fdset_create ();
1478   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1479   ret =
1480       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1481                                    delay, rs, NULL,
1482                                    task, task_cls);
1483   GNUNET_NETWORK_fdset_destroy (rs);
1484   return ret;
1485 #else
1486   int fd;
1487
1488   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1489   return add_without_sets (delay, 
1490                            GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1491                            fd, -1, task, task_cls);
1492
1493 #endif
1494 }
1495
1496
1497 /**
1498  * Schedule a new task to be run with a specified delay or when the
1499  * specified file descriptor is ready for writing.  The delay can be
1500  * used as a timeout on the socket being ready.  The task will be
1501  * scheduled for execution once either the delay has expired or the
1502  * socket operation is ready. It will be run with the DEFAULT priority.
1503  *
1504  * @param delay when should this operation time out? Use
1505  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1506  * @param wfd write file-descriptor
1507  * @param task main function of the task
1508  * @param task_cls closure of task
1509  * @return unique task identifier for the job
1510  *         only valid until "task" is started!
1511  */
1512 GNUNET_SCHEDULER_TaskIdentifier
1513 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1514                                  const struct GNUNET_DISK_FileHandle *wfd,
1515                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1516 {
1517 #if MINGW
1518   struct GNUNET_NETWORK_FDSet *ws;
1519   GNUNET_SCHEDULER_TaskIdentifier ret;
1520
1521   GNUNET_assert (wfd != NULL);
1522   ws = GNUNET_NETWORK_fdset_create ();
1523   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1524   ret =
1525       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1526                                    delay, NULL, ws,
1527                                    task, task_cls);
1528   GNUNET_NETWORK_fdset_destroy (ws);
1529   return ret;
1530 #else
1531   int fd;
1532
1533   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1534   GNUNET_assert (fd >= 0);
1535   return add_without_sets (delay, 
1536                            GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1537                            -1, fd, task, task_cls);
1538
1539 #endif
1540 }
1541
1542
1543
1544 /**
1545  * Schedule a new task to be run with a specified delay or when any of
1546  * the specified file descriptor sets is ready.  The delay can be used
1547  * as a timeout on the socket(s) being ready.  The task will be
1548  * scheduled for execution once either the delay has expired or any of
1549  * the socket operations is ready.  This is the most general
1550  * function of the "add" family.  Note that the "prerequisite_task"
1551  * must be satisfied in addition to any of the other conditions.  In
1552  * other words, the task will be started when
1553  * <code>
1554  * (prerequisite-run)
1555  * && (delay-ready
1556  *     || any-rs-ready
1557  *     || any-ws-ready
1558  *     || (shutdown-active && run-on-shutdown) )
1559  * </code>
1560  *
1561  * @param prio how important is this task?
1562  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1563  *        which means that the task will only be run after we receive SIGTERM
1564  * @param rs set of file descriptors we want to read (can be NULL)
1565  * @param ws set of file descriptors we want to write (can be NULL)
1566  * @param task main function of the task
1567  * @param task_cls closure of task
1568  * @return unique task identifier for the job
1569  *         only valid until "task" is started!
1570  */
1571 GNUNET_SCHEDULER_TaskIdentifier
1572 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1573                              struct GNUNET_TIME_Relative delay,
1574                              const struct GNUNET_NETWORK_FDSet *rs,
1575                              const struct GNUNET_NETWORK_FDSet *ws,
1576                              GNUNET_SCHEDULER_Task task, void *task_cls)
1577 {
1578   struct Task *t;
1579
1580 #if EXECINFO
1581   void *backtrace_array[MAX_TRACE_DEPTH];
1582 #endif
1583
1584   GNUNET_assert (NULL != active_task);
1585   GNUNET_assert (NULL != task);
1586   t = GNUNET_malloc (sizeof (struct Task));
1587   t->callback = task;
1588   t->callback_cls = task_cls;
1589 #if EXECINFO
1590   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1591   t->backtrace_strings =
1592       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1593 #endif
1594   t->read_fd = -1;
1595   t->write_fd = -1;
1596   if (rs != NULL)
1597   {
1598     t->read_set = GNUNET_NETWORK_fdset_create ();
1599     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1600   }
1601   if (ws != NULL)
1602   {
1603     t->write_set = GNUNET_NETWORK_fdset_create ();
1604     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1605   }
1606   t->id = ++last_id;
1607 #if PROFILE_DELAYS
1608   t->start_time = GNUNET_TIME_absolute_get ();
1609 #endif
1610   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1611   t->priority =
1612       check_priority ((prio ==
1613                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1614                       prio);
1615   t->lifeness = current_lifeness;
1616   t->next = pending;
1617   pending = t;
1618   max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
1619   LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding task: %llu / %p\n", t->id,
1620        t->callback_cls);
1621 #if EXECINFO
1622   int i;
1623
1624   for (i = 0; i < t->num_backtrace_strings; i++)
1625     LOG (GNUNET_ERROR_TYPE_DEBUG, "Task %llu trace %d: %s\n", t->id, i,
1626          t->backtrace_strings[i]);
1627 #endif
1628   return t->id;
1629 }
1630
1631 /* end of scheduler.c */