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