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