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