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