fe9d89d699f0c0c9ddcfade962b7cfeecb1f6c44
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2009-2013 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 3, 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., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, 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_util_lib.h"
28 #include "disk.h"
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
31
32 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
33
34
35 #if HAVE_EXECINFO_H
36 #include "execinfo.h"
37
38 /**
39  * Use lsof to generate file descriptor reports on select error?
40  * (turn off for stable releases).
41  */
42 #define USE_LSOF GNUNET_NO
43
44 /**
45  * Obtain trace information for all scheduler calls that schedule tasks.
46  */
47 #define EXECINFO GNUNET_NO
48
49 /**
50  * Check each file descriptor before adding
51  */
52 #define DEBUG_FDS GNUNET_NO
53
54 /**
55  * Depth of the traces collected via EXECINFO.
56  */
57 #define MAX_TRACE_DEPTH 50
58 #endif
59
60 /**
61  * Should we figure out which tasks are delayed for a while
62  * before they are run? (Consider using in combination with EXECINFO).
63  */
64 #define PROFILE_DELAYS GNUNET_NO
65
66 /**
67  * Task that were in the queue for longer than this are reported if
68  * PROFILE_DELAYS is active.
69  */
70 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
71
72
73 /**
74  * Entry in list of pending tasks.
75  */
76 struct GNUNET_SCHEDULER_Task
77 {
78   /**
79    * This is a linked list.
80    */
81   struct GNUNET_SCHEDULER_Task *next;
82
83   /**
84    * This is a linked list.
85    */
86   struct GNUNET_SCHEDULER_Task *prev;
87
88   /**
89    * Function to run when ready.
90    */
91   GNUNET_SCHEDULER_TaskCallback callback;
92
93   /**
94    * Closure for the @e callback.
95    */
96   void *callback_cls;
97
98   /**
99    * Set of file descriptors this task is waiting
100    * for for reading.  Once ready, this is updated
101    * to reflect the set of file descriptors ready
102    * for operation.
103    */
104   struct GNUNET_NETWORK_FDSet *read_set;
105
106   /**
107    * Set of file descriptors this task is waiting for for writing.
108    * Once ready, this is updated to reflect the set of file
109    * descriptors ready for operation.
110    */
111   struct GNUNET_NETWORK_FDSet *write_set;
112
113   /**
114    * Absolute timeout value for the task, or
115    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
116    */
117   struct GNUNET_TIME_Absolute timeout;
118
119 #if PROFILE_DELAYS
120   /**
121    * When was the task scheduled?
122    */
123   struct GNUNET_TIME_Absolute start_time;
124 #endif
125
126   /**
127    * Why is the task ready?  Set after task is added to ready queue.
128    * Initially set to zero.  All reasons that have already been
129    * satisfied (i.e.  read or write ready) will be set over time.
130    */
131   enum GNUNET_SCHEDULER_Reason reason;
132
133   /**
134    * Task priority.
135    */
136   enum GNUNET_SCHEDULER_Priority priority;
137
138   /**
139    * Set if we only wait for reading from a single FD, otherwise -1.
140    */
141   int read_fd;
142
143   /**
144    * Set if we only wait for writing to a single FD, otherwise -1.
145    */
146   int write_fd;
147
148   /**
149    * Should the existence of this task in the queue be counted as
150    * reason to not shutdown the scheduler?
151    */
152   int lifeness;
153
154   /**
155    * Is this task in the ready list?
156    */
157   int in_ready_list;
158
159 #if EXECINFO
160   /**
161    * Array of strings which make up a backtrace from the point when this
162    * task was scheduled (essentially, who scheduled the task?)
163    */
164   char **backtrace_strings;
165
166   /**
167    * Size of the backtrace_strings array
168    */
169   int num_backtrace_strings;
170 #endif
171
172
173 };
174
175
176 /**
177  * Head of list of tasks waiting for an event.
178  */
179 static struct GNUNET_SCHEDULER_Task *pending_head;
180
181 /**
182  * Tail of list of tasks waiting for an event.
183  */
184 static struct GNUNET_SCHEDULER_Task *pending_tail;
185
186 /**
187  * List of tasks waiting ONLY for a timeout event.
188  * Sorted by timeout (earliest first).  Used so that
189  * we do not traverse the list of these tasks when
190  * building select sets (we just look at the head
191  * to determine the respective timeout ONCE).
192  */
193 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
194
195 /**
196  * List of tasks waiting ONLY for a timeout event.
197  * Sorted by timeout (earliest first).  Used so that
198  * we do not traverse the list of these tasks when
199  * building select sets (we just look at the head
200  * to determine the respective timeout ONCE).
201  */
202 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
203
204 /**
205  * Last inserted task waiting ONLY for a timeout event.
206  * Used to (heuristically) speed up insertion.
207  */
208 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
209
210 /**
211  * ID of the task that is running right now.
212  */
213 static struct GNUNET_SCHEDULER_Task *active_task;
214
215 /**
216  * Head of list of tasks ready to run right now, grouped by importance.
217  */
218 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
219
220 /**
221  * Tail of list of tasks ready to run right now, grouped by importance.
222  */
223 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
224
225 /**
226  * Number of tasks on the ready list.
227  */
228 static unsigned int ready_count;
229
230 /**
231  * How many tasks have we run so far?
232  */
233 static unsigned long long tasks_run;
234
235 /**
236  * Priority of the task running right now.  Only
237  * valid while a task is running.
238  */
239 static enum GNUNET_SCHEDULER_Priority current_priority;
240
241 /**
242  * Priority of the highest task added in the current select
243  * iteration.
244  */
245 static enum GNUNET_SCHEDULER_Priority max_priority_added;
246
247 /**
248  * Value of the 'lifeness' flag for the current task.
249  */
250 static int current_lifeness;
251
252 /**
253  * Function to use as a select() in the scheduler.
254  * If NULL, we use GNUNET_NETWORK_socket_select().
255  */
256 static GNUNET_SCHEDULER_select scheduler_select;
257
258 /**
259  * Closure for #scheduler_select.
260  */
261 static void *scheduler_select_cls;
262
263
264 /**
265  * Sets the select function to use in the scheduler (scheduler_select).
266  *
267  * @param new_select new select function to use
268  * @param new_select_cls closure for @a new_select
269  * @return previously used select function, NULL for default
270  */
271 void
272 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
273                              void *new_select_cls)
274 {
275   scheduler_select = new_select;
276   scheduler_select_cls = new_select_cls;
277 }
278
279
280 /**
281  * Check that the given priority is legal (and return it).
282  *
283  * @param p priority value to check
284  * @return p on success, 0 on error
285  */
286 static enum GNUNET_SCHEDULER_Priority
287 check_priority (enum GNUNET_SCHEDULER_Priority p)
288 {
289   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
290     return p;
291   GNUNET_assert (0);
292   return 0;                     /* make compiler happy */
293 }
294
295
296 /**
297  * Update all sets and timeout for select.
298  *
299  * @param rs read-set, set to all FDs we would like to read (updated)
300  * @param ws write-set, set to all FDs we would like to write (updated)
301  * @param timeout next timeout (updated)
302  */
303 static void
304 update_sets (struct GNUNET_NETWORK_FDSet *rs,
305              struct GNUNET_NETWORK_FDSet *ws,
306              struct GNUNET_TIME_Relative *timeout)
307 {
308   struct GNUNET_SCHEDULER_Task *pos;
309   struct GNUNET_TIME_Absolute now;
310   struct GNUNET_TIME_Relative to;
311
312   now = GNUNET_TIME_absolute_get ();
313   pos = pending_timeout_head;
314   if (NULL != pos)
315   {
316     to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
317     if (timeout->rel_value_us > to.rel_value_us)
318       *timeout = to;
319     if (0 != pos->reason)
320       *timeout = GNUNET_TIME_UNIT_ZERO;
321   }
322   for (pos = pending_head; NULL != pos; pos = pos->next)
323   {
324     if (pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
325     {
326       to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
327       if (timeout->rel_value_us > to.rel_value_us)
328         *timeout = to;
329     }
330     if (-1 != pos->read_fd)
331       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
332     if (-1 != pos->write_fd)
333       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
334     if (NULL != pos->read_set)
335       GNUNET_NETWORK_fdset_add (rs, pos->read_set);
336     if (NULL != pos->write_set)
337       GNUNET_NETWORK_fdset_add (ws, pos->write_set);
338     if (0 != pos->reason)
339       *timeout = GNUNET_TIME_UNIT_ZERO;
340   }
341 }
342
343
344 /**
345  * Check if the ready set overlaps with the set we want to have ready.
346  * If so, update the want set (set all FDs that are ready).  If not,
347  * return #GNUNET_NO.
348  *
349  * @param ready set that is ready
350  * @param want set that we want to be ready
351  * @return #GNUNET_YES if there was some overlap
352  */
353 static int
354 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
355               struct GNUNET_NETWORK_FDSet *want)
356 {
357   if ((NULL == want) || (NULL == ready))
358     return GNUNET_NO;
359   if (GNUNET_NETWORK_fdset_overlap (ready, want))
360   {
361     /* copy all over (yes, there maybe unrelated bits,
362      * but this should not hurt well-written clients) */
363     GNUNET_NETWORK_fdset_copy (want, ready);
364     return GNUNET_YES;
365   }
366   return GNUNET_NO;
367 }
368
369
370 /**
371  * Check if the given task is eligible to run now.
372  * Also set the reason why it is eligible.
373  *
374  * @param task task to check if it is ready
375  * @param now the current time
376  * @param rs set of FDs ready for reading
377  * @param ws set of FDs ready for writing
378  * @return #GNUNET_YES if we can run it, #GNUNET_NO if not.
379  */
380 static int
381 is_ready (struct GNUNET_SCHEDULER_Task *task,
382           struct GNUNET_TIME_Absolute now,
383           const struct GNUNET_NETWORK_FDSet *rs,
384           const struct GNUNET_NETWORK_FDSet *ws)
385 {
386   enum GNUNET_SCHEDULER_Reason reason;
387
388   reason = task->reason;
389   if (now.abs_value_us >= task->timeout.abs_value_us)
390     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
391   if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
392       (((task->read_fd != -1) &&
393         (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd))) ||
394        (set_overlaps (rs, task->read_set))))
395     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
396   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
397       (((task->write_fd != -1) &&
398         (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)))
399        || (set_overlaps (ws, task->write_set))))
400     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
401   if (0 == reason)
402     return GNUNET_NO;           /* not ready */
403   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
404   task->reason = reason;
405   return GNUNET_YES;
406 }
407
408
409 /**
410  * Put a task that is ready for execution into the ready queue.
411  *
412  * @param task task ready for execution
413  */
414 static void
415 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
416 {
417   enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
418
419   if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
420     p = task->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
421   GNUNET_CONTAINER_DLL_insert (ready_head[p],
422                                ready_tail[p],
423                                task);
424   task->in_ready_list = GNUNET_YES;
425   ready_count++;
426 }
427
428
429 /**
430  * Check which tasks are ready and move them
431  * to the respective ready queue.
432  *
433  * @param rs FDs ready for reading
434  * @param ws FDs ready for writing
435  */
436 static void
437 check_ready (const struct GNUNET_NETWORK_FDSet *rs,
438              const struct GNUNET_NETWORK_FDSet *ws)
439 {
440   struct GNUNET_SCHEDULER_Task *pos;
441   struct GNUNET_SCHEDULER_Task *next;
442   struct GNUNET_TIME_Absolute now;
443
444   now = GNUNET_TIME_absolute_get ();
445   while (NULL != (pos = pending_timeout_head))
446   {
447     if (now.abs_value_us >= pos->timeout.abs_value_us)
448       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
449     if (0 == pos->reason)
450       break;
451     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
452                                  pending_timeout_tail,
453                                  pos);
454     if (pending_timeout_last == pos)
455       pending_timeout_last = NULL;
456     queue_ready_task (pos);
457   }
458   pos = pending_head;
459   while (NULL != pos)
460   {
461     next = pos->next;
462     if (GNUNET_YES == is_ready (pos, now, rs, ws))
463     {
464       GNUNET_CONTAINER_DLL_remove (pending_head,
465                                    pending_tail,
466                                    pos);
467       queue_ready_task (pos);
468     }
469     pos = next;
470   }
471 }
472
473
474 /**
475  * Request the shutdown of a scheduler.  Marks all currently
476  * pending tasks as ready because of shutdown.  This will
477  * cause all tasks to run (as soon as possible, respecting
478  * priorities and prerequisite tasks).  Note that tasks
479  * scheduled AFTER this call may still be delayed arbitrarily.
480  *
481  * Note that we don't move the tasks into the ready queue yet;
482  * check_ready() will do that later, possibly adding additional
483  * readiness-factors
484  */
485 void
486 GNUNET_SCHEDULER_shutdown ()
487 {
488   struct GNUNET_SCHEDULER_Task *pos;
489   int i;
490
491   for (pos = pending_timeout_head; NULL != pos; pos = pos->next)
492     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
493   for (pos = pending_head; NULL != pos; pos = pos->next)
494     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
495   for (i = 0; i < GNUNET_SCHEDULER_PRIORITY_COUNT; i++)
496     for (pos = ready_head[i]; NULL != pos; pos = pos->next)
497       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
498 }
499
500
501 /**
502  * Destroy a task (release associated resources)
503  *
504  * @param t task to destroy
505  */
506 static void
507 destroy_task (struct GNUNET_SCHEDULER_Task *t)
508 {
509   if (NULL != t->read_set)
510     GNUNET_NETWORK_fdset_destroy (t->read_set);
511   if (NULL != t->write_set)
512     GNUNET_NETWORK_fdset_destroy (t->write_set);
513 #if EXECINFO
514   GNUNET_free (t->backtrace_strings);
515 #endif
516   GNUNET_free (t);
517 }
518
519
520 /**
521  * Run at least one task in the highest-priority queue that is not
522  * empty.  Keep running tasks until we are either no longer running
523  * "URGENT" tasks or until we have at least one "pending" task (which
524  * may become ready, hence we should select on it).  Naturally, if
525  * there are no more ready tasks, we also return.
526  *
527  * @param rs FDs ready for reading
528  * @param ws FDs ready for writing
529  */
530 static void
531 run_ready (struct GNUNET_NETWORK_FDSet *rs,
532            struct GNUNET_NETWORK_FDSet *ws)
533 {
534   enum GNUNET_SCHEDULER_Priority p;
535   struct GNUNET_SCHEDULER_Task *pos;
536   struct GNUNET_SCHEDULER_TaskContext tc;
537
538   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
539   do
540   {
541     if (0 == ready_count)
542       return;
543     GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
544     /* yes, p>0 is correct, 0 is "KEEP" which should
545      * always be an empty queue (see assertion)! */
546     for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
547     {
548       pos = ready_head[p];
549       if (NULL != pos)
550         break;
551     }
552     GNUNET_assert (NULL != pos);        /* ready_count wrong? */
553     GNUNET_CONTAINER_DLL_remove (ready_head[p],
554                                  ready_tail[p],
555                                  pos);
556     ready_count--;
557     current_priority = pos->priority;
558     current_lifeness = pos->lifeness;
559     active_task = pos;
560 #if PROFILE_DELAYS
561     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
562         DELAY_THRESHOLD.rel_value_us)
563     {
564       LOG (GNUNET_ERROR_TYPE_DEBUG,
565            "Task %p took %s to be scheduled\n",
566            pos,
567            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
568                                                    GNUNET_YES));
569     }
570 #endif
571     tc.reason = pos->reason;
572     tc.read_ready = (NULL == pos->read_set) ? rs : pos->read_set;
573     if ((-1 != pos->read_fd) &&
574         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)))
575       GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
576     tc.write_ready = (NULL == pos->write_set) ? ws : pos->write_set;
577     if ((-1 != pos->write_fd) &&
578         (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
579       GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
580     if ((0 != (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
581         (-1 != pos->write_fd) &&
582         (!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
583       GNUNET_assert (0);          // added to ready in previous select loop!
584     LOG (GNUNET_ERROR_TYPE_DEBUG,
585          "Running task: %p\n",
586          pos);
587     pos->callback (pos->callback_cls, &tc);
588 #if EXECINFO
589     unsigned int i;
590
591     for (i = 0; i < pos->num_backtrace_strings; i++)
592       LOG (GNUNET_ERROR_TYPE_DEBUG,
593            "Task %p trace %u: %s\n",
594            pos,
595            i,
596            pos->backtrace_strings[i]);
597 #endif
598     active_task = NULL;
599     destroy_task (pos);
600     tasks_run++;
601   }
602   while ((NULL == pending_head) || (p >= max_priority_added));
603 }
604
605
606 /**
607  * Pipe used to communicate shutdown via signal.
608  */
609 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
610
611 /**
612  * Process ID of this process at the time we installed the various
613  * signal handlers.
614  */
615 static pid_t my_pid;
616
617 /**
618  * Signal handler called for SIGPIPE.
619  */
620 #ifndef MINGW
621 static void
622 sighandler_pipe ()
623 {
624   return;
625 }
626 #endif
627
628
629 /**
630  * Wait for a short time.
631  * Sleeps for @a ms ms (as that should be long enough for virtually all
632  * modern systems to context switch and allow another process to do
633  * some 'real' work).
634  *
635  * @param ms how many ms to wait
636  */
637 static void
638 short_wait (unsigned int ms)
639 {
640   struct GNUNET_TIME_Relative timeout;
641
642   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
643   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
644 }
645
646
647 /**
648  * Signal handler called for signals that should cause us to shutdown.
649  */
650 static void
651 sighandler_shutdown ()
652 {
653   static char c;
654   int old_errno = errno;        /* backup errno */
655
656   if (getpid () != my_pid)
657     exit (1);                   /* we have fork'ed since the signal handler was created,
658                                  * ignore the signal, see https://gnunet.org/vfork discussion */
659   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
660                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
661                           &c, sizeof (c));
662   errno = old_errno;
663 }
664
665
666 /**
667  * Check if the system is still life. Trigger shutdown if we
668  * have tasks, but none of them give us lifeness.
669  *
670  * @return #GNUNET_OK to continue the main loop,
671  *         #GNUNET_NO to exit
672  */
673 static int
674 check_lifeness ()
675 {
676   struct GNUNET_SCHEDULER_Task *t;
677
678   if (ready_count > 0)
679     return GNUNET_OK;
680   for (t = pending_head; NULL != t; t = t->next)
681     if (t->lifeness == GNUNET_YES)
682       return GNUNET_OK;
683   for (t = pending_timeout_head; NULL != t; t = t->next)
684     if (t->lifeness == GNUNET_YES)
685       return GNUNET_OK;
686   if ((NULL != pending_head) || (NULL != pending_timeout_head))
687   {
688     GNUNET_SCHEDULER_shutdown ();
689     return GNUNET_OK;
690   }
691   return GNUNET_NO;
692 }
693
694
695 /**
696  * Initialize and run scheduler.  This function will return when all
697  * tasks have completed.  On systems with signals, receiving a SIGTERM
698  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
699  * to be run after the active task is complete.  As a result, SIGTERM
700  * causes all active tasks to be scheduled with reason
701  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
702  * afterwards will execute normally!). Note that any particular signal
703  * will only shut down one scheduler; applications should always only
704  * create a single scheduler.
705  *
706  * @param task task to run immediately
707  * @param task_cls closure of @a task
708  */
709 void
710 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
711                       void *task_cls)
712 {
713   struct GNUNET_NETWORK_FDSet *rs;
714   struct GNUNET_NETWORK_FDSet *ws;
715   struct GNUNET_TIME_Relative timeout;
716   int ret;
717   struct GNUNET_SIGNAL_Context *shc_int;
718   struct GNUNET_SIGNAL_Context *shc_term;
719 #if (SIGTERM != GNUNET_TERM_SIG)
720   struct GNUNET_SIGNAL_Context *shc_gterm;
721 #endif
722
723 #ifndef MINGW
724   struct GNUNET_SIGNAL_Context *shc_quit;
725   struct GNUNET_SIGNAL_Context *shc_hup;
726   struct GNUNET_SIGNAL_Context *shc_pipe;
727 #endif
728   unsigned long long last_tr;
729   unsigned int busy_wait_warning;
730   const struct GNUNET_DISK_FileHandle *pr;
731   char c;
732
733   GNUNET_assert (NULL == active_task);
734   rs = GNUNET_NETWORK_fdset_create ();
735   ws = GNUNET_NETWORK_fdset_create ();
736   GNUNET_assert (NULL == shutdown_pipe_handle);
737   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
738                                            GNUNET_NO,
739                                            GNUNET_NO,
740                                            GNUNET_NO);
741   GNUNET_assert (NULL != shutdown_pipe_handle);
742   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
743                                 GNUNET_DISK_PIPE_END_READ);
744   GNUNET_assert (NULL != pr);
745   my_pid = getpid ();
746   LOG (GNUNET_ERROR_TYPE_DEBUG,
747        "Registering signal handlers\n");
748   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
749   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
750 #if (SIGTERM != GNUNET_TERM_SIG)
751   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG, &sighandler_shutdown);
752 #endif
753 #ifndef MINGW
754   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
755   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
756   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
757 #endif
758   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
759   current_lifeness = GNUNET_YES;
760   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
761                                                  task_cls,
762                                                  GNUNET_SCHEDULER_REASON_STARTUP,
763                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
764   active_task = (void *) (long) -1;     /* force passing of sanity check */
765   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
766                                           &GNUNET_OS_install_parent_control_handler,
767                                           NULL);
768   active_task = NULL;
769   last_tr = 0;
770   busy_wait_warning = 0;
771   while (GNUNET_OK == check_lifeness ())
772   {
773     GNUNET_NETWORK_fdset_zero (rs);
774     GNUNET_NETWORK_fdset_zero (ws);
775     timeout = GNUNET_TIME_UNIT_FOREVER_REL;
776     update_sets (rs, ws, &timeout);
777     GNUNET_NETWORK_fdset_handle_set (rs, pr);
778     if (ready_count > 0)
779     {
780       /* no blocking, more work already ready! */
781       timeout = GNUNET_TIME_UNIT_ZERO;
782     }
783     if (NULL == scheduler_select)
784       ret = GNUNET_NETWORK_socket_select (rs,
785                                           ws,
786                                           NULL,
787                                           timeout);
788     else
789       ret = scheduler_select (scheduler_select_cls,
790                               rs,
791                               ws,
792                               NULL,
793                               timeout);
794     if (ret == GNUNET_SYSERR)
795     {
796       if (errno == EINTR)
797         continue;
798
799       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
800 #ifndef MINGW
801 #if USE_LSOF
802       char lsof[512];
803
804       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
805       (void) close (1);
806       (void) dup2 (2, 1);
807       if (0 != system (lsof))
808         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
809                       "system");
810 #endif
811 #endif
812 #if DEBUG_FDS
813       struct GNUNET_SCHEDULER_Task *t;
814
815       for (t = pending_head; NULL != t; t = t->next)
816       {
817         if (-1 != t->read_fd)
818         {
819           int flags = fcntl (t->read_fd, F_GETFD);
820           if ((flags == -1) && (errno == EBADF))
821             {
822               LOG (GNUNET_ERROR_TYPE_ERROR,
823                    "Got invalid file descriptor %d!\n",
824                    t->read_fd);
825 #if EXECINFO
826               unsigned int i;
827
828               for (i = 0; i < t->num_backtrace_strings; i++)
829                 LOG (GNUNET_ERROR_TYPE_ERROR,
830                      "Trace: %s\n",
831                      t->backtrace_strings[i]);
832 #endif
833             }
834         }
835         if (-1 != t->write_fd)
836           {
837             int flags = fcntl (t->write_fd, F_GETFD);
838             if ((flags == -1) && (errno == EBADF))
839               {
840                 LOG (GNUNET_ERROR_TYPE_ERROR,
841                      "Got invalid file descriptor %d!\n",
842                      t->write_fd);
843 #if EXECINFO
844                 unsigned int i;
845
846                 for (i = 0; i < t->num_backtrace_strings; i++)
847                   LOG (GNUNET_ERROR_TYPE_DEBUG,
848                        "Trace: %s\n",
849                        t->backtrace_strings[i]);
850 #endif
851               }
852           }
853       }
854 #endif
855       GNUNET_assert (0);
856       break;
857     }
858
859     if ( (0 == ret) &&
860          (0 == timeout.rel_value_us) &&
861          (busy_wait_warning > 16) )
862     {
863       LOG (GNUNET_ERROR_TYPE_WARNING,
864            "Looks like we're busy waiting...\n");
865       short_wait (100);                /* mitigate */
866     }
867     check_ready (rs, ws);
868     run_ready (rs, ws);
869     if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
870     {
871       /* consume the signal */
872       GNUNET_DISK_file_read (pr, &c, sizeof (c));
873       /* mark all active tasks as ready due to shutdown */
874       GNUNET_SCHEDULER_shutdown ();
875     }
876     if (last_tr == tasks_run)
877     {
878       short_wait (1);
879       busy_wait_warning++;
880     }
881     else
882     {
883       last_tr = tasks_run;
884       busy_wait_warning = 0;
885     }
886   }
887   GNUNET_SIGNAL_handler_uninstall (shc_int);
888   GNUNET_SIGNAL_handler_uninstall (shc_term);
889 #if (SIGTERM != GNUNET_TERM_SIG)
890   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
891 #endif
892 #ifndef MINGW
893   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
894   GNUNET_SIGNAL_handler_uninstall (shc_quit);
895   GNUNET_SIGNAL_handler_uninstall (shc_hup);
896 #endif
897   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
898   shutdown_pipe_handle = NULL;
899   GNUNET_NETWORK_fdset_destroy (rs);
900   GNUNET_NETWORK_fdset_destroy (ws);
901 }
902
903
904 /**
905  * Obtain the reason code for why the current task was
906  * started.  Will return the same value as
907  * the `struct GNUNET_SCHEDULER_TaskContext`'s reason field.
908  *
909  * @return reason(s) why the current task is run
910  */
911 enum GNUNET_SCHEDULER_Reason
912 GNUNET_SCHEDULER_get_reason ()
913 {
914   GNUNET_assert (NULL != active_task);
915   return active_task->reason;
916 }
917
918
919 /**
920  * Get information about the current load of this scheduler.  Use this
921  * function to determine if an elective task should be added or simply
922  * dropped (if the decision should be made based on the number of
923  * tasks ready to run).
924  *
925  * @param p priority level to look at
926  * @return number of tasks pending right now
927  */
928 unsigned int
929 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
930 {
931   struct GNUNET_SCHEDULER_Task *pos;
932   unsigned int ret;
933
934   GNUNET_assert (NULL != active_task);
935   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
936     return ready_count;
937   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
938     p = current_priority;
939   ret = 0;
940   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
941     ret++;
942   return ret;
943 }
944
945
946 /**
947  * Cancel the task with the specified identifier.
948  * The task must not yet have run.
949  *
950  * @param task id of the task to cancel
951  * @return original closure of the task
952  */
953 void *
954 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
955 {
956   enum GNUNET_SCHEDULER_Priority p;
957   void *ret;
958
959   GNUNET_assert (NULL != active_task);
960   if (! task->in_ready_list)
961   {
962     if ( (-1 == task->read_fd) &&
963          (-1 == task->write_fd) &&
964          (NULL == task->read_set) &&
965          (NULL == task->write_set) )
966     {
967       GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
968                                    pending_timeout_tail,
969                                    task);
970       if (task == pending_timeout_last)
971         pending_timeout_last = NULL;
972     }
973     else
974     {
975       GNUNET_CONTAINER_DLL_remove (pending_head,
976                                    pending_tail,
977                                    task);
978     }
979   }
980   else
981   {
982     p = check_priority (task->priority);
983     GNUNET_CONTAINER_DLL_remove (ready_head[p],
984                                  ready_tail[p],
985                                  task);
986     ready_count--;
987   }
988   ret = task->callback_cls;
989   LOG (GNUNET_ERROR_TYPE_DEBUG,
990        "Canceling task %p\n",
991        task);
992   destroy_task (task);
993   return ret;
994 }
995
996
997 /**
998  * Continue the current execution with the given function.  This is
999  * similar to the other "add" functions except that there is no delay
1000  * and the reason code can be specified.
1001  *
1002  * @param task main function of the task
1003  * @param task_cls closure for @a task
1004  * @param reason reason for task invocation
1005  * @param priority priority to use for the task
1006  */
1007 void
1008 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
1009                                                void *task_cls,
1010                                                enum GNUNET_SCHEDULER_Reason reason,
1011                                                enum GNUNET_SCHEDULER_Priority priority)
1012 {
1013   struct GNUNET_SCHEDULER_Task *t;
1014
1015 #if EXECINFO
1016   void *backtrace_array[50];
1017 #endif
1018
1019   GNUNET_assert (NULL != task);
1020   GNUNET_assert ((NULL != active_task) ||
1021                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
1022   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1023 #if EXECINFO
1024   t->num_backtrace_strings = backtrace (backtrace_array, 50);
1025   t->backtrace_strings =
1026       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1027 #endif
1028   t->read_fd = -1;
1029   t->write_fd = -1;
1030   t->callback = task;
1031   t->callback_cls = task_cls;
1032 #if PROFILE_DELAYS
1033   t->start_time = GNUNET_TIME_absolute_get ();
1034 #endif
1035   t->reason = reason;
1036   t->priority = priority;
1037   t->lifeness = current_lifeness;
1038   LOG (GNUNET_ERROR_TYPE_DEBUG,
1039        "Adding continuation task %p\n",
1040        t);
1041   queue_ready_task (t);
1042 }
1043
1044
1045 /**
1046  * Schedule a new task to be run with a specified delay.  The task
1047  * will be scheduled for execution once the delay has expired.
1048  *
1049  * @param delay when should this operation time out? Use
1050  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1051  * @param priority priority to use for the task
1052  * @param task main function of the task
1053  * @param task_cls closure of @a task
1054  * @return unique task identifier for the job
1055  *         only valid until @a task is started!
1056  */
1057 struct GNUNET_SCHEDULER_Task *
1058 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1059                                             enum GNUNET_SCHEDULER_Priority priority,
1060                                             GNUNET_SCHEDULER_TaskCallback task,
1061                                             void *task_cls)
1062 {
1063   struct GNUNET_SCHEDULER_Task *t;
1064   struct GNUNET_SCHEDULER_Task *pos;
1065   struct GNUNET_SCHEDULER_Task *prev;
1066
1067 #if EXECINFO
1068   void *backtrace_array[MAX_TRACE_DEPTH];
1069 #endif
1070
1071   GNUNET_assert (NULL != active_task);
1072   GNUNET_assert (NULL != task);
1073   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1074   t->callback = task;
1075   t->callback_cls = task_cls;
1076 #if EXECINFO
1077   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1078   t->backtrace_strings =
1079       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1080 #endif
1081   t->read_fd = -1;
1082   t->write_fd = -1;
1083 #if PROFILE_DELAYS
1084   t->start_time = GNUNET_TIME_absolute_get ();
1085 #endif
1086   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1087   t->priority = priority;
1088   t->lifeness = current_lifeness;
1089   /* try tail first (optimization in case we are
1090    * appending to a long list of tasks with timeouts) */
1091   if (0 == delay.rel_value_us)
1092   {
1093     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
1094                                  pending_timeout_tail,
1095                                  t);
1096   }
1097   else
1098   {
1099     /* first move from heuristic start backwards to before start time */
1100     prev = pending_timeout_last;
1101     while ( (NULL != prev) &&
1102             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
1103       prev = prev->prev;
1104     /* now, move from heuristic start (or head of list) forward to insertion point */
1105     if (NULL == prev)
1106       pos = pending_timeout_head;
1107     else
1108       pos = prev->next;
1109     while ( (NULL != pos) &&
1110             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
1111               (0 != pos->reason) ) )
1112     {
1113       prev = pos;
1114       pos = pos->next;
1115     }
1116     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
1117                                        pending_timeout_tail,
1118                                        prev,
1119                                        t);
1120     /* finally, update heuristic insertion point to last insertion... */
1121     pending_timeout_last = t;
1122   }
1123
1124   LOG (GNUNET_ERROR_TYPE_DEBUG,
1125        "Adding task: %p\n",
1126        t);
1127 #if EXECINFO
1128   unsigned int i;
1129
1130   for (i = 0; i < t->num_backtrace_strings; i++)
1131     LOG (GNUNET_ERROR_TYPE_DEBUG,
1132          "Task %p trace %d: %s\n",
1133          t,
1134          i,
1135          t->backtrace_strings[i]);
1136 #endif
1137   return t;
1138 }
1139
1140
1141 /**
1142  * Schedule a new task to be run with a specified priority.
1143  *
1144  * @param prio how important is the new task?
1145  * @param task main function of the task
1146  * @param task_cls closure of @a task
1147  * @return unique task identifier for the job
1148  *         only valid until @a task is started!
1149  */
1150 struct GNUNET_SCHEDULER_Task *
1151 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1152                                     GNUNET_SCHEDULER_TaskCallback task,
1153                                     void *task_cls)
1154 {
1155   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1156                                                      prio,
1157                                                      task,
1158                                                      task_cls);
1159 }
1160
1161
1162 /**
1163  * Schedule a new task to be run with a specified delay.  The task
1164  * will be scheduled for execution once the delay has expired. It
1165  * will be run with the DEFAULT priority.
1166  *
1167  * @param delay when should this operation time out? Use
1168  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1169  * @param task main function of the task
1170  * @param task_cls closure of task
1171  * @return unique task identifier for the job
1172  *         only valid until "task" is started!
1173  */
1174 struct GNUNET_SCHEDULER_Task *
1175 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1176                               GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1177 {
1178   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1179                                                      GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1180                                                      task, task_cls);
1181 }
1182
1183
1184 /**
1185  * Schedule a new task to be run as soon as possible.  Note that this
1186  * does not guarantee that this will be the next task that is being
1187  * run, as other tasks with higher priority (or that are already ready
1188  * to run) might get to run first.  Just as with delays, clients must
1189  * not rely on any particular order of execution between tasks
1190  * scheduled concurrently.
1191  *
1192  * The task will be run with the DEFAULT priority.
1193  *
1194  * @param task main function of the task
1195  * @param task_cls closure of @a task
1196  * @return unique task identifier for the job
1197  *         only valid until "task" is started!
1198  */
1199 struct GNUNET_SCHEDULER_Task *
1200 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1201 {
1202   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO, task, task_cls);
1203 }
1204
1205
1206 /**
1207  * Schedule a new task to be run as soon as possible with the
1208  * (transitive) ignore-shutdown flag either explicitly set or
1209  * explicitly enabled.  This task (and all tasks created from it,
1210  * other than by another call to this function) will either count or
1211  * not count for the "lifeness" of the process.  This API is only
1212  * useful in a few special cases.
1213  *
1214  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1215  * @param task main function of the task
1216  * @param task_cls closure of @a task
1217  * @return unique task identifier for the job
1218  *         only valid until @a task is started!
1219  */
1220 struct GNUNET_SCHEDULER_Task *
1221 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1222                                         GNUNET_SCHEDULER_TaskCallback task,
1223                                         void *task_cls)
1224 {
1225   struct GNUNET_SCHEDULER_Task *ret;
1226
1227   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1228   ret->lifeness = lifeness;
1229   return ret;
1230 }
1231
1232
1233 /**
1234  * Schedule a new task to be run with a specified delay or when any of
1235  * the specified file descriptor sets is ready.  The delay can be used
1236  * as a timeout on the socket(s) being ready.  The task will be
1237  * scheduled for execution once either the delay has expired or any of
1238  * the socket operations is ready.  This is the most general
1239  * function of the "add" family.  Note that the "prerequisite_task"
1240  * must be satisfied in addition to any of the other conditions.  In
1241  * other words, the task will be started when
1242  * <code>
1243  * (prerequisite-run)
1244  * && (delay-ready
1245  *     || any-rs-ready
1246  *     || any-ws-ready
1247  *     || shutdown-active )
1248  * </code>
1249  *
1250  * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1251  *        which means that the task will only be run after we receive SIGTERM
1252  * @param priority priority to use
1253  * @param rfd file descriptor we want to read (can be -1)
1254  * @param wfd file descriptors we want to write (can be -1)
1255  * @param task main function of the task
1256  * @param task_cls closure of @a task
1257  * @return unique task identifier for the job
1258  *         only valid until @a task is started!
1259  */
1260 #ifndef MINGW
1261 static struct GNUNET_SCHEDULER_Task *
1262 add_without_sets (struct GNUNET_TIME_Relative delay,
1263                   enum GNUNET_SCHEDULER_Priority priority,
1264                   int rfd,
1265                   int wfd,
1266                   GNUNET_SCHEDULER_TaskCallback task,
1267                   void *task_cls)
1268 {
1269   struct GNUNET_SCHEDULER_Task *t;
1270
1271 #if EXECINFO
1272   void *backtrace_array[MAX_TRACE_DEPTH];
1273 #endif
1274
1275   GNUNET_assert (NULL != active_task);
1276   GNUNET_assert (NULL != task);
1277   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1278   t->callback = task;
1279   t->callback_cls = task_cls;
1280 #if EXECINFO
1281   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1282   t->backtrace_strings =
1283       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1284 #endif
1285 #if DEBUG_FDS
1286   if (-1 != rfd)
1287   {
1288     int flags = fcntl (rfd, F_GETFD);
1289
1290     if ((flags == -1) && (errno == EBADF))
1291     {
1292       LOG (GNUNET_ERROR_TYPE_ERROR,
1293            "Got invalid file descriptor %d!\n",
1294            rfd);
1295 #if EXECINFO
1296       unsigned int i;
1297
1298       for (i = 0; i < t->num_backtrace_strings; i++)
1299         LOG (GNUNET_ERROR_TYPE_ERROR,
1300              "Trace: %s\n",
1301              t->backtrace_strings[i]);
1302 #endif
1303       GNUNET_assert (0);
1304     }
1305   }
1306   if (-1 != wfd)
1307   {
1308     int flags = fcntl (wfd, F_GETFD);
1309
1310     if (flags == -1 && errno == EBADF)
1311     {
1312       LOG (GNUNET_ERROR_TYPE_ERROR,
1313            "Got invalid file descriptor %d!\n",
1314            wfd);
1315 #if EXECINFO
1316       unsigned int i;
1317
1318       for (i = 0; i < t->num_backtrace_strings; i++)
1319         LOG (GNUNET_ERROR_TYPE_DEBUG,
1320              "Trace: %s\n",
1321              t->backtrace_strings[i]);
1322 #endif
1323       GNUNET_assert (0);
1324     }
1325   }
1326 #endif
1327   t->read_fd = rfd;
1328   GNUNET_assert (wfd >= -1);
1329   t->write_fd = wfd;
1330 #if PROFILE_DELAYS
1331   t->start_time = GNUNET_TIME_absolute_get ();
1332 #endif
1333   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1334   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1335   t->lifeness = current_lifeness;
1336   GNUNET_CONTAINER_DLL_insert (pending_head,
1337                                pending_tail,
1338                                t);
1339   max_priority_added = GNUNET_MAX (max_priority_added,
1340                                    t->priority);
1341   LOG (GNUNET_ERROR_TYPE_DEBUG,
1342        "Adding task %p\n",
1343        t);
1344 #if EXECINFO
1345   unsigned int i;
1346
1347   for (i = 0; i < t->num_backtrace_strings; i++)
1348     LOG (GNUNET_ERROR_TYPE_DEBUG,
1349          "Task %p trace %d: %s\n",
1350          t,
1351          i,
1352          t->backtrace_strings[i]);
1353 #endif
1354   return t;
1355 }
1356 #endif
1357
1358
1359 /**
1360  * Schedule a new task to be run with a specified delay or when the
1361  * specified file descriptor is ready for reading.  The delay can be
1362  * used as a timeout on the socket being ready.  The task will be
1363  * scheduled for execution once either the delay has expired or the
1364  * socket operation is ready.  It will be run with the DEFAULT priority.
1365  *
1366  * @param delay when should this operation time out? Use
1367  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1368  * @param rfd read file-descriptor
1369  * @param task main function of the task
1370  * @param task_cls closure of @a task
1371  * @return unique task identifier for the job
1372  *         only valid until @a task is started!
1373  */
1374 struct GNUNET_SCHEDULER_Task *
1375 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1376                                struct GNUNET_NETWORK_Handle *rfd,
1377                                GNUNET_SCHEDULER_TaskCallback task,
1378                                void *task_cls)
1379 {
1380   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1381                                                       GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1382                                                       rfd, task, task_cls);
1383 }
1384
1385
1386 /**
1387  * Schedule a new task to be run with a specified priority and to be
1388  * run after the specified delay or when the specified file descriptor
1389  * is ready for reading.  The delay can be used as a timeout on the
1390  * socket being ready.  The task will be scheduled for execution once
1391  * either the delay has expired or the socket operation is ready.  It
1392  * will be run with the DEFAULT priority.
1393  *
1394  * @param delay when should this operation time out? Use
1395  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1396  * @param priority priority to use for the task
1397  * @param rfd read file-descriptor
1398  * @param task main function of the task
1399  * @param task_cls closure of @a task
1400  * @return unique task identifier for the job
1401  *         only valid until @a task is started!
1402  */
1403 struct GNUNET_SCHEDULER_Task *
1404 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1405                                              enum GNUNET_SCHEDULER_Priority priority,
1406                                              struct GNUNET_NETWORK_Handle *rfd,
1407                                              GNUNET_SCHEDULER_TaskCallback task,
1408                                              void *task_cls)
1409 {
1410   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1411                                                  rfd,
1412                                                  GNUNET_YES,
1413                                                  GNUNET_NO,
1414                                                  task, task_cls);
1415 }
1416
1417
1418 /**
1419  * Schedule a new task to be run with a specified delay or when the
1420  * specified file descriptor is ready for writing.  The delay can be
1421  * used as a timeout on the socket being ready.  The task will be
1422  * scheduled for execution once either the delay has expired or the
1423  * socket operation is ready.  It will be run with the priority of
1424  * the calling task.
1425  *
1426  * @param delay when should this operation time out? Use
1427  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1428  * @param wfd write file-descriptor
1429  * @param task main function of the task
1430  * @param task_cls closure of @a task
1431  * @return unique task identifier for the job
1432  *         only valid until @a task is started!
1433  */
1434 struct GNUNET_SCHEDULER_Task *
1435 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1436                                 struct GNUNET_NETWORK_Handle *wfd,
1437                                 GNUNET_SCHEDULER_TaskCallback task,
1438                                 void *task_cls)
1439 {
1440   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1441                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1442                                                  wfd,
1443                                                  GNUNET_NO, GNUNET_YES,
1444                                                  task, task_cls);
1445 }
1446
1447 /**
1448  * Schedule a new task to be run with a specified delay or when the
1449  * specified file descriptor is ready.  The delay can be
1450  * used as a timeout on the socket being ready.  The task will be
1451  * scheduled for execution once either the delay has expired or the
1452  * socket operation is ready.
1453  *
1454  * @param delay when should this operation time out? Use
1455  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1456  * @param priority priority of the task
1457  * @param fd file-descriptor
1458  * @param on_read whether to poll the file-descriptor for readability
1459  * @param on_write whether to poll the file-descriptor for writability
1460  * @param task main function of the task
1461  * @param task_cls closure of task
1462  * @return unique task identifier for the job
1463  *         only valid until "task" is started!
1464  */
1465 struct GNUNET_SCHEDULER_Task *
1466 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1467                                          enum GNUNET_SCHEDULER_Priority priority,
1468                                          struct GNUNET_NETWORK_Handle *fd,
1469                                          int on_read,
1470                                          int on_write,
1471                                          GNUNET_SCHEDULER_TaskCallback task,
1472                                          void *task_cls)
1473 {
1474 #if MINGW
1475   struct GNUNET_NETWORK_FDSet *s;
1476   struct GNUNET_SCHEDULER_Task * ret;
1477
1478   GNUNET_assert (NULL != fd);
1479   s = GNUNET_NETWORK_fdset_create ();
1480   GNUNET_NETWORK_fdset_set (s, fd);
1481   ret = GNUNET_SCHEDULER_add_select (
1482       priority, delay,
1483       on_read  ? s : NULL,
1484       on_write ? s : NULL,
1485       task, task_cls);
1486   GNUNET_NETWORK_fdset_destroy (s);
1487   return ret;
1488 #else
1489   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1490   return add_without_sets (delay, priority,
1491                            on_read  ? GNUNET_NETWORK_get_fd (fd) : -1,
1492                            on_write ? GNUNET_NETWORK_get_fd (fd) : -1,
1493                            task, task_cls);
1494 #endif
1495 }
1496
1497
1498 /**
1499  * Schedule a new task to be run with a specified delay or when the
1500  * specified file descriptor is ready for reading.  The delay can be
1501  * used as a timeout on the socket being ready.  The task will be
1502  * scheduled for execution once either the delay has expired or the
1503  * socket operation is ready. It will be run with the DEFAULT priority.
1504  *
1505  * @param delay when should this operation time out? Use
1506  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1507  * @param rfd read file-descriptor
1508  * @param task main function of the task
1509  * @param task_cls closure of @a task
1510  * @return unique task identifier for the job
1511  *         only valid until @a task is started!
1512  */
1513 struct GNUNET_SCHEDULER_Task *
1514 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1515                                 const struct GNUNET_DISK_FileHandle *rfd,
1516                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1517 {
1518   return GNUNET_SCHEDULER_add_file_with_priority (
1519       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1520       rfd, GNUNET_YES, GNUNET_NO,
1521       task, task_cls);
1522 }
1523
1524
1525 /**
1526  * Schedule a new task to be run with a specified delay or when the
1527  * specified file descriptor is ready for writing.  The delay can be
1528  * used as a timeout on the socket being ready.  The task will be
1529  * scheduled for execution once either the delay has expired or the
1530  * socket operation is ready. It will be run with the DEFAULT priority.
1531  *
1532  * @param delay when should this operation time out? Use
1533  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1534  * @param wfd write file-descriptor
1535  * @param task main function of the task
1536  * @param task_cls closure of @a task
1537  * @return unique task identifier for the job
1538  *         only valid until @a task is started!
1539  */
1540 struct GNUNET_SCHEDULER_Task *
1541 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1542                                  const struct GNUNET_DISK_FileHandle *wfd,
1543                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1544 {
1545   return GNUNET_SCHEDULER_add_file_with_priority (
1546       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1547       wfd, GNUNET_NO, GNUNET_YES,
1548       task, task_cls);
1549 }
1550
1551
1552 /**
1553  * Schedule a new task to be run with a specified delay or when the
1554  * specified file descriptor is ready.  The delay can be
1555  * used as a timeout on the socket being ready.  The task will be
1556  * scheduled for execution once either the delay has expired or the
1557  * socket operation is ready.
1558  *
1559  * @param delay when should this operation time out? Use
1560  *        #GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1561  * @param priority priority of the task
1562  * @param fd file-descriptor
1563  * @param on_read whether to poll the file-descriptor for readability
1564  * @param on_write whether to poll the file-descriptor for writability
1565  * @param task main function of the task
1566  * @param task_cls closure of @a task
1567  * @return unique task identifier for the job
1568  *         only valid until @a task is started!
1569  */
1570 struct GNUNET_SCHEDULER_Task *
1571 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1572                                          enum GNUNET_SCHEDULER_Priority priority,
1573                                          const struct GNUNET_DISK_FileHandle *fd,
1574                                          int on_read, int on_write,
1575                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1576 {
1577 #if MINGW
1578   struct GNUNET_NETWORK_FDSet *s;
1579   struct GNUNET_SCHEDULER_Task * ret;
1580
1581   GNUNET_assert (NULL != fd);
1582   s = GNUNET_NETWORK_fdset_create ();
1583   GNUNET_NETWORK_fdset_handle_set (s, fd);
1584   ret = GNUNET_SCHEDULER_add_select (
1585       priority, delay,
1586       on_read  ? s : NULL,
1587       on_write ? s : NULL,
1588       task, task_cls);
1589   GNUNET_NETWORK_fdset_destroy (s);
1590   return ret;
1591 #else
1592   int real_fd;
1593
1594   GNUNET_DISK_internal_file_handle_ (fd, &real_fd, sizeof (int));
1595   GNUNET_assert (real_fd >= 0);
1596   return add_without_sets (
1597       delay, priority,
1598       on_read  ? real_fd : -1,
1599       on_write ? real_fd : -1,
1600       task, task_cls);
1601 #endif
1602 }
1603
1604
1605 /**
1606  * Schedule a new task to be run with a specified delay or when any of
1607  * the specified file descriptor sets is ready.  The delay can be used
1608  * as a timeout on the socket(s) being ready.  The task will be
1609  * scheduled for execution once either the delay has expired or any of
1610  * the socket operations is ready.  This is the most general
1611  * function of the "add" family.  Note that the "prerequisite_task"
1612  * must be satisfied in addition to any of the other conditions.  In
1613  * other words, the task will be started when
1614  * <code>
1615  * (prerequisite-run)
1616  * && (delay-ready
1617  *     || any-rs-ready
1618  *     || any-ws-ready
1619  *     || (shutdown-active && run-on-shutdown) )
1620  * </code>
1621  *
1622  * @param prio how important is this task?
1623  * @param delay how long should we wait? Use #GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1624  *        which means that the task will only be run after we receive SIGTERM
1625  * @param rs set of file descriptors we want to read (can be NULL)
1626  * @param ws set of file descriptors we want to write (can be NULL)
1627  * @param task main function of the task
1628  * @param task_cls closure of @a task
1629  * @return unique task identifier for the job
1630  *         only valid until @a task is started!
1631  */
1632 struct GNUNET_SCHEDULER_Task *
1633 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1634                              struct GNUNET_TIME_Relative delay,
1635                              const struct GNUNET_NETWORK_FDSet *rs,
1636                              const struct GNUNET_NETWORK_FDSet *ws,
1637                              GNUNET_SCHEDULER_TaskCallback task,
1638                              void *task_cls)
1639 {
1640   struct GNUNET_SCHEDULER_Task *t;
1641 #if EXECINFO
1642   void *backtrace_array[MAX_TRACE_DEPTH];
1643 #endif
1644
1645   if ( (NULL == rs) &&
1646        (NULL == ws) )
1647     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1648                                                        prio,
1649                                                        task,
1650                                                        task_cls);
1651   GNUNET_assert (NULL != active_task);
1652   GNUNET_assert (NULL != task);
1653   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1654   t->callback = task;
1655   t->callback_cls = task_cls;
1656 #if EXECINFO
1657   t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
1658   t->backtrace_strings =
1659       backtrace_symbols (backtrace_array, t->num_backtrace_strings);
1660 #endif
1661   t->read_fd = -1;
1662   t->write_fd = -1;
1663   if (NULL != rs)
1664   {
1665     t->read_set = GNUNET_NETWORK_fdset_create ();
1666     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1667   }
1668   if (NULL != ws)
1669   {
1670     t->write_set = GNUNET_NETWORK_fdset_create ();
1671     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1672   }
1673 #if PROFILE_DELAYS
1674   t->start_time = GNUNET_TIME_absolute_get ();
1675 #endif
1676   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1677   t->priority =
1678       check_priority ((prio ==
1679                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1680                       prio);
1681   t->lifeness = current_lifeness;
1682   GNUNET_CONTAINER_DLL_insert (pending_head,
1683                                pending_tail,
1684                                t);
1685   max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
1686   LOG (GNUNET_ERROR_TYPE_DEBUG,
1687        "Adding task %p\n",
1688        t);
1689 #if EXECINFO
1690   int i;
1691
1692   for (i = 0; i < t->num_backtrace_strings; i++)
1693     LOG (GNUNET_ERROR_TYPE_DEBUG,
1694          "Task p trace %d: %s\n",
1695          t,
1696          i,
1697          t->backtrace_strings[i]);
1698 #endif
1699   return t;
1700 }
1701
1702 /* end of scheduler.c */