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