GNUNET_DISK_OPEN_READ
[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 #ifndef MINGW
474   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
475   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
476   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
477   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
478 #endif
479   memset (&sched, 0, sizeof (sched));
480   sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
481   GNUNET_SCHEDULER_add_continuation (&sched,
482                                      GNUNET_YES,
483                                      task,
484                                      cls, GNUNET_SCHEDULER_REASON_STARTUP);
485   while ((GNUNET_NO == sched.shutdown) &&
486          (!sig_shutdown) &&
487          ((sched.pending != NULL) || (sched.ready_count > 0)))
488     {
489       FD_ZERO (&rs);
490       FD_ZERO (&ws);
491       max = 0;
492       tv.tv_sec = 0x7FFFFFFF;
493       tv.tv_usec = 0;
494       if (sched.ready_count > 0)
495         {
496           /* no blocking, more work already ready! */
497           tv.tv_sec = 0;
498           tv.tv_usec = 0;
499         }
500       update_sets (&sched, &max, &rs, &ws, &tv);
501       ret = SELECT (max, &rs, &ws, NULL, &tv);
502       if (ret == -1)
503         {
504           if (errno == EINTR)
505             continue;
506           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
507           break;
508         }
509       check_ready (&sched, &rs, &ws);
510       run_ready (&sched);
511     }
512   if (sig_shutdown)
513     sched.shutdown = GNUNET_YES;
514   GNUNET_SIGNAL_handler_uninstall (shc_int);
515   GNUNET_SIGNAL_handler_uninstall (shc_term);
516   GNUNET_SIGNAL_handler_uninstall (shc_quit);
517   GNUNET_SIGNAL_handler_uninstall (shc_hup);
518   do
519     {
520       run_ready (&sched);
521       check_ready (&sched, NULL, NULL);
522     }
523   while (sched.ready_count > 0);
524   while (NULL != (tpos = sched.pending))
525     {
526       sched.pending = tpos->next;
527       GNUNET_free (tpos);
528     }
529 }
530
531
532 /**
533  * Request the shutdown of a scheduler.  This function can be used to
534  * stop a scheduling thread when created with the
535  * "GNUNET_SCHEDULER_init_thread" function or from within the signal
536  * handler for signals causing shutdowns.
537  */
538 void
539 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
540 {
541   sched->shutdown = GNUNET_YES;
542 }
543
544
545 /**
546  * Get information about the current load of this scheduler.  Use this
547  * function to determine if an elective task should be added or simply
548  * dropped (if the decision should be made based on the number of
549  * tasks ready to run).
550  *
551  * @param sched scheduler to query
552  * @return number of tasks pending right now
553  */
554 unsigned int
555 GNUNET_SCHEDULER_get_load (struct GNUNET_SCHEDULER_Handle *sched,
556                            enum GNUNET_SCHEDULER_Priority p)
557 {
558   struct Task *pos;
559   unsigned int ret;
560
561   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
562     return sched->ready_count;
563   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
564     p = sched->current_priority;
565   ret = 0;
566   pos = sched->ready[p];
567   while (pos != NULL)
568     {
569       pos = pos->next;
570       ret++;
571     }
572   return ret;
573 }
574
575
576 /**
577  * Cancel the task with the specified identifier.
578  * The task must not yet have run.
579  *
580  * @param sched scheduler to use
581  * @param task id of the task to cancel
582  */
583 void *
584 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
585                          GNUNET_SCHEDULER_TaskIdentifier task)
586 {
587   struct Task *t;
588   struct Task *prev;
589   enum GNUNET_SCHEDULER_Priority p;
590   void *ret;
591
592   prev = NULL;
593   t = sched->pending;
594   while (t != NULL)
595     {
596       if (t->id == task)
597         break;
598       prev = t;
599       t = t->next;
600     }
601   p = 0;
602   while (t == NULL)
603     {
604       p++;
605       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
606       prev = NULL;
607       t = sched->ready[p];
608       while (t != NULL)
609         {
610           if (t->id == task)
611             {
612               sched->ready_count--;
613               break;
614             }
615           prev = t;
616           t = t->next;
617         }
618     }
619   if (prev == NULL)
620     {
621       if (p == 0)
622         sched->pending = t->next;
623       else
624         sched->ready[p] = t->next;
625     }
626   else
627     prev->next = t->next;
628   ret = t->callback_cls;
629   GNUNET_free (t);
630   return ret;
631 }
632
633
634 /**
635  * Continue the current execution with the given function.  This is
636  * similar to the other "add" functions except that there is no delay
637  * and the reason code can be specified.
638  *
639  * @param sched scheduler to use
640  * @param main main function of the task
641  * @param cls closure of task
642  * @param reason reason for task invocation
643  */
644 void
645 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
646                                    int run_on_shutdown,
647                                    GNUNET_SCHEDULER_Task main,
648                                    void *cls,
649                                    enum GNUNET_SCHEDULER_Reason reason)
650 {
651   struct Task *task;
652
653   task = GNUNET_malloc (sizeof (struct Task));
654   task->callback = main;
655   task->callback_cls = cls;
656   task->id = ++sched->last_id;
657   task->reason = reason;
658   task->priority = sched->current_priority;
659   task->run_on_shutdown = run_on_shutdown;
660   queue_ready_task (sched, task);
661 }
662
663
664 /**
665  * Schedule a new task to be run after the specified
666  * prerequisite task has completed.
667  *
668  * @param sched scheduler to use
669  * @param run_on_shutdown run on shutdown?
670  * @param prio how important is this task?
671  * @param prerequisite_task run this task after the task with the given
672  *        task identifier completes (and any of our other
673  *        conditions, such as delay, read or write-readyness
674  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
675  *        on completion of other tasks.
676  * @param main main function of the task
677  * @param cls closure of task
678  * @return unique task identifier for the job
679  *         only valid until "main" is started!
680  */
681 GNUNET_SCHEDULER_TaskIdentifier
682 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
683                             int run_on_shutdown,
684                             enum GNUNET_SCHEDULER_Priority prio,
685                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
686                             GNUNET_SCHEDULER_Task main, void *cls)
687 {
688   return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
689                                       prerequisite_task,
690                                       GNUNET_TIME_UNIT_ZERO,
691                                       0, NULL, NULL, main, cls);
692 }
693
694
695 /**
696  * Schedule a new task to be run with a specified delay.  The task
697  * will be scheduled for execution once the delay has expired and the
698  * prerequisite task has completed.
699  *
700  * @param sched scheduler to use
701  * @param run_on_shutdown run on shutdown? You can use this
702  *        argument to run a function only during shutdown
703  *        by setting delay to -1.  Set this
704  *        argument to GNUNET_NO to skip this task if
705  *        the user requested process termination.
706  * @param prio how important is this task?
707  * @param prerequisite_task run this task after the task with the given
708  *        task identifier completes (and any of our other
709  *        conditions, such as delay, read or write-readyness
710  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
711  *        on completion of other tasks.
712  * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
713  * @param main main function of the task
714  * @param cls closure of task
715  * @return unique task identifier for the job
716  *         only valid until "main" is started!
717  */
718 GNUNET_SCHEDULER_TaskIdentifier
719 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
720                               int run_on_shutdown,
721                               enum GNUNET_SCHEDULER_Priority prio,
722                               GNUNET_SCHEDULER_TaskIdentifier
723                               prerequisite_task,
724                               struct GNUNET_TIME_Relative delay,
725                               GNUNET_SCHEDULER_Task main, void *cls)
726 {
727   return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
728                                       prerequisite_task, delay,
729                                       0, NULL, NULL, main, cls);
730 }
731
732
733 /**
734  * Schedule a new task to be run with a specified delay or when the
735  * specified file descriptor is ready for reading.  The delay can be
736  * used as a timeout on the socket being ready.  The task will be
737  * scheduled for execution once either the delay has expired or the
738  * socket operation is ready.
739  *
740  * @param sched scheduler to use
741  * @param run_on_shutdown run on shutdown? Set this
742  *        argument to GNUNET_NO to skip this task if
743  *        the user requested process termination.
744  * @param prio how important is this task?
745  * @param prerequisite_task run this task after the task with the given
746  *        task identifier completes (and any of our other
747  *        conditions, such as delay, read or write-readyness
748  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
749  *        on completion of other tasks.
750  * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
751  * @param rfd read file-descriptor
752  * @param main main function of the task
753  * @param cls closure of task
754  * @return unique task identifier for the job
755  *         only valid until "main" is started!
756  */
757 GNUNET_SCHEDULER_TaskIdentifier
758 GNUNET_SCHEDULER_add_read (struct GNUNET_SCHEDULER_Handle * sched,
759                            int run_on_shutdown,
760                            enum GNUNET_SCHEDULER_Priority prio,
761                            GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
762                            struct GNUNET_TIME_Relative delay,
763                            int rfd, GNUNET_SCHEDULER_Task main, void *cls)
764 {
765   fd_set rs;
766
767   GNUNET_assert (rfd >= 0);
768   FD_ZERO (&rs);
769   FD_SET (rfd, &rs);
770   return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
771                                       prerequisite_task, delay,
772                                       rfd + 1, &rs, NULL, main, cls);
773 }
774
775
776 /**
777  * Schedule a new task to be run with a specified delay or when the
778  * specified file descriptor is ready for writing.  The delay can be
779  * used as a timeout on the socket being ready.  The task will be
780  * scheduled for execution once either the delay has expired or the
781  * socket operation is ready.
782  *
783  * @param sched scheduler to use
784  * @param run_on_shutdown run on shutdown? Set this
785  *        argument to GNUNET_NO to skip this task if
786  *        the user requested process termination.
787  * @param prio how important is this task?
788  * @param prerequisite_task run this task after the task with the given
789  *        task identifier completes (and any of our other
790  *        conditions, such as delay, read or write-readyness
791  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
792  *        on completion of other tasks.
793  * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
794  * @param wfd write file-descriptor
795  * @param main main function of the task
796  * @param cls closure of task
797  * @return unique task identifier for the job
798  *         only valid until "main" is started!
799  */
800 GNUNET_SCHEDULER_TaskIdentifier
801 GNUNET_SCHEDULER_add_write (struct GNUNET_SCHEDULER_Handle * sched,
802                             int run_on_shutdown,
803                             enum GNUNET_SCHEDULER_Priority prio,
804                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
805                             struct GNUNET_TIME_Relative delay,
806                             int wfd, GNUNET_SCHEDULER_Task main, void *cls)
807 {
808   fd_set ws;
809
810   GNUNET_assert (wfd >= 0);
811   FD_ZERO (&ws);
812   FD_SET (wfd, &ws);
813   return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
814                                       prerequisite_task, delay,
815                                       wfd + 1, NULL, &ws, main, cls);
816 }
817
818
819 /**
820  * Schedule a new task to be run with a specified delay or when any of
821  * the specified file descriptor sets is ready.  The delay can be used
822  * as a timeout on the socket(s) being ready.  The task will be
823  * scheduled for execution once either the delay has expired or any of
824  * the socket operations is ready.  This is the most general
825  * function of the "add" family.  Note that the "prerequisite_task"
826  * must be satisfied in addition to any of the other conditions.  In
827  * other words, the task will be started when
828  * <code>
829  * (prerequisite-run)
830  * && (delay-ready
831  *     || any-rs-ready
832  *     || any-ws-ready
833  *     || (shutdown-active && run-on-shutdown) )
834  * </code>
835  *
836  * @param sched scheduler to use
837  * @param run_on_shutdown run on shutdown?  Set this
838  *        argument to GNUNET_NO to skip this task if
839  *        the user requested process termination.
840  * @param prio how important is this task?
841  * @param prerequisite_task run this task after the task with the given
842  *        task identifier completes (and any of our other
843  *        conditions, such as delay, read or write-readyness
844  *        are satisfied).  Use GNUNET_SCHEDULER_NO_PREREQUISITE_TASK to not have any dependency
845  *        on completion of other tasks.
846  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
847  * @param nfds highest-numbered file descriptor in any of the two sets plus one
848  * @param rs set of file descriptors we want to read (can be NULL)
849  * @param ws set of file descriptors we want to write (can be NULL)
850  * @param main main function of the task
851  * @param cls closure of task
852  * @return unique task identifier for the job
853  *         only valid until "main" is started!
854  */
855 GNUNET_SCHEDULER_TaskIdentifier
856 GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
857                              int run_on_shutdown,
858                              enum GNUNET_SCHEDULER_Priority prio,
859                              GNUNET_SCHEDULER_TaskIdentifier
860                              prerequisite_task,
861                              struct GNUNET_TIME_Relative delay,
862                              int nfds, const fd_set * rs, const fd_set * ws,
863                              GNUNET_SCHEDULER_Task main, void *cls)
864 {
865   struct Task *task;
866
867   task = GNUNET_malloc (sizeof (struct Task));
868   task->callback = main;
869   task->callback_cls = cls;
870   if ((rs != NULL) && (nfds > 0))
871     memcpy (&task->read_set, rs, sizeof (fd_set));
872   if ((ws != NULL) && (nfds > 0))
873     memcpy (&task->write_set, ws, sizeof (fd_set));
874   task->id = ++sched->last_id;
875   task->prereq_id = prerequisite_task;
876   task->timeout = GNUNET_TIME_relative_to_absolute (delay);
877   task->priority =
878     check_priority ((prio ==
879                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
880                     : prio);
881   task->nfds = nfds;
882   task->run_on_shutdown = run_on_shutdown;
883   task->next = sched->pending;
884   sched->pending = task;
885   return task->id;
886 }
887
888 /* end of scheduler.c */