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