429adafa31457cb6e30f7a91b1b5df8ed59ee8d9
[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_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   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
790                                           &GNUNET_OS_install_parent_control_handler,
791                                           NULL, GNUNET_SCHEDULER_REASON_STARTUP);
792 #endif
793   last_tr = 0;
794   busy_wait_warning = 0;
795   while (GNUNET_OK == check_lifeness ())
796     {
797       GNUNET_NETWORK_fdset_zero (rs);
798       GNUNET_NETWORK_fdset_zero (ws);
799       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
800       update_sets (rs, ws, &timeout);
801       GNUNET_NETWORK_fdset_handle_set (rs, pr);
802       if (ready_count > 0)
803         {
804           /* no blocking, more work already ready! */
805           timeout = GNUNET_TIME_UNIT_ZERO;
806         }
807       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
808       if (ret == GNUNET_SYSERR)
809         {
810           if (errno == EINTR)
811             continue;
812
813           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
814 #ifndef MINGW
815 #if USE_LSOF
816           char lsof[512];
817           snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
818           (void) close (1);
819           (void) dup2 (2, 1);
820           if (0 != system (lsof))
821             GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "system");    
822 #endif
823 #endif
824           abort ();
825           break;
826         }
827       if ((ret == 0) && (timeout.rel_value == 0) && (busy_wait_warning > 16))
828         {
829           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
830                       _("Looks like we're busy waiting...\n"));
831           sleep (1);            /* mitigate */
832         }
833       check_ready (rs, ws);
834       run_ready (rs, ws);
835       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
836         {
837           /* consume the signal */
838           GNUNET_DISK_file_read (pr, &c, sizeof (c));
839           /* mark all active tasks as ready due to shutdown */
840           GNUNET_SCHEDULER_shutdown ();
841         }
842       if (last_tr == tasks_run)
843         {
844           busy_wait_warning++;
845         }
846       else
847         {
848           last_tr = tasks_run;
849           busy_wait_warning = 0;
850         }
851     }
852   GNUNET_SIGNAL_handler_uninstall (shc_int);
853   GNUNET_SIGNAL_handler_uninstall (shc_term);
854 #ifndef MINGW
855   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
856   GNUNET_SIGNAL_handler_uninstall (shc_quit);
857   GNUNET_SIGNAL_handler_uninstall (shc_hup);
858 #endif
859   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
860   shutdown_pipe_handle = NULL;
861   GNUNET_NETWORK_fdset_destroy (rs);
862   GNUNET_NETWORK_fdset_destroy (ws);
863 }
864
865
866 /**
867  * Obtain the reason code for why the current task was
868  * started.  Will return the same value as 
869  * the GNUNET_SCHEDULER_TaskContext's reason field.
870  *
871  * @return reason(s) why the current task is run
872  */
873 enum GNUNET_SCHEDULER_Reason
874 GNUNET_SCHEDULER_get_reason ()
875 {
876   GNUNET_assert (active_task != NULL);
877   return active_task->reason;
878 }
879
880
881 /**
882  * Get information about the current load of this scheduler.  Use this
883  * function to determine if an elective task should be added or simply
884  * dropped (if the decision should be made based on the number of
885  * tasks ready to run).
886  *
887  * @param p priority level to look at
888  * @return number of tasks pending right now
889  */
890 unsigned int
891 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
892 {
893   struct Task *pos;
894   unsigned int ret;
895
896   GNUNET_assert (active_task != NULL);
897   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
898     return ready_count;
899   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
900     p = current_priority;
901   ret = 0;
902   pos = ready[check_priority (p)];
903   while (pos != NULL)
904     {
905       pos = pos->next;
906       ret++;
907     }
908   return ret;
909 }
910
911
912 /**
913  * Cancel the task with the specified identifier.
914  * The task must not yet have run.
915  *
916  * @param task id of the task to cancel
917  * @return original closure of the task
918  */
919 void *
920 GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_TaskIdentifier task)
921 {
922   struct Task *t;
923   struct Task *prev;
924   enum GNUNET_SCHEDULER_Priority p;
925   int to;
926   void *ret;
927
928   GNUNET_assert (active_task != NULL);
929   to = 0;
930   prev = NULL;
931   t = pending;
932   while (t != NULL)
933     {
934       if (t->id == task)
935         break;
936       prev = t;
937       t = t->next;
938     }
939   if (t == NULL)
940     {
941       prev = NULL;
942       to = 1;
943       t = pending_timeout;
944       while (t != NULL)
945         {
946           if (t->id == task)
947             break;
948           prev = t;
949           t = t->next;
950         }
951       if (pending_timeout_last == t)
952         pending_timeout_last = NULL;
953     }
954   p = 0;
955   while (t == NULL)
956     {
957       p++;
958       if (p >= GNUNET_SCHEDULER_PRIORITY_COUNT)
959         {
960           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
961                       _("Attempt to cancel dead task %llu!\n"),
962                       (unsigned long long) task);
963           GNUNET_assert (0);
964         }
965       prev = NULL;
966       t = ready[p];
967       while (t != NULL)
968         {
969           if (t->id == task)
970             {
971               ready_count--;
972               break;
973             }
974           prev = t;
975           t = t->next;
976         }
977     }
978   if (prev == NULL)
979     {
980       if (p == 0)
981         {
982           if (to == 0)
983             {
984               pending = t->next;
985             }
986           else
987             {
988               pending_timeout = t->next;
989             }
990         }
991       else
992         {
993           ready[p] = t->next;
994         }
995     }
996   else
997     {
998       prev->next = t->next;
999     }
1000   ret = t->callback_cls;
1001 #if DEBUG_TASKS
1002   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1003               "Canceling task: %llu / %p\n", task, t->callback_cls);
1004 #endif
1005   destroy_task (t);
1006   return ret;
1007 }
1008
1009
1010 /**
1011  * Continue the current execution with the given function.  This is
1012  * similar to the other "add" functions except that there is no delay
1013  * and the reason code can be specified.
1014  *
1015  * @param task main function of the task
1016  * @param task_cls closure for 'main'
1017  * @param reason reason for task invocation
1018  */
1019 void
1020 GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_Task task,
1021                                    void *task_cls,
1022                                    enum GNUNET_SCHEDULER_Reason reason)
1023 {
1024   struct Task *t;
1025 #if EXECINFO
1026   void *backtrace_array[50];
1027 #endif
1028
1029   GNUNET_assert ( (active_task != NULL) ||
1030                   (reason == GNUNET_SCHEDULER_REASON_STARTUP) );
1031   t = GNUNET_malloc (sizeof (struct Task));
1032 #if EXECINFO
1033   t->num_backtrace_strings = backtrace(backtrace_array, 50);
1034   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1035 #endif
1036   t->read_fd = -1;
1037   t->write_fd = -1;
1038   t->callback = task;
1039   t->callback_cls = task_cls;
1040   t->id = ++last_id;
1041 #if PROFILE_DELAYS
1042   t->start_time = GNUNET_TIME_absolute_get ();
1043 #endif
1044   t->reason = reason;
1045   t->priority = current_priority;
1046   t->lifeness = current_lifeness;
1047 #if DEBUG_TASKS
1048   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1049               "Adding continuation task: %llu / %p\n",
1050               t->id, t->callback_cls);
1051 #endif
1052   queue_ready_task (t);
1053 }
1054
1055
1056
1057 /**
1058  * Schedule a new task to be run after the specified prerequisite task
1059  * has completed. It will be run with the priority of the calling
1060  * task.
1061  *
1062  * @param prerequisite_task run this task after the task with the given
1063  *        task identifier completes (and any of our other
1064  *        conditions, such as delay, read or write-readiness
1065  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
1066  *        on completion of other tasks (this will cause the task to run as
1067  *        soon as possible).
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_after (GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
1075                             GNUNET_SCHEDULER_Task task, void *task_cls)
1076 {
1077   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1078                                       prerequisite_task,
1079                                       GNUNET_TIME_UNIT_ZERO,
1080                                       NULL, NULL, task, task_cls);
1081 }
1082
1083
1084 /**
1085  * Schedule a new task to be run with a specified priority.
1086  *
1087  * @param prio how important is the new task?
1088  * @param task main function of the task
1089  * @param task_cls closure of task
1090  * @return unique task identifier for the job
1091  *         only valid until "task" is started!
1092  */
1093 GNUNET_SCHEDULER_TaskIdentifier
1094 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1095                                     GNUNET_SCHEDULER_Task task,
1096                                     void *task_cls)
1097 {
1098   return GNUNET_SCHEDULER_add_select (prio,
1099                                       GNUNET_SCHEDULER_NO_TASK,
1100                                       GNUNET_TIME_UNIT_ZERO,
1101                                       NULL, NULL, task, task_cls);
1102 }
1103
1104
1105
1106 /**
1107  * Schedule a new task to be run with a specified delay.  The task
1108  * will be scheduled for execution once the delay has expired. It
1109  * will be run with the priority of the calling task.
1110  *
1111  * @param delay when should this operation time out? Use 
1112  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1113  * @param task main function of the task
1114  * @param task_cls closure of task
1115  * @return unique task identifier for the job
1116  *         only valid until "task" is started!
1117  */
1118 GNUNET_SCHEDULER_TaskIdentifier
1119 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1120                               GNUNET_SCHEDULER_Task task, void *task_cls)
1121 {
1122 #if 1
1123   /* new, optimized version */
1124   struct Task *t;
1125   struct Task *pos;
1126   struct Task *prev;
1127 #if EXECINFO
1128   void *backtrace_array[MAX_TRACE_DEPTH];
1129 #endif
1130
1131   GNUNET_assert (active_task != NULL);
1132   GNUNET_assert (NULL != task);
1133   t = GNUNET_malloc (sizeof (struct Task));
1134   t->callback = task;
1135   t->callback_cls = task_cls;
1136 #if EXECINFO
1137   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1138   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1139 #endif
1140   t->read_fd = -1;
1141   t->write_fd = -1;
1142   t->id = ++last_id;
1143 #if PROFILE_DELAYS
1144   t->start_time = GNUNET_TIME_absolute_get ();
1145 #endif
1146   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1147   t->priority = current_priority;
1148   t->lifeness = current_lifeness;
1149   /* try tail first (optimization in case we are
1150      appending to a long list of tasks with timeouts) */
1151   prev = pending_timeout_last;
1152   if (prev != NULL) 
1153     {
1154       if (prev->timeout.abs_value > t->timeout.abs_value)
1155         prev = NULL;
1156       else
1157         pos = prev->next; /* heuristic success! */
1158     }
1159   if (prev == NULL)
1160     {
1161       /* heuristic failed, do traversal of timeout list */
1162       pos = pending_timeout;
1163     }
1164   while ( (pos != NULL) &&
1165           ( (pos->timeout.abs_value <= t->timeout.abs_value) ||
1166             (pos->reason != 0) ) )
1167     {
1168       prev = pos;
1169       pos = pos->next;
1170     }
1171   if (prev == NULL)
1172     pending_timeout = t;
1173   else
1174     prev->next = t;
1175   t->next = pos;
1176   /* hyper-optimization... */
1177   pending_timeout_last = t;
1178
1179 #if DEBUG_TASKS
1180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1181               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1182 #endif
1183 #if EXECINFO
1184   int i;
1185
1186   for (i=0;i<t->num_backtrace_strings;i++)
1187       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1188                   "Task %llu trace %d: %s\n",
1189                   t->id,
1190                   i,
1191                   t->backtrace_strings[i]);
1192 #endif
1193   return t->id;
1194
1195 #else
1196   /* unoptimized version */
1197   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1198                                       GNUNET_SCHEDULER_NO_TASK, delay,
1199                                       NULL, NULL, task, task_cls);
1200 #endif
1201 }
1202
1203
1204
1205 /**
1206  * Schedule a new task to be run as soon as possible. The task
1207  * will be run with the priority of the calling task.
1208  *
1209  * @param task main function of the task
1210  * @param task_cls closure of task
1211  * @return unique task identifier for the job
1212  *         only valid until "task" is started!
1213  */
1214 GNUNET_SCHEDULER_TaskIdentifier
1215 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task,
1216                           void *task_cls)
1217 {
1218   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1219                                       GNUNET_SCHEDULER_NO_TASK,
1220                                       GNUNET_TIME_UNIT_ZERO,
1221                                       NULL, NULL, task, task_cls);
1222 }
1223
1224
1225 /**
1226  * Schedule a new task to be run as soon as possible with the
1227  * (transitive) ignore-shutdown flag either explicitly set or
1228  * explicitly enabled.  This task (and all tasks created from it,
1229  * other than by another call to this function) will either count or
1230  * not count for the 'lifeness' of the process.  This API is only
1231  * useful in a few special cases.
1232  *
1233  * @param lifeness GNUNET_YES if the task counts for lifeness, GNUNET_NO if not.
1234  * @param task main function of the task
1235  * @param task_cls closure of task
1236  * @return unique task identifier for the job
1237  *         only valid until "task" is started!
1238  */
1239 GNUNET_SCHEDULER_TaskIdentifier
1240 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1241                                         GNUNET_SCHEDULER_Task task,
1242                                         void *task_cls)
1243 {
1244   GNUNET_SCHEDULER_TaskIdentifier ret;
1245
1246   ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1247                                      GNUNET_SCHEDULER_NO_TASK,
1248                                      GNUNET_TIME_UNIT_ZERO,
1249                                      NULL, NULL, task, task_cls);
1250   GNUNET_assert (pending->id == ret);
1251   pending->lifeness = lifeness;
1252   return ret;
1253 }
1254
1255
1256
1257
1258 /**
1259  * Schedule a new task to be run with a specified delay or when any of
1260  * the specified file descriptor sets is ready.  The delay can be used
1261  * as a timeout on the socket(s) being ready.  The task will be
1262  * scheduled for execution once either the delay has expired or any of
1263  * the socket operations is ready.  This is the most general
1264  * function of the "add" family.  Note that the "prerequisite_task"
1265  * must be satisfied in addition to any of the other conditions.  In
1266  * other words, the task will be started when
1267  * <code>
1268  * (prerequisite-run)
1269  * && (delay-ready
1270  *     || any-rs-ready
1271  *     || any-ws-ready
1272  *     || shutdown-active )
1273  * </code>
1274  *
1275  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1276  *        which means that the task will only be run after we receive SIGTERM
1277  * @param rfd file descriptor we want to read (can be -1)
1278  * @param wfd file descriptors we want to write (can be -1)
1279  * @param task main function of the task
1280  * @param task_cls closure of task
1281  * @return unique task identifier for the job
1282  *         only valid until "task" is started!
1283  */
1284 #ifndef MINGW
1285 GNUNET_SCHEDULER_TaskIdentifier
1286 add_without_sets (struct GNUNET_TIME_Relative delay,
1287                   int rfd,
1288                   int wfd,
1289                   GNUNET_SCHEDULER_Task task, void *task_cls)
1290 {
1291   struct Task *t;
1292 #if EXECINFO
1293   void *backtrace_array[MAX_TRACE_DEPTH];
1294 #endif
1295
1296   GNUNET_assert (active_task != NULL);
1297   GNUNET_assert (NULL != task);
1298   t = GNUNET_malloc (sizeof (struct Task));
1299   t->callback = task;
1300   t->callback_cls = task_cls;
1301 #if EXECINFO
1302   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1303   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1304 #endif
1305 #if DEBUG_FDS
1306   if (-1 != rfd)
1307     {
1308       int flags = fcntl(rfd, F_GETFD);
1309       if (flags == -1 && errno == EBADF)
1310         {
1311           GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", rfd);
1312 #if EXECINFO
1313           int i;
1314
1315           for (i=0;i<t->num_backtrace_strings;i++)
1316             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1317                         "Trace: %s\n",
1318                         t->backtrace_strings[i]);
1319 #endif
1320           GNUNET_assert(0);
1321         }
1322     }
1323   if (-1 != wfd)
1324     {
1325       int flags = fcntl(wfd, F_GETFD);
1326       if (flags == -1 && errno == EBADF)
1327         {
1328           GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", wfd);
1329 #if EXECINFO
1330           int i;
1331
1332           for (i=0;i<t->num_backtrace_strings;i++)
1333             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1334                         "Trace: %s\n",
1335                         t->backtrace_strings[i]);
1336 #endif
1337           GNUNET_assert(0);
1338         }
1339     }
1340 #endif
1341   t->read_fd = rfd;
1342   GNUNET_assert(wfd >= -1);
1343   t->write_fd = wfd;
1344   t->id = ++last_id;
1345 #if PROFILE_DELAYS
1346   t->start_time = GNUNET_TIME_absolute_get ();
1347 #endif
1348   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1349   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1350   t->priority = check_priority (current_priority);
1351   t->lifeness = current_lifeness;
1352   t->next = pending;
1353   pending = t;
1354   max_priority_added = GNUNET_MAX (max_priority_added,
1355                                           t->priority);
1356 #if DEBUG_TASKS
1357   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1358               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1359 #endif
1360 #if EXECINFO
1361   int i;
1362
1363   for (i=0;i<t->num_backtrace_strings;i++)
1364       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1365                   "Task %llu trace %d: %s\n",
1366                   t->id,
1367                   i,
1368                   t->backtrace_strings[i]);
1369 #endif
1370   return t->id;
1371 }
1372 #endif
1373
1374
1375
1376 /**
1377  * Schedule a new task to be run with a specified delay or when the
1378  * specified file descriptor is ready for reading.  The delay can be
1379  * used as a timeout on the socket being ready.  The task will be
1380  * scheduled for execution once either the delay has expired or the
1381  * socket operation is ready.  It will be run with the priority of
1382  * the calling task.
1383  *
1384  * @param delay when should this operation time out? Use 
1385  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1386  * @param rfd read file-descriptor
1387  * @param task main function of the task
1388  * @param task_cls closure of task
1389  * @return unique task identifier for the job
1390  *         only valid until "task" is started!
1391  */
1392 GNUNET_SCHEDULER_TaskIdentifier
1393 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1394                                struct GNUNET_NETWORK_Handle * rfd,
1395                                GNUNET_SCHEDULER_Task task, void *task_cls)
1396 {
1397 #if MINGW
1398   struct GNUNET_NETWORK_FDSet *rs;
1399   GNUNET_SCHEDULER_TaskIdentifier ret;
1400
1401   GNUNET_assert (rfd != NULL);
1402   rs = GNUNET_NETWORK_fdset_create ();
1403   GNUNET_NETWORK_fdset_set (rs, rfd);
1404   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1405                                      GNUNET_SCHEDULER_NO_TASK, delay,
1406                                      rs, NULL, task, task_cls);
1407   GNUNET_NETWORK_fdset_destroy (rs);
1408   return ret;
1409 #else
1410   return add_without_sets (delay,
1411                            GNUNET_NETWORK_get_fd (rfd),
1412                            -1,
1413                            task,
1414                            task_cls);
1415 #endif
1416 }
1417
1418
1419 /**
1420  * Schedule a new task to be run with a specified delay or when the
1421  * specified file descriptor is ready for writing.  The delay can be
1422  * used as a timeout on the socket being ready.  The task will be
1423  * scheduled for execution once either the delay has expired or the
1424  * socket operation is ready.  It will be run with the priority of
1425  * the calling task.
1426  *
1427  * @param delay when should this operation time out? Use 
1428  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1429  * @param wfd write file-descriptor
1430  * @param task main function of the task
1431  * @param task_cls closure of task
1432  * @return unique task identifier for the job
1433  *         only valid until "task" is started!
1434  */
1435 GNUNET_SCHEDULER_TaskIdentifier
1436 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1437                                 struct GNUNET_NETWORK_Handle * wfd,
1438                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1439 {
1440 #if MINGW
1441   struct GNUNET_NETWORK_FDSet *ws;
1442   GNUNET_SCHEDULER_TaskIdentifier ret;
1443
1444   GNUNET_assert (wfd != NULL);
1445   ws = GNUNET_NETWORK_fdset_create ();
1446   GNUNET_NETWORK_fdset_set (ws, wfd);
1447   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1448                                      GNUNET_SCHEDULER_NO_TASK, delay,
1449                                      NULL, ws, task, task_cls);
1450   GNUNET_NETWORK_fdset_destroy (ws);
1451   return ret;
1452 #else
1453   GNUNET_assert(GNUNET_NETWORK_get_fd(wfd) >= 0);
1454   return add_without_sets (delay,
1455                            -1,
1456                            GNUNET_NETWORK_get_fd (wfd),
1457                            task,
1458                            task_cls);
1459 #endif
1460 }
1461
1462
1463 /**
1464  * Schedule a new task to be run with a specified delay or when the
1465  * specified file descriptor is ready for reading.  The delay can be
1466  * used as a timeout on the socket being ready.  The task will be
1467  * scheduled for execution once either the delay has expired or the
1468  * socket operation is ready. It will be run with the priority of
1469  * the calling task.
1470  *
1471  * @param delay when should this operation time out? Use 
1472  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1473  * @param rfd read file-descriptor
1474  * @param task main function of the task
1475  * @param task_cls closure of task
1476  * @return unique task identifier for the job
1477  *         only valid until "task" is started!
1478  */
1479 GNUNET_SCHEDULER_TaskIdentifier
1480 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1481                                 const struct GNUNET_DISK_FileHandle * rfd,
1482                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1483 {
1484 #if MINGW
1485   struct GNUNET_NETWORK_FDSet *rs;
1486   GNUNET_SCHEDULER_TaskIdentifier ret;
1487
1488   GNUNET_assert (rfd != NULL);
1489   rs = GNUNET_NETWORK_fdset_create ();
1490   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1491   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1492                                      GNUNET_SCHEDULER_NO_TASK, delay,
1493                                      rs, NULL, task, task_cls);
1494   GNUNET_NETWORK_fdset_destroy (rs);
1495   return ret;
1496 #else
1497   int fd;
1498
1499   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1500   return add_without_sets (delay,
1501                            fd,
1502                            -1,
1503                            task,
1504                            task_cls);
1505
1506 #endif
1507 }
1508
1509
1510 /**
1511  * Schedule a new task to be run with a specified delay or when the
1512  * specified file descriptor is ready for writing.  The delay can be
1513  * used as a timeout on the socket being ready.  The task will be
1514  * scheduled for execution once either the delay has expired or the
1515  * socket operation is ready. It will be run with the priority of
1516  * the calling task.
1517  *
1518  * @param delay when should this operation time out? Use 
1519  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1520  * @param wfd write file-descriptor
1521  * @param task main function of the task
1522  * @param task_cls closure of task
1523  * @return unique task identifier for the job
1524  *         only valid until "task" is started!
1525  */
1526 GNUNET_SCHEDULER_TaskIdentifier
1527 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1528                                  const struct GNUNET_DISK_FileHandle * wfd,
1529                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1530 {
1531 #if MINGW
1532   struct GNUNET_NETWORK_FDSet *ws;
1533   GNUNET_SCHEDULER_TaskIdentifier ret;
1534
1535   GNUNET_assert (wfd != NULL);
1536   ws = GNUNET_NETWORK_fdset_create ();
1537   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1538   ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
1539                                      GNUNET_SCHEDULER_NO_TASK,
1540                                      delay, NULL, ws, task, task_cls);
1541   GNUNET_NETWORK_fdset_destroy (ws);
1542   return ret;
1543 #else
1544   int fd;
1545
1546   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1547   GNUNET_assert(fd >= 0);
1548   return add_without_sets (delay,
1549                            -1,
1550                            fd,
1551                            task,
1552                            task_cls);
1553
1554 #endif
1555 }
1556
1557
1558
1559 /**
1560  * Schedule a new task to be run with a specified delay or when any of
1561  * the specified file descriptor sets is ready.  The delay can be used
1562  * as a timeout on the socket(s) being ready.  The task will be
1563  * scheduled for execution once either the delay has expired or any of
1564  * the socket operations is ready.  This is the most general
1565  * function of the "add" family.  Note that the "prerequisite_task"
1566  * must be satisfied in addition to any of the other conditions.  In
1567  * other words, the task will be started when
1568  * <code>
1569  * (prerequisite-run)
1570  * && (delay-ready
1571  *     || any-rs-ready
1572  *     || any-ws-ready
1573  *     || (shutdown-active && run-on-shutdown) )
1574  * </code>
1575  *
1576  * @param prio how important is this task?
1577  * @param prerequisite_task run this task after the task with the given
1578  *        task identifier completes (and any of our other
1579  *        conditions, such as delay, read or write-readiness
1580  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1581  *        on completion of other tasks.
1582  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1583  *        which means that the task will only be run after we receive SIGTERM
1584  * @param rs set of file descriptors we want to read (can be NULL)
1585  * @param ws set of file descriptors we want to write (can be NULL)
1586  * @param task main function of the task
1587  * @param task_cls closure of task
1588  * @return unique task identifier for the job
1589  *         only valid until "task" is started!
1590  */
1591 GNUNET_SCHEDULER_TaskIdentifier
1592 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1593                              GNUNET_SCHEDULER_TaskIdentifier
1594                              prerequisite_task,
1595                              struct GNUNET_TIME_Relative delay,
1596                              const struct GNUNET_NETWORK_FDSet * rs,
1597                              const struct GNUNET_NETWORK_FDSet * ws,
1598                              GNUNET_SCHEDULER_Task task, void *task_cls)
1599 {
1600   struct Task *t;
1601 #if EXECINFO
1602   void *backtrace_array[MAX_TRACE_DEPTH];
1603 #endif
1604
1605   GNUNET_assert (active_task != NULL);
1606   GNUNET_assert (NULL != task);
1607   t = GNUNET_malloc (sizeof (struct Task));
1608   t->callback = task;
1609   t->callback_cls = task_cls;
1610 #if EXECINFO
1611   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1612   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1613 #endif
1614   t->read_fd = -1;
1615   t->write_fd = -1;
1616   if (rs != NULL)
1617     {
1618       t->read_set = GNUNET_NETWORK_fdset_create ();
1619       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1620     }
1621   if (ws != NULL)
1622     {
1623       t->write_set = GNUNET_NETWORK_fdset_create ();
1624       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1625     }
1626   t->id = ++last_id;
1627 #if PROFILE_DELAYS
1628   t->start_time = GNUNET_TIME_absolute_get ();
1629 #endif
1630   t->prereq_id = prerequisite_task;
1631   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1632   t->priority =
1633     check_priority ((prio ==
1634                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority
1635                     : prio);
1636   t->lifeness = current_lifeness;
1637   t->next = pending;
1638   pending = t;
1639   max_priority_added = GNUNET_MAX (max_priority_added,
1640                                    t->priority);
1641 #if DEBUG_TASKS
1642   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1643               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1644 #endif
1645 #if EXECINFO
1646   int i;
1647
1648   for (i=0;i<t->num_backtrace_strings;i++)
1649       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1650                   "Task %llu trace %d: %s\n",
1651                   t->id,
1652                   i,
1653                   t->backtrace_strings[i]);
1654 #endif
1655   return t->id;
1656 }
1657
1658 /* end of scheduler.c */