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