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