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