fix
[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 #ifdef LINUX
33 #include "execinfo.h"
34 #define EXECINFO GNUNET_NO
35 #define MAX_TRACE_DEPTH 50
36 #endif
37
38 #define DEBUG_TASKS GNUNET_NO
39
40 /**
41  * Linked list of pending tasks.
42  */
43 struct Task
44 {
45   /**
46    * This is a linked list.
47    */
48   struct Task *next;
49
50   /**
51    * Function to run when ready.
52    */
53   GNUNET_SCHEDULER_Task callback;
54
55   /**
56    * Closure for the callback.
57    */
58   void *callback_cls;
59
60   /**
61    * Set of file descriptors this task is waiting
62    * for for reading.  Once ready, this is updated
63    * to reflect the set of file descriptors ready
64    * for operation.
65    */
66   struct GNUNET_NETWORK_FDSet *read_set;
67
68   /**
69    * Set of file descriptors this task is waiting for for writing.
70    * Once ready, this is updated to reflect the set of file
71    * descriptors ready for operation.
72    */
73   struct GNUNET_NETWORK_FDSet *write_set;
74
75   /**
76    * Unique task identifier.
77    */
78   GNUNET_SCHEDULER_TaskIdentifier id;
79
80   /**
81    * Identifier of a prerequisite task.
82    */
83   GNUNET_SCHEDULER_TaskIdentifier prereq_id;
84
85   /**
86    * Absolute timeout value for the task, or
87    * GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
88    */
89   struct GNUNET_TIME_Absolute timeout;
90
91   /**
92    * Why is the task ready?  Set after task is added to ready queue.
93    * Initially set to zero.  All reasons that have already been
94    * satisfied (i.e.  read or write ready) will be set over time.
95    */
96   enum GNUNET_SCHEDULER_Reason reason;
97
98   /**
99    * Task priority.
100    */
101   enum GNUNET_SCHEDULER_Priority priority;
102
103 #if EXECINFO
104   /**
105    * Array of strings which make up a backtrace from the point when this
106    * task was scheduled (essentially, who scheduled the task?)
107    */
108   char **backtrace_strings;
109
110   /**
111    * Size of the backtrace_strings array
112    */
113   int num_backtrace_strings;
114 #endif
115
116 };
117
118
119 /**
120  * Handle for the scheduling service.
121  */
122 struct GNUNET_SCHEDULER_Handle
123 {
124
125   /**
126    * List of tasks waiting for an event.
127    */
128   struct Task *pending;
129
130   /**
131    * ID of the task that is running right now.
132    */
133   struct Task *active_task;
134
135   /**
136    * List of tasks ready to run right now,
137    * grouped by importance.
138    */
139   struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
140
141   /**
142    * Identity of the last task queued.  Incremented for each task to
143    * generate a unique task ID (it is virtually impossible to start
144    * more than 2^64 tasks during the lifetime of a process).
145    */
146   GNUNET_SCHEDULER_TaskIdentifier last_id;
147
148   /**
149    * Highest number so that all tasks with smaller identifiers
150    * have already completed.  Also the lowest number of a task
151    * still waiting to be executed.
152    */
153   GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
154
155   /**
156    * Number of tasks on the ready list.
157    */
158   unsigned int ready_count;
159
160   /**
161    * How many tasks have we run so far?
162    */
163   unsigned long long tasks_run;
164
165   /**
166    * Priority of the task running right now.  Only
167    * valid while a task is running.
168    */
169   enum GNUNET_SCHEDULER_Priority current_priority;
170
171   /**
172    * How 'nice' are we right now?
173    */
174   int nice_level;
175
176 };
177
178
179 /**
180  * Check that the given priority is legal (and return it).
181  *
182  * @param p priority value to check
183  * @return p on success, 0 on error
184  */
185 static enum GNUNET_SCHEDULER_Priority
186 check_priority (enum GNUNET_SCHEDULER_Priority p)
187 {
188   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
189     return p;
190   GNUNET_assert (0);
191   return 0;                     /* make compiler happy */
192 }
193
194
195 /**
196  * Is a task with this identifier still pending?  Also updates
197  * "lowest_pending_id" as a side-effect (for faster checks in the
198  * future), but only if the return value is "GNUNET_NO" (and
199  * the "lowest_pending_id" check failed).
200  *
201  * @param sched the scheduler
202  * @param id which task are we checking for
203  * @return GNUNET_YES if so, GNUNET_NO if not
204  */
205 static int
206 is_pending (struct GNUNET_SCHEDULER_Handle *sched,
207             GNUNET_SCHEDULER_TaskIdentifier id)
208 {
209   struct Task *pos;
210   enum GNUNET_SCHEDULER_Priority p;
211   GNUNET_SCHEDULER_TaskIdentifier min;
212
213   if (id < sched->lowest_pending_id)
214     return GNUNET_NO;
215   min = -1;                     /* maximum value */
216   pos = sched->pending;
217   while (pos != NULL)
218     {
219       if (pos->id == id)
220         return GNUNET_YES;
221       if (pos->id < min)
222         min = pos->id;
223       pos = pos->next;
224     }
225   for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
226     {
227       pos = sched->ready[p];
228       while (pos != NULL)
229         {
230           if (pos->id == id)
231             return GNUNET_YES;
232           if (pos->id < min)
233             min = pos->id;
234           pos = pos->next;
235         }
236     }
237   sched->lowest_pending_id = min;
238   return GNUNET_NO;
239 }
240
241
242 /**
243  * Update all sets and timeout for select.
244  *
245  * @param sched the scheduler
246  * @param rs read-set, set to all FDs we would like to read (updated)
247  * @param ws write-set, set to all FDs we would like to write (updated)
248  * @param timeout next timeout (updated)
249  */
250 static void
251 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
252              struct GNUNET_NETWORK_FDSet *rs,
253              struct GNUNET_NETWORK_FDSet *ws,
254              struct GNUNET_TIME_Relative *timeout)
255 {
256   struct Task *pos;
257
258   pos = sched->pending;
259   while (pos != NULL)
260     {
261       if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
262           (GNUNET_YES == is_pending (sched, pos->prereq_id)))
263         {
264           pos = pos->next;
265           continue;
266         }
267
268       if (pos->timeout.value != GNUNET_TIME_UNIT_FOREVER_ABS.value)
269         {
270           struct GNUNET_TIME_Relative to;
271
272           to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
273           if (timeout->value > to.value)
274             *timeout = to;
275         }
276       if (pos->read_set != NULL)
277         GNUNET_NETWORK_fdset_add (rs, pos->read_set);
278       if (pos->write_set != NULL)
279         GNUNET_NETWORK_fdset_add (ws, pos->write_set);
280       if (pos->reason != 0)
281         *timeout = GNUNET_TIME_UNIT_ZERO;
282       pos = pos->next;
283     }
284 }
285
286
287 /**
288  * Check if the ready set overlaps with the set we want to have ready.
289  * If so, update the want set (set all FDs that are ready).  If not,
290  * return GNUNET_NO.
291  *
292  * @param ready set that is ready
293  * @param want set that we want to be ready
294  * @return GNUNET_YES if there was some overlap
295  */
296 static int
297 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
298               struct GNUNET_NETWORK_FDSet *want)
299 {
300   if (NULL == want)
301     return GNUNET_NO;
302   if (GNUNET_NETWORK_fdset_overlap (ready, want))
303     {
304       /* copy all over (yes, there maybe unrelated bits,
305          but this should not hurt well-written clients) */
306       GNUNET_NETWORK_fdset_copy (want, ready);
307       return GNUNET_YES;
308     }
309   return GNUNET_NO;
310 }
311
312
313 /**
314  * Check if the given task is eligible to run now.
315  * Also set the reason why it is eligible.
316  *
317  * @param sched the scheduler
318  * @param task task to check if it is ready
319  * @param now the current time
320  * @param rs set of FDs ready for reading
321  * @param ws set of FDs ready for writing
322  * @return GNUNET_YES if we can run it, GNUNET_NO if not.
323  */
324 static int
325 is_ready (struct GNUNET_SCHEDULER_Handle *sched,
326           struct Task *task,
327           struct GNUNET_TIME_Absolute now,
328           const struct GNUNET_NETWORK_FDSet *rs,
329           const struct GNUNET_NETWORK_FDSet *ws)
330 {
331   if (now.value >= task->timeout.value)
332     task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
333   if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
334       (rs != NULL) && (set_overlaps (rs, task->read_set)))
335     task->reason |= GNUNET_SCHEDULER_REASON_READ_READY;
336   if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
337       (ws != NULL) && (set_overlaps (ws, task->write_set)))
338     task->reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
339   if (task->reason == 0)
340     return GNUNET_NO;           /* not ready */
341   if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
342     {
343       if (GNUNET_YES == is_pending (sched, task->prereq_id))
344         return GNUNET_NO;       /* prereq waiting */
345       task->reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
346     }
347   return GNUNET_YES;
348 }
349
350
351 /**
352  * Put a task that is ready for execution into the ready queue.
353  *
354  * @param handle the scheduler
355  * @param task task ready for execution
356  */
357 static void
358 queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
359 {
360   enum GNUNET_SCHEDULER_Priority p = task->priority;
361   if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
362     p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
363   task->next = handle->ready[check_priority (p)];
364   handle->ready[check_priority (p)] = task;
365   handle->ready_count++;
366 }
367
368
369 /**
370  * Check which tasks are ready and move them
371  * to the respective ready queue.
372  *
373  * @param handle the scheduler
374  * @param rs FDs ready for reading
375  * @param ws FDs ready for writing
376  */
377 static void
378 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
379              const struct GNUNET_NETWORK_FDSet *rs,
380              const struct GNUNET_NETWORK_FDSet *ws)
381 {
382   struct Task *pos;
383   struct Task *prev;
384   struct Task *next;
385   struct GNUNET_TIME_Absolute now;
386
387   now = GNUNET_TIME_absolute_get ();
388   prev = NULL;
389   pos = handle->pending;
390   while (pos != NULL)
391     {
392 #if DEBUG_TASKS
393       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394                   "Checking readiness of task: %llu / %p\n",
395                   pos->id, pos->callback_cls);
396 #endif
397       next = pos->next;
398       if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
399         {
400           if (prev == NULL)
401             handle->pending = next;
402           else
403             prev->next = next;
404           queue_ready_task (handle, pos);
405           pos = next;
406           continue;
407         }
408       prev = pos;
409       pos = next;
410     }
411 }
412
413
414 /**
415  * Request the shutdown of a scheduler.  Marks all currently
416  * pending tasks as ready because of shutdown.  This will
417  * cause all tasks to run (as soon as possible, respecting
418  * priorities and prerequisite tasks).  Note that tasks
419  * scheduled AFTER this call may still be delayed arbitrarily.
420  *
421  * @param sched the scheduler
422  */
423 void
424 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
425 {
426   struct Task *pos;
427   int i;
428
429   pos = sched->pending;
430   while (pos != NULL)
431     {
432       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
433       /* we don't move the task into the ready queue yet; check_ready
434          will do that later, possibly adding additional
435          readiness-factors */
436       pos = pos->next;
437     }
438   for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
439     {
440       pos = sched->ready[i];
441       while (pos != NULL)
442         {
443           pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
444           /* we don't move the task into the ready queue yet; check_ready
445              will do that later, possibly adding additional
446              readiness-factors */
447           pos = pos->next;
448         }
449     }  
450 }
451
452
453 /**
454  * Destroy a task (release associated resources)
455  *
456  * @param t task to destroy
457  */
458 static void
459 destroy_task (struct Task *t)
460 {
461   if (NULL != t->read_set)
462     GNUNET_NETWORK_fdset_destroy (t->read_set);
463   if (NULL != t->write_set)
464     GNUNET_NETWORK_fdset_destroy (t->write_set);
465 #if EXECINFO
466   GNUNET_free (t->backtrace_strings);
467 #endif
468   GNUNET_free (t);
469 }
470
471
472 /**
473  * Run at least one task in the highest-priority queue that is not
474  * empty.  Keep running tasks until we are either no longer running
475  * "URGENT" tasks or until we have at least one "pending" task (which
476  * may become ready, hence we should select on it).  Naturally, if
477  * there are no more ready tasks, we also return.  
478  *
479  * @param sched the scheduler
480  */
481 static void
482 run_ready (struct GNUNET_SCHEDULER_Handle *sched)
483 {
484   enum GNUNET_SCHEDULER_Priority p;
485   struct Task *pos;
486   struct GNUNET_SCHEDULER_TaskContext tc;
487
488   do
489     {
490       if (sched->ready_count == 0)
491         return;
492       GNUNET_assert (sched->ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
493       /* yes, p>0 is correct, 0 is "KEEP" which should
494          always be an empty queue (see assertion)! */
495       for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
496         {
497           pos = sched->ready[p];
498           if (pos != NULL)
499             break;
500         }
501       GNUNET_assert (pos != NULL);      /* ready_count wrong? */
502       sched->ready[p] = pos->next;
503       sched->ready_count--;
504       if (sched->current_priority != pos->priority)
505         {
506           sched->current_priority = pos->priority;
507           (void) GNUNET_OS_set_process_priority (0, pos->priority);
508         }
509       sched->active_task = pos;
510       tc.sched = sched;
511       tc.reason = pos->reason;
512       tc.read_ready = pos->read_set;
513       tc.write_ready = pos->write_set;
514       pos->callback (pos->callback_cls, &tc);
515 #if DEBUG_TASKS
516       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517                   "Running task: %llu / %p\n", pos->id, pos->callback_cls);
518 #endif
519       sched->active_task = NULL;
520       destroy_task (pos);
521       sched->tasks_run++;
522     }
523   while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
524 }
525
526 /**
527  * Pipe used to communicate shutdown via signal.
528  */
529 static struct GNUNET_DISK_PipeHandle *sigpipe;
530
531
532 /**
533  * Signal handler called for signals that should cause us to shutdown.
534  */
535 static void
536 sighandler_shutdown ()
537 {
538   static char c;
539
540   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
541                           (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
542                           sizeof (c));
543 }
544
545
546 /**
547  * Initialize and run scheduler.  This function will return when all
548  * tasks have completed.  On systems with signals, receiving a SIGTERM
549  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
550  * to be run after the active task is complete.  As a result, SIGTERM
551  * causes all active tasks to be scheduled with reason
552  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
553  * afterwards will execute normally!). Note that any particular signal
554  * will only shut down one scheduler; applications should always only
555  * create a single scheduler.
556  *
557  * @param task task to run immediately
558  * @param task_cls closure of task
559  */
560 void
561 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
562 {
563   struct GNUNET_SCHEDULER_Handle sched;
564   struct GNUNET_NETWORK_FDSet *rs;
565   struct GNUNET_NETWORK_FDSet *ws;
566   struct GNUNET_TIME_Relative timeout;
567   int ret;
568   struct GNUNET_SIGNAL_Context *shc_int;
569   struct GNUNET_SIGNAL_Context *shc_term;
570   struct GNUNET_SIGNAL_Context *shc_quit;
571   struct GNUNET_SIGNAL_Context *shc_hup;
572   unsigned long long last_tr;
573   unsigned int busy_wait_warning;
574   const struct GNUNET_DISK_FileHandle *pr;
575   char c;
576
577   rs = GNUNET_NETWORK_fdset_create ();
578   ws = GNUNET_NETWORK_fdset_create ();
579   GNUNET_assert (sigpipe == NULL);
580   sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
581   GNUNET_assert (sigpipe != NULL);
582   pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
583   GNUNET_assert (pr != NULL);
584   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
585   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
586 #ifndef MINGW
587   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
588   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
589 #endif
590   memset (&sched, 0, sizeof (sched));
591   sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
592   GNUNET_SCHEDULER_add_continuation (&sched,
593                                      task,
594                                      task_cls,
595                                      GNUNET_SCHEDULER_REASON_STARTUP);
596   last_tr = 0;
597   busy_wait_warning = 0;
598   while ((sched.pending != NULL) || (sched.ready_count > 0))
599     {
600       GNUNET_NETWORK_fdset_zero (rs);
601       GNUNET_NETWORK_fdset_zero (ws);
602       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
603       update_sets (&sched, rs, ws, &timeout);
604       GNUNET_NETWORK_fdset_handle_set (rs, pr);
605       if (sched.ready_count > 0)
606         {
607           /* no blocking, more work already ready! */
608           timeout = GNUNET_TIME_UNIT_ZERO;
609         }
610       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
611       if (ret == GNUNET_SYSERR)
612         {
613           if (errno == EINTR)
614             continue;
615           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
616           break;
617         }
618       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
619         {
620           /* consume the signal */
621           GNUNET_DISK_file_read (pr, &c, sizeof (c));
622           /* mark all active tasks as ready due to shutdown */
623           GNUNET_SCHEDULER_shutdown (&sched);
624         }
625       if (last_tr == sched.tasks_run)
626         {
627           busy_wait_warning++;
628         }
629       else
630         {
631           last_tr = sched.tasks_run;
632           busy_wait_warning = 0;
633         }
634       if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
635         {
636           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
637                       _("Looks like we're busy waiting...\n"));
638           sleep (1);            /* mitigate */
639         }
640       check_ready (&sched, rs, ws);
641       run_ready (&sched);
642     }
643   GNUNET_SIGNAL_handler_uninstall (shc_int);
644   GNUNET_SIGNAL_handler_uninstall (shc_term);
645 #ifndef MINGW
646   GNUNET_SIGNAL_handler_uninstall (shc_quit);
647   GNUNET_SIGNAL_handler_uninstall (shc_hup);
648 #endif
649   GNUNET_DISK_pipe_close (sigpipe);
650   sigpipe = NULL;
651   GNUNET_NETWORK_fdset_destroy (rs);
652   GNUNET_NETWORK_fdset_destroy (ws);
653 }
654
655
656 /**
657  * Obtain the reason code for why the current task was
658  * started.  Will return the same value as 
659  * the GNUNET_SCHEDULER_TaskContext's reason field.
660  *
661  * @param sched scheduler to query
662  * @return reason(s) why the current task is run
663  */
664 enum GNUNET_SCHEDULER_Reason
665 GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
666 {
667   return sched->active_task->reason;
668 }
669
670
671 /**
672  * Get information about the current load of this scheduler.  Use this
673  * function to determine if an elective task should be added or simply
674  * dropped (if the decision should be made based on the number of
675  * tasks ready to run).
676  *
677  * @param sched scheduler to query
678  * @param p priority level to look at
679  * @return number of tasks pending right now
680  */
681 unsigned int
682 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
683                            enum GNUNET_SCHEDULER_Priority p)
684 {
685   struct Task *pos;
686   unsigned int ret;
687
688   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
689     return sched->ready_count;
690   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
691     p = sched->current_priority;
692   ret = 0;
693   pos = sched->ready[p];
694   while (pos != NULL)
695     {
696       pos = pos->next;
697       ret++;
698     }
699   return ret;
700 }
701
702
703 /**
704  * Cancel the task with the specified identifier.
705  * The task must not yet have run.
706  *
707  * @param sched scheduler to use
708  * @param task id of the task to cancel
709  * @return original closure of the task
710  */
711 void *
712 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
713                          GNUNET_SCHEDULER_TaskIdentifier task)
714 {
715   struct Task *t;
716   struct Task *prev;
717   enum GNUNET_SCHEDULER_Priority p;
718   void *ret;
719
720   prev = NULL;
721   t = sched->pending;
722   while (t != NULL)
723     {
724       if (t->id == task)
725         break;
726       prev = t;
727       t = t->next;
728     }
729   p = 0;
730   while (t == NULL)
731     {
732       p++;
733       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
734       prev = NULL;
735       t = sched->ready[p];
736       while (t != NULL)
737         {
738           if (t->id == task)
739             {
740               sched->ready_count--;
741               break;
742             }
743           prev = t;
744           t = t->next;
745         }
746     }
747   if (prev == NULL)
748     {
749       if (p == 0)
750         sched->pending = t->next;
751       else
752         sched->ready[p] = t->next;
753     }
754   else
755     prev->next = t->next;
756   ret = t->callback_cls;
757 #if DEBUG_TASKS
758   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759               "Canceling task: %llu / %p\n", task, t->callback_cls);
760 #endif
761   destroy_task (t);
762   return ret;
763 }
764
765
766 /**
767  * Continue the current execution with the given function.  This is
768  * similar to the other "add" functions except that there is no delay
769  * and the reason code can be specified.
770  *
771  * @param sched scheduler to use
772  * @param task main function of the task
773  * @param task_cls closure for 'main'
774  * @param reason reason for task invocation
775  */
776 void
777 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
778                                    GNUNET_SCHEDULER_Task task,
779                                    void *task_cls,
780                                    enum GNUNET_SCHEDULER_Reason reason)
781 {
782   struct Task *t;
783 #if EXECINFO
784   void *backtrace_array[50];
785 #endif
786   t = GNUNET_malloc (sizeof (struct Task));
787 #if EXECINFO
788   t->num_backtrace_strings = backtrace(backtrace_array, 50);
789   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
790 #endif
791   t->callback = task;
792   t->callback_cls = task_cls;
793   t->id = ++sched->last_id;
794   t->reason = reason;
795   t->priority = sched->current_priority;
796 #if DEBUG_TASKS
797   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798               "Adding continuation task: %llu / %p\n",
799               t->id, t->callback_cls);
800 #endif
801   queue_ready_task (sched, t);
802 }
803
804
805
806 /**
807  * Schedule a new task to be run after the specified prerequisite task
808  * has completed. It will be run with the priority of the calling
809  * task.
810  *
811  * @param sched scheduler to use
812  * @param prerequisite_task run this task after the task with the given
813  *        task identifier completes (and any of our other
814  *        conditions, such as delay, read or write-readiness
815  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
816  *        on completion of other tasks (this will cause the task to run as
817  *        soon as possible).
818  * @param task main function of the task
819  * @param task_cls closure of task
820  * @return unique task identifier for the job
821  *         only valid until "task" is started!
822  */
823 GNUNET_SCHEDULER_TaskIdentifier
824 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
825                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
826                             GNUNET_SCHEDULER_Task task, void *task_cls)
827 {
828   return GNUNET_SCHEDULER_add_select (sched,
829                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
830                                       prerequisite_task,
831                                       GNUNET_TIME_UNIT_ZERO,
832                                       NULL, NULL, task, task_cls);
833 }
834
835
836 /**
837  * Schedule a new task to be run with a specified priority.
838  *
839  * @param sched scheduler to use
840  * @param prio how important is the new task?
841  * @param task main function of the task
842  * @param task_cls closure of task
843  * @return unique task identifier for the job
844  *         only valid until "task" is started!
845  */
846 GNUNET_SCHEDULER_TaskIdentifier
847 GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
848                                     enum GNUNET_SCHEDULER_Priority prio,
849                                     GNUNET_SCHEDULER_Task task,
850                                     void *task_cls)
851 {
852   return GNUNET_SCHEDULER_add_select (sched,
853                                       prio,
854                                       GNUNET_SCHEDULER_NO_TASK,
855                                       GNUNET_TIME_UNIT_ZERO,
856                                       NULL, NULL, task, task_cls);
857 }
858
859
860
861 /**
862  * Schedule a new task to be run with a specified delay.  The task
863  * will be scheduled for execution once the delay has expired. It
864  * will be run with the priority of the calling task.
865  *
866  * @param sched scheduler to use
867  * @param delay when should this operation time out? Use 
868  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
869  * @param task main function of the task
870  * @param task_cls closure of task
871  * @return unique task identifier for the job
872  *         only valid until "task" is started!
873  */
874 GNUNET_SCHEDULER_TaskIdentifier
875 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
876                               struct GNUNET_TIME_Relative delay,
877                               GNUNET_SCHEDULER_Task task, void *task_cls)
878 {
879   return GNUNET_SCHEDULER_add_select (sched,
880                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
881                                       GNUNET_SCHEDULER_NO_TASK, delay,
882                                       NULL, NULL, task, task_cls);
883 }
884
885
886
887 /**
888  * Schedule a new task to be run as soon as possible. The task
889  * will be run with the priority of the calling task.
890  *
891  * @param sched scheduler to use
892  * @param task main function of the task
893  * @param task_cls closure of task
894  * @return unique task identifier for the job
895  *         only valid until "task" is started!
896  */
897 GNUNET_SCHEDULER_TaskIdentifier
898 GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
899                           GNUNET_SCHEDULER_Task task,
900                           void *task_cls)
901 {
902   return GNUNET_SCHEDULER_add_select (sched,
903                                       GNUNET_SCHEDULER_PRIORITY_KEEP,
904                                       GNUNET_SCHEDULER_NO_TASK,
905                                       GNUNET_TIME_UNIT_ZERO,
906                                       NULL, NULL, task, task_cls);
907 }
908
909
910
911 /**
912  * Schedule a new task to be run with a specified delay or when the
913  * specified file descriptor is ready for reading.  The delay can be
914  * used as a timeout on the socket being ready.  The task will be
915  * scheduled for execution once either the delay has expired or the
916  * socket operation is ready.  It will be run with the priority of
917  * the calling task.
918  *
919  * @param sched scheduler to use
920  * @param delay when should this operation time out? Use 
921  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
922  * @param rfd read file-descriptor
923  * @param task main function of the task
924  * @param task_cls closure of task
925  * @return unique task identifier for the job
926  *         only valid until "task" is started!
927  */
928 GNUNET_SCHEDULER_TaskIdentifier
929 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
930                                struct GNUNET_TIME_Relative delay,
931                                struct GNUNET_NETWORK_Handle * rfd,
932                                GNUNET_SCHEDULER_Task task, void *task_cls)
933 {
934   struct GNUNET_NETWORK_FDSet *rs;
935   GNUNET_SCHEDULER_TaskIdentifier ret;
936
937   GNUNET_assert (rfd != NULL);
938   rs = GNUNET_NETWORK_fdset_create ();
939   GNUNET_NETWORK_fdset_set (rs, rfd);
940   ret = GNUNET_SCHEDULER_add_select (sched,
941                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
942                                      GNUNET_SCHEDULER_NO_TASK,
943                                      delay, rs, NULL, task, task_cls);
944   GNUNET_NETWORK_fdset_destroy (rs);
945   return ret;
946 }
947
948
949 /**
950  * Schedule a new task to be run with a specified delay or when the
951  * specified file descriptor is ready for writing.  The delay can be
952  * used as a timeout on the socket being ready.  The task will be
953  * scheduled for execution once either the delay has expired or the
954  * socket operation is ready.  It will be run with the priority of
955  * the calling task.
956  *
957  * @param sched scheduler to use
958  * @param delay when should this operation time out? Use 
959  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
960  * @param wfd write file-descriptor
961  * @param task main function of the task
962  * @param task_cls closure of task
963  * @return unique task identifier for the job
964  *         only valid until "task" is started!
965  */
966 GNUNET_SCHEDULER_TaskIdentifier
967 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
968                                 struct GNUNET_TIME_Relative delay,
969                                 struct GNUNET_NETWORK_Handle * wfd,
970                                 GNUNET_SCHEDULER_Task task, void *task_cls)
971 {
972   struct GNUNET_NETWORK_FDSet *ws;
973   GNUNET_SCHEDULER_TaskIdentifier ret;
974
975   GNUNET_assert (wfd != NULL);
976   ws = GNUNET_NETWORK_fdset_create ();
977   GNUNET_NETWORK_fdset_set (ws, wfd);
978   ret = GNUNET_SCHEDULER_add_select (sched,
979                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
980                                      GNUNET_SCHEDULER_NO_TASK, delay,
981                                      NULL, ws, task, task_cls);
982   GNUNET_NETWORK_fdset_destroy (ws);
983   return ret;
984 }
985
986
987 /**
988  * Schedule a new task to be run with a specified delay or when the
989  * specified file descriptor is ready for reading.  The delay can be
990  * used as a timeout on the socket being ready.  The task will be
991  * scheduled for execution once either the delay has expired or the
992  * socket operation is ready. It will be run with the priority of
993  * the calling task.
994  *
995  * @param sched scheduler to use
996  * @param delay when should this operation time out? Use 
997  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
998  * @param rfd read file-descriptor
999  * @param task main function of the task
1000  * @param task_cls closure of task
1001  * @return unique task identifier for the job
1002  *         only valid until "task" is started!
1003  */
1004 GNUNET_SCHEDULER_TaskIdentifier
1005 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
1006                                 struct GNUNET_TIME_Relative delay,
1007                                 const struct GNUNET_DISK_FileHandle * rfd,
1008                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1009 {
1010   struct GNUNET_NETWORK_FDSet *rs;
1011   GNUNET_SCHEDULER_TaskIdentifier ret;
1012
1013   GNUNET_assert (rfd != NULL);
1014   rs = GNUNET_NETWORK_fdset_create ();
1015   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1016   ret = GNUNET_SCHEDULER_add_select (sched,
1017                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1018                                      GNUNET_SCHEDULER_NO_TASK, delay,
1019                                      rs, NULL, task, task_cls);
1020   GNUNET_NETWORK_fdset_destroy (rs);
1021   return ret;
1022 }
1023
1024
1025 /**
1026  * Schedule a new task to be run with a specified delay or when the
1027  * specified file descriptor is ready for writing.  The delay can be
1028  * used as a timeout on the socket being ready.  The task will be
1029  * scheduled for execution once either the delay has expired or the
1030  * socket operation is ready. It will be run with the priority of
1031  * the calling task.
1032  *
1033  * @param sched scheduler to use
1034  * @param delay when should this operation time out? Use 
1035  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1036  * @param wfd write file-descriptor
1037  * @param task main function of the task
1038  * @param task_cls closure of task
1039  * @return unique task identifier for the job
1040  *         only valid until "task" is started!
1041  */
1042 GNUNET_SCHEDULER_TaskIdentifier
1043 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
1044                                  struct GNUNET_TIME_Relative delay,
1045                                  const struct GNUNET_DISK_FileHandle * wfd,
1046                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1047 {
1048   struct GNUNET_NETWORK_FDSet *ws;
1049   GNUNET_SCHEDULER_TaskIdentifier ret;
1050
1051   GNUNET_assert (wfd != NULL);
1052   ws = GNUNET_NETWORK_fdset_create ();
1053   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1054   ret = GNUNET_SCHEDULER_add_select (sched,
1055                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
1056                                      GNUNET_SCHEDULER_NO_TASK,
1057                                      delay, NULL, ws, task, task_cls);
1058   GNUNET_NETWORK_fdset_destroy (ws);
1059   return ret;
1060 }
1061
1062
1063
1064 /**
1065  * Schedule a new task to be run with a specified delay or when any of
1066  * the specified file descriptor sets is ready.  The delay can be used
1067  * as a timeout on the socket(s) being ready.  The task will be
1068  * scheduled for execution once either the delay has expired or any of
1069  * the socket operations is ready.  This is the most general
1070  * function of the "add" family.  Note that the "prerequisite_task"
1071  * must be satisfied in addition to any of the other conditions.  In
1072  * other words, the task will be started when
1073  * <code>
1074  * (prerequisite-run)
1075  * && (delay-ready
1076  *     || any-rs-ready
1077  *     || any-ws-ready
1078  *     || (shutdown-active && run-on-shutdown) )
1079  * </code>
1080  *
1081  * @param sched scheduler to use
1082  * @param prio how important is this task?
1083  * @param prerequisite_task run this task after the task with the given
1084  *        task identifier completes (and any of our other
1085  *        conditions, such as delay, read or write-readiness
1086  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1087  *        on completion of other tasks.
1088  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1089  *        which means that the task will only be run after we receive SIGTERM
1090  * @param rs set of file descriptors we want to read (can be NULL)
1091  * @param ws set of file descriptors we want to write (can be NULL)
1092  * @param task main function of the task
1093  * @param task_cls closure of task
1094  * @return unique task identifier for the job
1095  *         only valid until "task" is started!
1096  */
1097 GNUNET_SCHEDULER_TaskIdentifier
1098 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
1099                              enum GNUNET_SCHEDULER_Priority prio,
1100                              GNUNET_SCHEDULER_TaskIdentifier
1101                              prerequisite_task,
1102                              struct GNUNET_TIME_Relative delay,
1103                              const struct GNUNET_NETWORK_FDSet * rs,
1104                              const struct GNUNET_NETWORK_FDSet * ws,
1105                              GNUNET_SCHEDULER_Task task, void *task_cls)
1106 {
1107   struct Task *t;
1108 #if EXECINFO
1109   void *backtrace_array[MAX_TRACE_DEPTH];
1110 #endif
1111
1112   GNUNET_assert (NULL != task);
1113   t = GNUNET_malloc (sizeof (struct Task));
1114   t->callback = task;
1115   t->callback_cls = task_cls;
1116 #if EXECINFO
1117   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1118   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1119 #endif
1120   if (rs != NULL)
1121     {
1122       t->read_set = GNUNET_NETWORK_fdset_create ();
1123       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1124     }
1125   if (ws != NULL)
1126     {
1127       t->write_set = GNUNET_NETWORK_fdset_create ();
1128       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1129     }
1130   t->id = ++sched->last_id;
1131   t->prereq_id = prerequisite_task;
1132   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1133   t->priority =
1134     check_priority ((prio ==
1135                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
1136                     : prio);
1137   t->next = sched->pending;
1138   sched->pending = t;
1139 #if DEBUG_TASKS
1140   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1142 #endif
1143   return t->id;
1144 }
1145
1146 /* end of scheduler.c */