assert
[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
709   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
710                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE), &c,
711                           sizeof (c));
712 }
713
714
715 /**
716  * Initialize and run scheduler.  This function will return when all
717  * tasks have completed.  On systems with signals, receiving a SIGTERM
718  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
719  * to be run after the active task is complete.  As a result, SIGTERM
720  * causes all active tasks to be scheduled with reason
721  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
722  * afterwards will execute normally!). Note that any particular signal
723  * will only shut down one scheduler; applications should always only
724  * create a single scheduler.
725  *
726  * @param task task to run immediately
727  * @param task_cls closure of task
728  */
729 void
730 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
731 {
732   struct GNUNET_SCHEDULER_Handle sched;
733   struct GNUNET_NETWORK_FDSet *rs;
734   struct GNUNET_NETWORK_FDSet *ws;
735   struct GNUNET_TIME_Relative timeout;
736   int ret;
737   struct GNUNET_SIGNAL_Context *shc_int;
738   struct GNUNET_SIGNAL_Context *shc_term;
739   struct GNUNET_SIGNAL_Context *shc_quit;
740   struct GNUNET_SIGNAL_Context *shc_hup;
741   struct GNUNET_SIGNAL_Context *shc_pipe;
742   unsigned long long last_tr;
743   unsigned int busy_wait_warning;
744   const struct GNUNET_DISK_FileHandle *pr;
745   char c;
746
747   rs = GNUNET_NETWORK_fdset_create ();
748   ws = GNUNET_NETWORK_fdset_create ();
749   GNUNET_assert (shutdown_pipe_handle == NULL);
750   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO);
751   GNUNET_assert (shutdown_pipe_handle != NULL);
752   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_READ);
753   GNUNET_assert (pr != NULL);
754   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
755   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
756 #ifndef MINGW
757   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
758   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
759   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
760 #endif
761   memset (&sched, 0, sizeof (sched));
762   sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
763   GNUNET_SCHEDULER_add_continuation (&sched,
764                                      task,
765                                      task_cls,
766                                      GNUNET_SCHEDULER_REASON_STARTUP);
767   last_tr = 0;
768   busy_wait_warning = 0;
769   while ((sched.pending != NULL) || 
770          (sched.pending_timeout != NULL) ||
771          (sched.ready_count > 0))
772     {
773       GNUNET_NETWORK_fdset_zero (rs);
774       GNUNET_NETWORK_fdset_zero (ws);
775       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
776       update_sets (&sched, rs, ws, &timeout);
777       GNUNET_NETWORK_fdset_handle_set (rs, pr);
778       if (sched.ready_count > 0)
779         {
780           /* no blocking, more work already ready! */
781           timeout = GNUNET_TIME_UNIT_ZERO;
782         }
783       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
784       if (ret == GNUNET_SYSERR)
785         {
786           if (errno == EINTR)
787             continue;
788
789           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
790 #ifndef MINGW
791 #if USE_LSOF
792           char lsof[512];
793           snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
794           close (1);
795           dup2 (2, 1);
796           system (lsof);                  
797 #endif
798 #endif
799           abort ();
800           break;
801         }
802       if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
803         {
804           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
805                       _("Looks like we're busy waiting...\n"));
806           sleep (1);            /* mitigate */
807         }
808       check_ready (&sched, rs, ws);
809       run_ready (&sched, rs, ws);
810       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
811         {
812           /* consume the signal */
813           GNUNET_DISK_file_read (pr, &c, sizeof (c));
814           /* mark all active tasks as ready due to shutdown */
815           GNUNET_SCHEDULER_shutdown (&sched);
816         }
817       if (last_tr == sched.tasks_run)
818         {
819           busy_wait_warning++;
820         }
821       else
822         {
823           last_tr = sched.tasks_run;
824           busy_wait_warning = 0;
825         }
826     }
827   GNUNET_SIGNAL_handler_uninstall (shc_int);
828   GNUNET_SIGNAL_handler_uninstall (shc_term);
829 #ifndef MINGW
830   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
831   GNUNET_SIGNAL_handler_uninstall (shc_quit);
832   GNUNET_SIGNAL_handler_uninstall (shc_hup);
833 #endif
834   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
835   shutdown_pipe_handle = NULL;
836   GNUNET_NETWORK_fdset_destroy (rs);
837   GNUNET_NETWORK_fdset_destroy (ws);
838 }
839
840
841 /**
842  * Obtain the reason code for why the current task was
843  * started.  Will return the same value as 
844  * the GNUNET_SCHEDULER_TaskContext's reason field.
845  *
846  * @param sched scheduler to query
847  * @return reason(s) why the current task is run
848  */
849 enum GNUNET_SCHEDULER_Reason
850 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
851 {
852   return sched->active_task->reason;
853 }
854
855
856 /**
857  * Get information about the current load of this scheduler.  Use this
858  * function to determine if an elective task should be added or simply
859  * dropped (if the decision should be made based on the number of
860  * tasks ready to run).
861  *
862  * @param sched scheduler to query
863  * @param p priority level to look at
864  * @return number of tasks pending right now
865  */
866 unsigned int
867 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
868                            enum GNUNET_SCHEDULER_Priority p)
869 {
870   struct Task *pos;
871   unsigned int ret;
872
873   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
874     return sched->ready_count;
875   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
876     p = sched->current_priority;
877   ret = 0;
878   pos = sched->ready[check_priority (p)];
879   while (pos != NULL)
880     {
881       pos = pos->next;
882       ret++;
883     }
884   return ret;
885 }
886
887
888 /**
889  * Cancel the task with the specified identifier.
890  * The task must not yet have run.
891  *
892  * @param sched scheduler to use
893  * @param task id of the task to cancel
894  * @return original closure of the task
895  */
896 void *
897 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
898                          GNUNET_SCHEDULER_TaskIdentifier task)
899 {
900   struct Task *t;
901   struct Task *prev;
902   enum GNUNET_SCHEDULER_Priority p;
903   int to;
904   void *ret;
905
906   to = 0;
907   prev = NULL;
908   t = sched->pending;
909   while (t != NULL)
910     {
911       if (t->id == task)
912         break;
913       prev = t;
914       t = t->next;
915     }
916   if (t == NULL)
917     {
918       prev = NULL;
919       to = 1;
920       t = sched->pending_timeout;
921       while (t != NULL)
922         {
923           if (t->id == task)
924             break;
925           prev = t;
926           t = t->next;
927         }
928       if (sched->pending_timeout_last == t)
929         sched->pending_timeout_last = NULL;
930     }
931   p = 0;
932   while (t == NULL)
933     {
934       p++;
935       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
936       prev = NULL;
937       t = sched->ready[p];
938       while (t != NULL)
939         {
940           if (t->id == task)
941             {
942               sched->ready_count--;
943               break;
944             }
945           prev = t;
946           t = t->next;
947         }
948     }
949   if (prev == NULL)
950     {
951       if (p == 0)
952         {
953           if (to == 0)
954             {
955               sched->pending = t->next;
956             }
957           else
958             {
959               sched->pending_timeout = t->next;
960             }
961         }
962       else
963         {
964           sched->ready[p] = t->next;
965         }
966     }
967   else
968     {
969       prev->next = t->next;
970     }
971   ret = t->callback_cls;
972 #if DEBUG_TASKS
973   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974               "Canceling task: %llu / %p\n", task, t->callback_cls);
975 #endif
976   destroy_task (t);
977   return ret;
978 }
979
980
981 /**
982  * Continue the current execution with the given function.  This is
983  * similar to the other "add" functions except that there is no delay
984  * and the reason code can be specified.
985  *
986  * @param sched scheduler to use
987  * @param task main function of the task
988  * @param task_cls closure for 'main'
989  * @param reason reason for task invocation
990  */
991 void
992 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
993                                    GNUNET_SCHEDULER_Task task,
994                                    void *task_cls,
995                                    enum GNUNET_SCHEDULER_Reason reason)
996 {
997   struct Task *t;
998 #if EXECINFO
999   void *backtrace_array[50];
1000 #endif
1001   t = GNUNET_malloc (sizeof (struct Task));
1002 #if EXECINFO
1003   t->num_backtrace_strings = backtrace(backtrace_array, 50);
1004   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1005 #endif
1006   t->read_fd = -1;
1007   t->write_fd = -1;
1008   t->callback = task;
1009   t->callback_cls = task_cls;
1010   t->id = ++sched->last_id;
1011 #if PROFILE_DELAYS
1012   t->start_time = GNUNET_TIME_absolute_get ();
1013 #endif
1014   t->reason = reason;
1015   t->priority = sched->current_priority;
1016 #if DEBUG_TASKS
1017   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018               "Adding continuation task: %llu / %p\n",
1019               t->id, t->callback_cls);
1020 #endif
1021   queue_ready_task (sched, t);
1022 }
1023
1024
1025
1026 /**
1027  * Schedule a new task to be run after the specified prerequisite task
1028  * has completed. It will be run with the priority of the calling
1029  * task.
1030  *
1031  * @param sched scheduler to use
1032  * @param prerequisite_task run this task after the task with the given
1033  *        task identifier completes (and any of our other
1034  *        conditions, such as delay, read or write-readiness
1035  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
1036  *        on completion of other tasks (this will cause the task to run as
1037  *        soon as possible).
1038  * @param task main function of the task
1039  * @param task_cls closure of task
1040  * @return unique task identifier for the job
1041  *         only valid until "task" is started!
1042  */
1043 GNUNET_SCHEDULER_TaskIdentifier
1044 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
1045                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
1046                             GNUNET_SCHEDULER_Task task, void *task_cls)
1047 {
1048   return GNUNET_SCHEDULER_add_select (sched,
1049                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1050                                       prerequisite_task,
1051                                       GNUNET_TIME_UNIT_ZERO,
1052                                       NULL, NULL, task, task_cls);
1053 }
1054
1055
1056 /**
1057  * Schedule a new task to be run with a specified priority.
1058  *
1059  * @param sched scheduler to use
1060  * @param prio how important is the new task?
1061  * @param task main function of the task
1062  * @param task_cls closure of task
1063  * @return unique task identifier for the job
1064  *         only valid until "task" is started!
1065  */
1066 GNUNET_SCHEDULER_TaskIdentifier
1067 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
1068                                     enum GNUNET_SCHEDULER_Priority prio,
1069                                     GNUNET_SCHEDULER_Task task,
1070                                     void *task_cls)
1071 {
1072   return GNUNET_SCHEDULER_add_select (sched,
1073                                       prio,
1074                                       GNUNET_SCHEDULER_NO_TASK,
1075                                       GNUNET_TIME_UNIT_ZERO,
1076                                       NULL, NULL, task, task_cls);
1077 }
1078
1079
1080
1081 /**
1082  * Schedule a new task to be run with a specified delay.  The task
1083  * will be scheduled for execution once the delay has expired. It
1084  * will be run with the priority of the calling task.
1085  *
1086  * @param sched scheduler to use
1087  * @param delay when should this operation time out? Use 
1088  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1089  * @param task main function of the task
1090  * @param task_cls closure of task
1091  * @return unique task identifier for the job
1092  *         only valid until "task" is started!
1093  */
1094 GNUNET_SCHEDULER_TaskIdentifier
1095 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
1096                               struct GNUNET_TIME_Relative delay,
1097                               GNUNET_SCHEDULER_Task task, void *task_cls)
1098 {
1099 #if 1
1100   /* new, optimized version */
1101   struct Task *t;
1102   struct Task *pos;
1103   struct Task *prev;
1104 #if EXECINFO
1105   void *backtrace_array[MAX_TRACE_DEPTH];
1106 #endif
1107
1108   GNUNET_assert (NULL != task);
1109   t = GNUNET_malloc (sizeof (struct Task));
1110   t->callback = task;
1111   t->callback_cls = task_cls;
1112 #if EXECINFO
1113   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1114   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1115 #endif
1116   t->read_fd = -1;
1117   t->write_fd = -1;
1118   t->id = ++sched->last_id;
1119 #if PROFILE_DELAYS
1120   t->start_time = GNUNET_TIME_absolute_get ();
1121 #endif
1122   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1123   t->priority = sched->current_priority;
1124   /* try tail first (optimization in case we are
1125      appending to a long list of tasks with timeouts) */
1126   prev = sched->pending_timeout_last;
1127   if (prev != NULL) 
1128     {
1129       if (prev->timeout.value > t->timeout.value)
1130         prev = NULL;
1131       else
1132         pos = prev->next; /* heuristic success! */
1133     }
1134   if (prev == NULL)
1135     {
1136       /* heuristic failed, do traversal of timeout list */
1137       pos = sched->pending_timeout;
1138     }
1139   while ( (pos != NULL) &&
1140           ( (pos->timeout.value <= t->timeout.value) ||
1141             (pos->reason != 0) ) )
1142     {
1143       prev = pos;
1144       pos = pos->next;
1145     }
1146   if (prev == NULL)
1147     sched->pending_timeout = t;
1148   else
1149     prev->next = t;
1150   t->next = pos;
1151   /* hyper-optimization... */
1152   sched->pending_timeout_last = t;
1153
1154 #if DEBUG_TASKS
1155   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1156               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1157 #endif
1158 #if EXECINFO
1159   int i;
1160
1161   for (i=0;i<t->num_backtrace_strings;i++)
1162       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163                   "Task %u trace %d: %s\n",
1164                   t->id,
1165                   i,
1166                   t->backtrace_strings[i]);
1167 #endif
1168   return t->id;
1169
1170 #else
1171   /* unoptimized version */
1172   return GNUNET_SCHEDULER_add_select (sched,
1173                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1174                                       GNUNET_SCHEDULER_NO_TASK, delay,
1175                                       NULL, NULL, task, task_cls);
1176 #endif
1177 }
1178
1179
1180
1181 /**
1182  * Schedule a new task to be run as soon as possible. The task
1183  * will be run with the priority of the calling task.
1184  *
1185  * @param sched scheduler to use
1186  * @param task main function of the task
1187  * @param task_cls closure of task
1188  * @return unique task identifier for the job
1189  *         only valid until "task" is started!
1190  */
1191 GNUNET_SCHEDULER_TaskIdentifier
1192 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
1193                           GNUNET_SCHEDULER_Task task,
1194                           void *task_cls)
1195 {
1196   return GNUNET_SCHEDULER_add_select (sched,
1197                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
1198                                       GNUNET_SCHEDULER_NO_TASK,
1199                                       GNUNET_TIME_UNIT_ZERO,
1200                                       NULL, NULL, task, task_cls);
1201 }
1202
1203
1204
1205
1206 /**
1207  * Schedule a new task to be run with a specified delay or when any of
1208  * the specified file descriptor sets is ready.  The delay can be used
1209  * as a timeout on the socket(s) being ready.  The task will be
1210  * scheduled for execution once either the delay has expired or any of
1211  * the socket operations is ready.  This is the most general
1212  * function of the "add" family.  Note that the "prerequisite_task"
1213  * must be satisfied in addition to any of the other conditions.  In
1214  * other words, the task will be started when
1215  * <code>
1216  * (prerequisite-run)
1217  * && (delay-ready
1218  *     || any-rs-ready
1219  *     || any-ws-ready
1220  *     || shutdown-active )
1221  * </code>
1222  *
1223  * @param sched scheduler to use
1224  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1225  *        which means that the task will only be run after we receive SIGTERM
1226  * @param rfd file descriptor we want to read (can be -1)
1227  * @param wfd file descriptors we want to write (can be -1)
1228  * @param task main function of the task
1229  * @param task_cls closure of task
1230  * @return unique task identifier for the job
1231  *         only valid until "task" is started!
1232  */
1233 GNUNET_SCHEDULER_TaskIdentifier
1234 add_without_sets (struct GNUNET_SCHEDULER_Handle * sched,
1235                   struct GNUNET_TIME_Relative delay,
1236                   int rfd,
1237                   int wfd,
1238                   GNUNET_SCHEDULER_Task task, void *task_cls)
1239 {
1240   struct Task *t;
1241 #if EXECINFO
1242   void *backtrace_array[MAX_TRACE_DEPTH];
1243 #endif
1244
1245   GNUNET_assert (NULL != task);
1246   t = GNUNET_malloc (sizeof (struct Task));
1247   t->callback = task;
1248   t->callback_cls = task_cls;
1249 #if EXECINFO
1250   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1251   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1252 #endif
1253   t->read_fd = rfd;
1254   t->write_fd = wfd;
1255   t->id = ++sched->last_id;
1256 #if PROFILE_DELAYS
1257   t->start_time = GNUNET_TIME_absolute_get ();
1258 #endif
1259   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1260   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1261   t->priority = check_priority (sched->current_priority);
1262   t->next = sched->pending;
1263   sched->pending = t;
1264   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1265                                           t->priority);
1266 #if DEBUG_TASKS
1267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1268               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1269 #endif
1270 #if EXECINFO
1271   int i;
1272
1273   for (i=0;i<t->num_backtrace_strings;i++)
1274       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1275                   "Task %u trace %d: %s\n",
1276                   t->id,
1277                   i,
1278                   t->backtrace_strings[i]);
1279 #endif
1280   return t->id;
1281 }
1282
1283
1284
1285 /**
1286  * Schedule a new task to be run with a specified delay or when the
1287  * specified file descriptor is ready for reading.  The delay can be
1288  * used as a timeout on the socket being ready.  The task will be
1289  * scheduled for execution once either the delay has expired or the
1290  * socket operation is ready.  It will be run with the priority of
1291  * the calling task.
1292  *
1293  * @param sched scheduler to use
1294  * @param delay when should this operation time out? Use 
1295  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1296  * @param rfd read file-descriptor
1297  * @param task main function of the task
1298  * @param task_cls closure of task
1299  * @return unique task identifier for the job
1300  *         only valid until "task" is started!
1301  */
1302 GNUNET_SCHEDULER_TaskIdentifier
1303 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
1304                                struct GNUNET_TIME_Relative delay,
1305                                struct GNUNET_NETWORK_Handle * rfd,
1306                                GNUNET_SCHEDULER_Task task, void *task_cls)
1307 {
1308   return add_without_sets (sched,
1309                            delay,
1310                            GNUNET_NETWORK_get_fd (rfd),
1311                            -1,
1312                            task,
1313                            task_cls);
1314 }
1315
1316
1317 /**
1318  * Schedule a new task to be run with a specified delay or when the
1319  * specified file descriptor is ready for writing.  The delay can be
1320  * used as a timeout on the socket being ready.  The task will be
1321  * scheduled for execution once either the delay has expired or the
1322  * socket operation is ready.  It will be run with the priority of
1323  * the calling task.
1324  *
1325  * @param sched scheduler to use
1326  * @param delay when should this operation time out? Use 
1327  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1328  * @param wfd write file-descriptor
1329  * @param task main function of the task
1330  * @param task_cls closure of task
1331  * @return unique task identifier for the job
1332  *         only valid until "task" is started!
1333  */
1334 GNUNET_SCHEDULER_TaskIdentifier
1335 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
1336                                 struct GNUNET_TIME_Relative delay,
1337                                 struct GNUNET_NETWORK_Handle * wfd,
1338                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1339 {
1340   return add_without_sets (sched,
1341                            delay,
1342                            -1,
1343                            GNUNET_NETWORK_get_fd (wfd),
1344                            task,
1345                            task_cls);
1346 }
1347
1348
1349 /**
1350  * Schedule a new task to be run with a specified delay or when the
1351  * specified file descriptor is ready for reading.  The delay can be
1352  * used as a timeout on the socket being ready.  The task will be
1353  * scheduled for execution once either the delay has expired or the
1354  * socket operation is ready. It will be run with the priority of
1355  * the calling task.
1356  *
1357  * @param sched scheduler to use
1358  * @param delay when should this operation time out? Use 
1359  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1360  * @param rfd read file-descriptor
1361  * @param task main function of the task
1362  * @param task_cls closure of task
1363  * @return unique task identifier for the job
1364  *         only valid until "task" is started!
1365  */
1366 GNUNET_SCHEDULER_TaskIdentifier
1367 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1368                                 struct GNUNET_TIME_Relative delay,
1369                                 const struct GNUNET_DISK_FileHandle * rfd,
1370                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1371 {
1372 #if MINGW
1373   struct GNUNET_NETWORK_FDSet *rs;
1374   GNUNET_SCHEDULER_TaskIdentifier ret;
1375
1376   GNUNET_assert (rfd != NULL);
1377   rs = GNUNET_NETWORK_fdset_create ();
1378   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1379   ret = GNUNET_SCHEDULER_add_select (sched,
1380                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1381                                      GNUNET_SCHEDULER_NO_TASK, delay,
1382                                      rs, NULL, task, task_cls);
1383   GNUNET_NETWORK_fdset_destroy (rs);
1384   return ret;
1385 #else
1386   int fd;
1387
1388   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1389   return add_without_sets (sched,
1390                            delay,
1391                            fd,
1392                            -1,
1393                            task,
1394                            task_cls);
1395
1396 #endif
1397 }
1398
1399
1400 /**
1401  * Schedule a new task to be run with a specified delay or when the
1402  * specified file descriptor is ready for writing.  The delay can be
1403  * used as a timeout on the socket being ready.  The task will be
1404  * scheduled for execution once either the delay has expired or the
1405  * socket operation is ready. It will be run with the priority of
1406  * the calling task.
1407  *
1408  * @param sched scheduler to use
1409  * @param delay when should this operation time out? Use 
1410  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1411  * @param wfd write file-descriptor
1412  * @param task main function of the task
1413  * @param task_cls closure of task
1414  * @return unique task identifier for the job
1415  *         only valid until "task" is started!
1416  */
1417 GNUNET_SCHEDULER_TaskIdentifier
1418 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1419                                  struct GNUNET_TIME_Relative delay,
1420                                  const struct GNUNET_DISK_FileHandle * wfd,
1421                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1422 {
1423 #if MINGW
1424   struct GNUNET_NETWORK_FDSet *ws;
1425   GNUNET_SCHEDULER_TaskIdentifier ret;
1426
1427   GNUNET_assert (wfd != NULL);
1428   ws = GNUNET_NETWORK_fdset_create ();
1429   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1430   ret = GNUNET_SCHEDULER_add_select (sched,
1431                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1432                                      GNUNET_SCHEDULER_NO_TASK,
1433                                      delay, NULL, ws, task, task_cls);
1434   GNUNET_NETWORK_fdset_destroy (ws);
1435   return ret;
1436 #else
1437   int fd;
1438
1439   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1440   return add_without_sets (sched,
1441                            delay,
1442                            -1,
1443                            fd,
1444                            task,
1445                            task_cls);
1446
1447 #endif
1448 }
1449
1450
1451
1452 /**
1453  * Schedule a new task to be run with a specified delay or when any of
1454  * the specified file descriptor sets is ready.  The delay can be used
1455  * as a timeout on the socket(s) being ready.  The task will be
1456  * scheduled for execution once either the delay has expired or any of
1457  * the socket operations is ready.  This is the most general
1458  * function of the "add" family.  Note that the "prerequisite_task"
1459  * must be satisfied in addition to any of the other conditions.  In
1460  * other words, the task will be started when
1461  * <code>
1462  * (prerequisite-run)
1463  * && (delay-ready
1464  *     || any-rs-ready
1465  *     || any-ws-ready
1466  *     || (shutdown-active && run-on-shutdown) )
1467  * </code>
1468  *
1469  * @param sched scheduler to use
1470  * @param prio how important is this task?
1471  * @param prerequisite_task run this task after the task with the given
1472  *        task identifier completes (and any of our other
1473  *        conditions, such as delay, read or write-readiness
1474  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1475  *        on completion of other tasks.
1476  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1477  *        which means that the task will only be run after we receive SIGTERM
1478  * @param rs set of file descriptors we want to read (can be NULL)
1479  * @param ws set of file descriptors we want to write (can be NULL)
1480  * @param task main function of the task
1481  * @param task_cls closure of task
1482  * @return unique task identifier for the job
1483  *         only valid until "task" is started!
1484  */
1485 GNUNET_SCHEDULER_TaskIdentifier
1486 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1487                              enum GNUNET_SCHEDULER_Priority prio,
1488                              GNUNET_SCHEDULER_TaskIdentifier
1489                              prerequisite_task,
1490                              struct GNUNET_TIME_Relative delay,
1491                              const struct GNUNET_NETWORK_FDSet * rs,
1492                              const struct GNUNET_NETWORK_FDSet * ws,
1493                              GNUNET_SCHEDULER_Task task, void *task_cls)
1494 {
1495   struct Task *t;
1496 #if EXECINFO
1497   void *backtrace_array[MAX_TRACE_DEPTH];
1498 #endif
1499
1500   GNUNET_assert (NULL != task);
1501   t = GNUNET_malloc (sizeof (struct Task));
1502   t->callback = task;
1503   t->callback_cls = task_cls;
1504 #if EXECINFO
1505   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1506   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1507 #endif
1508   t->read_fd = -1;
1509   t->write_fd = -1;
1510   if (rs != NULL)
1511     {
1512       t->read_set = GNUNET_NETWORK_fdset_create ();
1513       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1514     }
1515   if (ws != NULL)
1516     {
1517       t->write_set = GNUNET_NETWORK_fdset_create ();
1518       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1519     }
1520   t->id = ++sched->last_id;
1521 #if PROFILE_DELAYS
1522   t->start_time = GNUNET_TIME_absolute_get ();
1523 #endif
1524   t->prereq_id = prerequisite_task;
1525   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1526   t->priority =
1527     check_priority ((prio ==
1528                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1529                     : prio);
1530   t->next = sched->pending; 
1531   sched->pending = t;
1532   sched->max_priority_added = GNUNET_MAX (sched->max_priority_added,
1533                                           t->priority);
1534 #if DEBUG_TASKS
1535   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1536               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1537 #endif
1538 #if EXECINFO
1539   int i;
1540
1541   for (i=0;i<t->num_backtrace_strings;i++)
1542       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1543                   "Task %u trace %d: %s\n",
1544                   t->id,
1545                   i,
1546                   t->backtrace_strings[i]);
1547 #endif
1548   return t->id;
1549 }
1550
1551 /* end of scheduler.c */