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