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