f1cfd2c3667b554a7535335b9b20efeb585078a7
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       (C) 2009 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 2, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file util/scheduler.c
23  * @brief schedule computations using continuation passing style
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_common.h"
28 #include "gnunet_os_lib.h"
29 #include "gnunet_scheduler_lib.h"
30 #include "gnunet_signal_lib.h"
31 #include "gnunet_time_lib.h"
32 #include "disk.h"
33 #ifdef LINUX
34 #include "execinfo.h"
35
36
37 /**
38  * Use lsof to generate file descriptor reports on select error?
39  * (turn off for stable releases).
40  */
41 #define USE_LSOF GNUNET_YES
42
43 /**
44  * Obtain trace information for all scheduler calls that schedule tasks.
45  */
46 #define EXECINFO GNUNET_NO
47
48 /**
49  * Depth of the traces collected via EXECINFO.
50  */
51 #define MAX_TRACE_DEPTH 50
52 #endif
53
54 #define DEBUG_TASKS GNUNET_NO
55
56 /**
57  * Should we figure out which tasks are delayed for a while
58  * before they are run? (Consider using in combination with EXECINFO).
59  */
60 #define PROFILE_DELAYS GNUNET_NO
61
62 /**
63  * Task that were in the queue for longer than this are reported if
64  * PROFILE_DELAYS is active.
65  */
66 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
67
68 /**
69  * Linked list of pending tasks.
70  */
71 struct Task
72 {
73   /**
74    * This is a linked list.
75    */
76   struct Task *next;
77
78   /**
79    * Function to run when ready.
80    */
81   GNUNET_SCHEDULER_Task callback;
82
83   /**
84    * Closure for the callback.
85    */
86   void *callback_cls;
87
88   /**
89    * Set of file descriptors this task is waiting
90    * for for reading.  Once ready, this is updated
91    * to reflect the set of file descriptors ready
92    * for operation.
93    */
94   struct GNUNET_NETWORK_FDSet *read_set;
95
96   /**
97    * Set of file descriptors this task is waiting for for writing.
98    * Once ready, this is updated to reflect the set of file
99    * descriptors ready for operation.
100    */
101   struct GNUNET_NETWORK_FDSet *write_set;
102
103   /**
104    * Unique task identifier.
105    */
106   GNUNET_SCHEDULER_TaskIdentifier id;
107
108   /**
109    * Identifier of a prerequisite task.
110    */
111   GNUNET_SCHEDULER_TaskIdentifier prereq_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 #if EXECINFO
149   /**
150    * Array of strings which make up a backtrace from the point when this
151    * task was scheduled (essentially, who scheduled the task?)
152    */
153   char **backtrace_strings;
154
155   /**
156    * Size of the backtrace_strings array
157    */
158   int num_backtrace_strings;
159 #endif
160
161
162 };
163
164
165 /**
166  * List of tasks waiting for an event.
167  */
168 static struct Task *pending;
169
170 /**
171  * List of tasks waiting ONLY for a timeout event.
172  * Sorted by timeout (earliest first).  Used so that
173  * we do not traverse the list of these tasks when
174  * building select sets (we just look at the head
175  * to determine the respective timeout ONCE).
176  */
177 static struct Task *pending_timeout;
178
179 /**
180  * Last inserted task waiting ONLY for a timeout event.
181  * Used to (heuristically) speed up insertion.
182  */
183 static struct Task *pending_timeout_last;
184
185 /**
186  * ID of the task that is running right now.
187  */
188 static struct Task *active_task;
189
190 /**
191  * List of tasks ready to run right now,
192  * grouped by importance.
193  */
194 static struct Task *ready[GNUNET_SCHEDULER_PRIORITY_COUNT];
195
196 /**
197  * Identity of the last task queued.  Incremented for each task to
198  * generate a unique task ID (it is virtually impossible to start
199  * more than 2^64 tasks during the lifetime of a process).
200  */
201 static GNUNET_SCHEDULER_TaskIdentifier last_id;
202
203 /**
204  * Highest number so that all tasks with smaller identifiers
205  * have already completed.  Also the lowest number of a task
206  * still waiting to be executed.
207  */
208 static GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
209
210 /**
211  * Number of tasks on the ready list.
212  */
213 static unsigned int ready_count;
214
215 /**
216  * How many tasks have we run so far?
217  */
218 static unsigned long long tasks_run;
219
220 /**
221  * Priority of the task running right now.  Only
222  * valid while a task is running.
223  */
224 static enum GNUNET_SCHEDULER_Priority current_priority;
225
226 /**
227  * Priority of the highest task added in the current select
228  * iteration.
229  */
230 static enum GNUNET_SCHEDULER_Priority max_priority_added;
231
232
233 /**
234  * Check that the given priority is legal (and return it).
235  *
236  * @param p priority value to check
237  * @return p on success, 0 on error
238  */
239 static enum GNUNET_SCHEDULER_Priority
240 check_priority (enum GNUNET_SCHEDULER_Priority p)
241 {
242   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
243     return p;
244   GNUNET_assert (0);
245   return 0;                     /* make compiler happy */
246 }
247
248
249 /**
250  * Is a task with this identifier still pending?  Also updates
251  * "lowest_pending_id" as a side-effect (for faster checks in the
252  * future), but only if the return value is "GNUNET_NO" (and
253  * the "lowest_pending_id" check failed).
254  *
255  * @param id which task are we checking for
256  * @return GNUNET_YES if so, GNUNET_NO if not
257  */
258 static int
259 is_pending (GNUNET_SCHEDULER_TaskIdentifier id)
260 {
261   struct Task *pos;
262   enum GNUNET_SCHEDULER_Priority p;
263   GNUNET_SCHEDULER_TaskIdentifier min;
264
265   if (id < lowest_pending_id)
266     return GNUNET_NO;
267   min = -1;                     /* maximum value */
268   pos = pending;
269   while (pos != NULL)
270     {
271       if (pos->id == id)
272         return GNUNET_YES;
273       if (pos->id < min)
274         min = pos->id;
275       pos = pos->next;
276     }
277   pos = pending_timeout;
278   while (pos != NULL)
279     {
280       if (pos->id == id)
281         return GNUNET_YES;
282       if (pos->id < min)
283         min = pos->id;
284       pos = pos->next;
285     }
286   for (p = 0; p < GNUNET_SCHEDULER_PRIORITY_COUNT; p++)
287     {
288       pos = ready[p];
289       while (pos != NULL)
290         {
291           if (pos->id == id)
292             return GNUNET_YES;
293           if (pos->id < min)
294             min = pos->id;
295           pos = pos->next;
296         }
297     }
298   lowest_pending_id = min;
299   return GNUNET_NO;
300 }
301
302
303 /**
304  * Update all sets and timeout for select.
305  *
306  * @param rs read-set, set to all FDs we would like to read (updated)
307  * @param ws write-set, set to all FDs we would like to write (updated)
308  * @param timeout next timeout (updated)
309  */
310 static void
311 update_sets (struct GNUNET_NETWORK_FDSet *rs,
312              struct GNUNET_NETWORK_FDSet *ws,
313              struct GNUNET_TIME_Relative *timeout)
314 {
315   struct Task *pos;
316   struct GNUNET_TIME_Absolute now;
317   struct GNUNET_TIME_Relative to;
318
319   now = GNUNET_TIME_absolute_get ();
320   pos = pending_timeout;
321   if (pos != NULL) 
322     {
323       to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
324       if (timeout->rel_value > to.rel_value)
325         *timeout = to;
326       if (pos->reason != 0)
327         *timeout = GNUNET_TIME_UNIT_ZERO;
328     }
329   pos = pending;
330   while (pos != NULL)
331     {
332       if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
333           (GNUNET_YES == is_pending (pos->prereq_id)))
334         {
335           pos = pos->next;
336           continue;
337         }
338       if (pos->timeout.abs_value != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
339         {
340           to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
341           if (timeout->rel_value > to.rel_value)
342             *timeout = to;
343         }
344       if (pos->read_fd != -1)
345         GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
346       if (pos->write_fd != -1)
347         GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
348       if (pos->read_set != NULL)
349         GNUNET_NETWORK_fdset_add (rs, pos->read_set);
350       if (pos->write_set != NULL)
351         GNUNET_NETWORK_fdset_add (ws, pos->write_set);
352       if (pos->reason != 0)
353         *timeout = GNUNET_TIME_UNIT_ZERO;
354       pos = pos->next;
355     }
356 }
357
358
359 /**
360  * Check if the ready set overlaps with the set we want to have ready.
361  * If so, update the want set (set all FDs that are ready).  If not,
362  * return GNUNET_NO.
363  *
364  * @param ready set that is ready
365  * @param want set that we want to be ready
366  * @return GNUNET_YES if there was some overlap
367  */
368 static int
369 set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
370               struct GNUNET_NETWORK_FDSet *want)
371 {
372   if ( (NULL == want) || (NULL == ready) )
373     return GNUNET_NO;
374   if (GNUNET_NETWORK_fdset_overlap (ready, want))
375     {
376       /* copy all over (yes, there maybe unrelated bits,
377          but this should not hurt well-written clients) */
378       GNUNET_NETWORK_fdset_copy (want, ready);
379       return GNUNET_YES;
380     }
381   return GNUNET_NO;
382 }
383
384
385 /**
386  * Check if the given task is eligible to run now.
387  * Also set the reason why it is eligible.
388  *
389  * @param task task to check if it is ready
390  * @param now the current time
391  * @param rs set of FDs ready for reading
392  * @param ws set of FDs ready for writing
393  * @return GNUNET_YES if we can run it, GNUNET_NO if not.
394  */
395 static int
396 is_ready (struct Task *task,
397           struct GNUNET_TIME_Absolute now,
398           const struct GNUNET_NETWORK_FDSet *rs,
399           const struct GNUNET_NETWORK_FDSet *ws)
400 {
401   enum GNUNET_SCHEDULER_Reason reason;
402
403   reason = task->reason;
404   if (now.abs_value >= task->timeout.abs_value)
405     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
406   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
407        ( ( (task->read_fd != -1) &&
408            (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd)) ) ||
409          (set_overlaps (rs, task->read_set) ) ) )
410     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
411   if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
412       ( ( (task->write_fd != -1) &&
413           (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)) ) ||
414         (set_overlaps (ws, task->write_set) ) ) )
415     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
416   if (reason == 0)
417     return GNUNET_NO;           /* not ready */    
418   if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
419     {
420       if (GNUNET_YES == is_pending (task->prereq_id))
421         {
422           task->reason = reason;
423           return GNUNET_NO;       /* prereq waiting */
424         }
425       reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
426     }
427   task->reason = reason;
428   return GNUNET_YES;
429 }
430
431
432 /**
433  * Put a task that is ready for execution into the ready queue.
434  *
435  * @param handle the scheduler
436  * @param task task ready for execution
437  */
438 static void
439 queue_ready_task (struct Task *task)
440 {
441   enum GNUNET_SCHEDULER_Priority p = task->priority;
442   if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
443     p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
444   task->next = ready[check_priority (p)];
445   ready[check_priority (p)] = task;
446   ready_count++;
447 }
448
449
450 /**
451  * Check which tasks are ready and move them
452  * to the respective ready queue.
453  *
454  * @param handle the scheduler
455  * @param rs FDs ready for reading
456  * @param ws FDs ready for writing
457  */
458 static void
459 check_ready (const struct GNUNET_NETWORK_FDSet *rs,
460              const struct GNUNET_NETWORK_FDSet *ws)
461 {
462   struct Task *pos;
463   struct Task *prev;
464   struct Task *next;
465   struct GNUNET_TIME_Absolute now;
466
467   now = GNUNET_TIME_absolute_get ();
468   prev = NULL;
469   pos = pending_timeout;
470   while (pos != NULL)
471     {
472       next = pos->next;
473       if (now.abs_value >= pos->timeout.abs_value)
474         pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
475       if (0 == pos->reason)
476         break;
477       pending_timeout = next;
478       if (pending_timeout_last == pos)
479         pending_timeout_last = NULL;
480       queue_ready_task (pos);
481       pos = next;
482     }
483   pos = pending;
484   while (pos != NULL)
485     {
486 #if DEBUG_TASKS
487       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488                   "Checking readiness of task: %llu / %p\n",
489                   pos->id, pos->callback_cls);
490 #endif
491       next = pos->next;
492       if (GNUNET_YES == is_ready (pos, now, rs, ws))
493         {
494           if (prev == NULL)
495             pending = next;
496           else
497             prev->next = next;
498           queue_ready_task (pos);
499           pos = next;
500           continue;
501         }
502       prev = pos;
503       pos = next;
504     }
505 }
506
507
508 /**
509  * Request the shutdown of a scheduler.  Marks all currently
510  * pending tasks as ready because of shutdown.  This will
511  * cause all tasks to run (as soon as possible, respecting
512  * priorities and prerequisite tasks).  Note that tasks
513  * scheduled AFTER this call may still be delayed arbitrarily.
514  */
515 void
516 GNUNET_SCHEDULER_shutdown ()
517 {
518   struct Task *pos;
519   int i;
520
521   pos = pending_timeout;
522   while (pos != NULL)
523     {
524       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
525       /* we don't move the task into the ready queue yet; check_ready
526          will do that later, possibly adding additional
527          readiness-factors */
528       pos = pos->next;
529     }
530   pos = pending;
531   while (pos != NULL)
532     {
533       pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
534       /* we don't move the task into the ready queue yet; check_ready
535          will do that later, possibly adding additional
536          readiness-factors */
537       pos = pos->next;
538     }
539   for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
540     {
541       pos = ready[i];
542       while (pos != NULL)
543         {
544           pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
545           /* we don't move the task into the ready queue yet; check_ready
546              will do that later, possibly adding additional
547              readiness-factors */
548           pos = pos->next;
549         }
550     }  
551 }
552
553
554 /**
555  * Destroy a task (release associated resources)
556  *
557  * @param t task to destroy
558  */
559 static void
560 destroy_task (struct Task *t)
561 {
562   if (NULL != t->read_set)
563     GNUNET_NETWORK_fdset_destroy (t->read_set);
564   if (NULL != t->write_set)
565     GNUNET_NETWORK_fdset_destroy (t->write_set);
566 #if EXECINFO
567   GNUNET_free (t->backtrace_strings);
568 #endif
569   GNUNET_free (t);
570 }
571
572
573 /**
574  * Run at least one task in the highest-priority queue that is not
575  * empty.  Keep running tasks until we are either no longer running
576  * "URGENT" tasks or until we have at least one "pending" task (which
577  * may become ready, hence we should select on it).  Naturally, if
578  * there are no more ready tasks, we also return.  
579  *
580  * @param rs FDs ready for reading
581  * @param ws FDs ready for writing
582  */
583 static void
584 run_ready (struct GNUNET_NETWORK_FDSet *rs,
585            struct GNUNET_NETWORK_FDSet *ws)
586 {
587   enum GNUNET_SCHEDULER_Priority p;
588   struct Task *pos;
589   struct GNUNET_SCHEDULER_TaskContext tc;
590
591   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
592   do
593     {
594       if (ready_count == 0)
595         return;
596       GNUNET_assert (ready[GNUNET_SCHEDULER_PRIORITY_KEEP] == NULL);
597       /* yes, p>0 is correct, 0 is "KEEP" which should
598          always be an empty queue (see assertion)! */
599       for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
600         {
601           pos = ready[p];
602           if (pos != NULL)
603             break;
604         }
605       GNUNET_assert (pos != NULL);      /* ready_count wrong? */
606       ready[p] = pos->next;
607       ready_count--;
608       if (current_priority != pos->priority)
609         {
610           current_priority = pos->priority;
611           (void) GNUNET_OS_set_process_priority (GNUNET_OS_process_current (), pos->priority);
612         }
613       active_task = pos;
614 #if PROFILE_DELAYS
615       if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value >
616           DELAY_THRESHOLD.rel_value)
617         {
618           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619                       "Task %llu took %llums to be scheduled\n",
620                       pos->id,
621                       (unsigned long long) GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value);
622         }
623 #endif
624       tc.reason = pos->reason;
625       tc.read_ready = (pos->read_set == NULL) ? rs : pos->read_set; 
626       if ( (pos->read_fd != -1) &&
627            (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) )
628         GNUNET_NETWORK_fdset_set_native (rs,
629                                          pos->read_fd);
630       tc.write_ready = (pos->write_set == NULL) ? ws : pos->write_set;
631       if ( (pos->write_fd != -1) &&
632            (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) )
633         GNUNET_NETWORK_fdset_set_native (ws,
634                                          pos->write_fd);
635       if ( ( (tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0) &&
636            (pos->write_fd != -1) &&
637            (! GNUNET_NETWORK_fdset_test_native (ws,
638                                                 pos->write_fd))) 
639         abort (); // added to ready in previous select loop!
640 #if DEBUG_TASKS
641       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642                   "Running task: %llu / %p\n", pos->id, pos->callback_cls);
643 #endif
644       pos->callback (pos->callback_cls, &tc);
645 #if EXECINFO
646       int i;
647       for (i=0;i<pos->num_backtrace_strings;i++)
648         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
649                     "Task %llu trace %d: %s\n",
650                     pos->id,
651                     i,
652                     pos->backtrace_strings[i]);
653 #endif
654       active_task = NULL;
655       destroy_task (pos);
656       tasks_run++;
657     }
658   while ( (pending == NULL) || (p >= max_priority_added) );
659 }
660
661 /**
662  * Pipe used to communicate shutdown via signal.
663  */
664 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
665
666 /**
667  * Signal handler called for SIGPIPE.
668  */
669 #ifndef MINGW
670 static void
671 sighandler_pipe ()
672 {
673   return;
674 }
675 #endif
676 /**
677  * Signal handler called for signals that should cause us to shutdown.
678  */
679 static void
680 sighandler_shutdown ()
681 {
682   static char c;
683   int old_errno = errno; /* backup errno */
684
685   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
686                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE), &c,
687                           sizeof (c));
688   errno = old_errno;
689 }
690
691
692 /**
693  * Initialize and run scheduler.  This function will return when all
694  * tasks have completed.  On systems with signals, receiving a SIGTERM
695  * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
696  * to be run after the active task is complete.  As a result, SIGTERM
697  * causes all active tasks to be scheduled with reason
698  * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
699  * afterwards will execute normally!). Note that any particular signal
700  * will only shut down one scheduler; applications should always only
701  * create a single scheduler.
702  *
703  * @param task task to run immediately
704  * @param task_cls closure of task
705  */
706 void
707 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
708 {
709   struct GNUNET_NETWORK_FDSet *rs;
710   struct GNUNET_NETWORK_FDSet *ws;
711   struct GNUNET_TIME_Relative timeout;
712   int ret;
713   struct GNUNET_SIGNAL_Context *shc_int;
714   struct GNUNET_SIGNAL_Context *shc_term;
715 #ifndef MINGW
716   struct GNUNET_SIGNAL_Context *shc_quit;
717   struct GNUNET_SIGNAL_Context *shc_hup;
718   struct GNUNET_SIGNAL_Context *shc_pipe;
719 #endif
720   unsigned long long last_tr;
721   unsigned int busy_wait_warning;
722   const struct GNUNET_DISK_FileHandle *pr;
723   char c;
724
725   GNUNET_assert (active_task == NULL);
726   rs = GNUNET_NETWORK_fdset_create ();
727   ws = GNUNET_NETWORK_fdset_create ();
728   GNUNET_assert (shutdown_pipe_handle == NULL);
729   shutdown_pipe_handle =  GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO);
730   GNUNET_assert (shutdown_pipe_handle != NULL);
731   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_READ);
732   GNUNET_assert (pr != NULL);
733   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
734   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
735 #ifndef MINGW
736   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
737   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
738   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
739 #endif
740   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
741   GNUNET_SCHEDULER_add_continuation (task,
742                                      task_cls,
743                                      GNUNET_SCHEDULER_REASON_STARTUP);
744   last_tr = 0;
745   busy_wait_warning = 0;
746   while ((pending != NULL) ||
747          (pending_timeout != NULL) ||
748          (ready_count > 0))
749     {
750       GNUNET_NETWORK_fdset_zero (rs);
751       GNUNET_NETWORK_fdset_zero (ws);
752       timeout = GNUNET_TIME_UNIT_FOREVER_REL;
753       update_sets (rs, ws, &timeout);
754       GNUNET_NETWORK_fdset_handle_set (rs, pr);
755       if (ready_count > 0)
756         {
757           /* no blocking, more work already ready! */
758           timeout = GNUNET_TIME_UNIT_ZERO;
759         }
760       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
761       if (ret == GNUNET_SYSERR)
762         {
763           if (errno == EINTR)
764             continue;
765
766           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
767 #ifndef MINGW
768 #if USE_LSOF
769           char lsof[512];
770           snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
771           close (1);
772           dup2 (2, 1);
773           system (lsof);                  
774 #endif
775 #endif
776           abort ();
777           break;
778         }
779       if ((ret == 0) && (timeout.rel_value == 0) && (busy_wait_warning > 16))
780         {
781           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
782                       _("Looks like we're busy waiting...\n"));
783           sleep (1);            /* mitigate */
784         }
785       check_ready (rs, ws);
786       run_ready (rs, ws);
787       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
788         {
789           /* consume the signal */
790           GNUNET_DISK_file_read (pr, &c, sizeof (c));
791           /* mark all active tasks as ready due to shutdown */
792           GNUNET_SCHEDULER_shutdown ();
793         }
794       if (last_tr == tasks_run)
795         {
796           busy_wait_warning++;
797         }
798       else
799         {
800           last_tr = tasks_run;
801           busy_wait_warning = 0;
802         }
803     }
804   GNUNET_SIGNAL_handler_uninstall (shc_int);
805   GNUNET_SIGNAL_handler_uninstall (shc_term);
806 #ifndef MINGW
807   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
808   GNUNET_SIGNAL_handler_uninstall (shc_quit);
809   GNUNET_SIGNAL_handler_uninstall (shc_hup);
810 #endif
811   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
812   shutdown_pipe_handle = NULL;
813   GNUNET_NETWORK_fdset_destroy (rs);
814   GNUNET_NETWORK_fdset_destroy (ws);
815 }
816
817
818 /**
819  * Obtain the reason code for why the current task was
820  * started.  Will return the same value as 
821  * the GNUNET_SCHEDULER_TaskContext's reason field.
822  *
823  * @return reason(s) why the current task is run
824  */
825 enum GNUNET_SCHEDULER_Reason
826 GNUNET_SCHEDULER_get_reason ()
827 {
828   GNUNET_assert (active_task != NULL);
829   return active_task->reason;
830 }
831
832
833 /**
834  * Get information about the current load of this scheduler.  Use this
835  * function to determine if an elective task should be added or simply
836  * dropped (if the decision should be made based on the number of
837  * tasks ready to run).
838  *
839  * @param p priority level to look at
840  * @return number of tasks pending right now
841  */
842 unsigned int
843 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
844 {
845   struct Task *pos;
846   unsigned int ret;
847
848   GNUNET_assert (active_task != NULL);
849   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
850     return ready_count;
851   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
852     p = current_priority;
853   ret = 0;
854   pos = ready[check_priority (p)];
855   while (pos != NULL)
856     {
857       pos = pos->next;
858       ret++;
859     }
860   return ret;
861 }
862
863
864 /**
865  * Cancel the task with the specified identifier.
866  * The task must not yet have run.
867  *
868  * @param task id of the task to cancel
869  * @return original closure of the task
870  */
871 void *
872 GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_TaskIdentifier task)
873 {
874   struct Task *t;
875   struct Task *prev;
876   enum GNUNET_SCHEDULER_Priority p;
877   int to;
878   void *ret;
879
880   GNUNET_assert (active_task != NULL);
881   to = 0;
882   prev = NULL;
883   t = pending;
884   while (t != NULL)
885     {
886       if (t->id == task)
887         break;
888       prev = t;
889       t = t->next;
890     }
891   if (t == NULL)
892     {
893       prev = NULL;
894       to = 1;
895       t = pending_timeout;
896       while (t != NULL)
897         {
898           if (t->id == task)
899             break;
900           prev = t;
901           t = t->next;
902         }
903       if (pending_timeout_last == t)
904         pending_timeout_last = NULL;
905     }
906   p = 0;
907   while (t == NULL)
908     {
909       p++;
910       GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
911       prev = NULL;
912       t = ready[p];
913       while (t != NULL)
914         {
915           if (t->id == task)
916             {
917               ready_count--;
918               break;
919             }
920           prev = t;
921           t = t->next;
922         }
923     }
924   if (prev == NULL)
925     {
926       if (p == 0)
927         {
928           if (to == 0)
929             {
930               pending = t->next;
931             }
932           else
933             {
934               pending_timeout = t->next;
935             }
936         }
937       else
938         {
939           ready[p] = t->next;
940         }
941     }
942   else
943     {
944       prev->next = t->next;
945     }
946   ret = t->callback_cls;
947 #if DEBUG_TASKS
948   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
949               "Canceling task: %llu / %p\n", task, t->callback_cls);
950 #endif
951   destroy_task (t);
952   return ret;
953 }
954
955
956 /**
957  * Continue the current execution with the given function.  This is
958  * similar to the other "add" functions except that there is no delay
959  * and the reason code can be specified.
960  *
961  * @param task main function of the task
962  * @param task_cls closure for 'main'
963  * @param reason reason for task invocation
964  */
965 void
966 GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_Task task,
967                                    void *task_cls,
968                                    enum GNUNET_SCHEDULER_Reason reason)
969 {
970   struct Task *t;
971 #if EXECINFO
972   void *backtrace_array[50];
973 #endif
974
975   GNUNET_assert ( (active_task != NULL) ||
976                   (reason == GNUNET_SCHEDULER_REASON_STARTUP) );
977   t = GNUNET_malloc (sizeof (struct Task));
978 #if EXECINFO
979   t->num_backtrace_strings = backtrace(backtrace_array, 50);
980   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
981 #endif
982   t->read_fd = -1;
983   t->write_fd = -1;
984   t->callback = task;
985   t->callback_cls = task_cls;
986   t->id = ++last_id;
987 #if PROFILE_DELAYS
988   t->start_time = GNUNET_TIME_absolute_get ();
989 #endif
990   t->reason = reason;
991   t->priority = current_priority;
992 #if DEBUG_TASKS
993   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
994               "Adding continuation task: %llu / %p\n",
995               t->id, t->callback_cls);
996 #endif
997   queue_ready_task (t);
998 }
999
1000
1001
1002 /**
1003  * Schedule a new task to be run after the specified prerequisite task
1004  * has completed. It will be run with the priority of the calling
1005  * task.
1006  *
1007  * @param prerequisite_task run this task after the task with the given
1008  *        task identifier completes (and any of our other
1009  *        conditions, such as delay, read or write-readiness
1010  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
1011  *        on completion of other tasks (this will cause the task to run as
1012  *        soon as possible).
1013  * @param task main function of the task
1014  * @param task_cls closure of task
1015  * @return unique task identifier for the job
1016  *         only valid until "task" is started!
1017  */
1018 GNUNET_SCHEDULER_TaskIdentifier
1019 GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
1020                             GNUNET_SCHEDULER_Task task, void *task_cls)
1021 {
1022   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1023                                       prerequisite_task,
1024                                       GNUNET_TIME_UNIT_ZERO,
1025                                       NULL, NULL, task, task_cls);
1026 }
1027
1028
1029 /**
1030  * Schedule a new task to be run with a specified priority.
1031  *
1032  * @param prio how important is the new task?
1033  * @param task main function of the task
1034  * @param task_cls closure of task
1035  * @return unique task identifier for the job
1036  *         only valid until "task" is started!
1037  */
1038 GNUNET_SCHEDULER_TaskIdentifier
1039 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1040                                     GNUNET_SCHEDULER_Task task,
1041                                     void *task_cls)
1042 {
1043   return GNUNET_SCHEDULER_add_select (prio,
1044                                       GNUNET_SCHEDULER_NO_TASK,
1045                                       GNUNET_TIME_UNIT_ZERO,
1046                                       NULL, NULL, task, task_cls);
1047 }
1048
1049
1050
1051 /**
1052  * Schedule a new task to be run with a specified delay.  The task
1053  * will be scheduled for execution once the delay has expired. It
1054  * will be run with the priority of the calling task.
1055  *
1056  * @param delay when should this operation time out? Use 
1057  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1058  * @param task main function of the task
1059  * @param task_cls closure of task
1060  * @return unique task identifier for the job
1061  *         only valid until "task" is started!
1062  */
1063 GNUNET_SCHEDULER_TaskIdentifier
1064 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1065                               GNUNET_SCHEDULER_Task task, void *task_cls)
1066 {
1067 #if 1
1068   /* new, optimized version */
1069   struct Task *t;
1070   struct Task *pos;
1071   struct Task *prev;
1072 #if EXECINFO
1073   void *backtrace_array[MAX_TRACE_DEPTH];
1074 #endif
1075
1076   GNUNET_assert (active_task != NULL);
1077   GNUNET_assert (NULL != task);
1078   t = GNUNET_malloc (sizeof (struct Task));
1079   t->callback = task;
1080   t->callback_cls = task_cls;
1081 #if EXECINFO
1082   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1083   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1084 #endif
1085   t->read_fd = -1;
1086   t->write_fd = -1;
1087   t->id = ++last_id;
1088 #if PROFILE_DELAYS
1089   t->start_time = GNUNET_TIME_absolute_get ();
1090 #endif
1091   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1092   t->priority = current_priority;
1093   /* try tail first (optimization in case we are
1094      appending to a long list of tasks with timeouts) */
1095   prev = pending_timeout_last;
1096   if (prev != NULL) 
1097     {
1098       if (prev->timeout.abs_value > t->timeout.abs_value)
1099         prev = NULL;
1100       else
1101         pos = prev->next; /* heuristic success! */
1102     }
1103   if (prev == NULL)
1104     {
1105       /* heuristic failed, do traversal of timeout list */
1106       pos = pending_timeout;
1107     }
1108   while ( (pos != NULL) &&
1109           ( (pos->timeout.abs_value <= t->timeout.abs_value) ||
1110             (pos->reason != 0) ) )
1111     {
1112       prev = pos;
1113       pos = pos->next;
1114     }
1115   if (prev == NULL)
1116     pending_timeout = t;
1117   else
1118     prev->next = t;
1119   t->next = pos;
1120   /* hyper-optimization... */
1121   pending_timeout_last = t;
1122
1123 #if DEBUG_TASKS
1124   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1126 #endif
1127 #if EXECINFO
1128   int i;
1129
1130   for (i=0;i<t->num_backtrace_strings;i++)
1131       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1132                   "Task %llu trace %d: %s\n",
1133                   t->id,
1134                   i,
1135                   t->backtrace_strings[i]);
1136 #endif
1137   return t->id;
1138
1139 #else
1140   /* unoptimized version */
1141   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1142                                       GNUNET_SCHEDULER_NO_TASK, delay,
1143                                       NULL, NULL, task, task_cls);
1144 #endif
1145 }
1146
1147
1148
1149 /**
1150  * Schedule a new task to be run as soon as possible. The task
1151  * will be run with the priority of the calling task.
1152  *
1153  * @param task main function of the task
1154  * @param task_cls closure of task
1155  * @return unique task identifier for the job
1156  *         only valid until "task" is started!
1157  */
1158 GNUNET_SCHEDULER_TaskIdentifier
1159 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task,
1160                                                   void *task_cls)
1161 {
1162   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1163                                       GNUNET_SCHEDULER_NO_TASK,
1164                                       GNUNET_TIME_UNIT_ZERO,
1165                                       NULL, NULL, task, task_cls);
1166 }
1167
1168
1169
1170
1171 /**
1172  * Schedule a new task to be run with a specified delay or when any of
1173  * the specified file descriptor sets is ready.  The delay can be used
1174  * as a timeout on the socket(s) being ready.  The task will be
1175  * scheduled for execution once either the delay has expired or any of
1176  * the socket operations is ready.  This is the most general
1177  * function of the "add" family.  Note that the "prerequisite_task"
1178  * must be satisfied in addition to any of the other conditions.  In
1179  * other words, the task will be started when
1180  * <code>
1181  * (prerequisite-run)
1182  * && (delay-ready
1183  *     || any-rs-ready
1184  *     || any-ws-ready
1185  *     || shutdown-active )
1186  * </code>
1187  *
1188  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1189  *        which means that the task will only be run after we receive SIGTERM
1190  * @param rfd file descriptor we want to read (can be -1)
1191  * @param wfd file descriptors we want to write (can be -1)
1192  * @param task main function of the task
1193  * @param task_cls closure of task
1194  * @return unique task identifier for the job
1195  *         only valid until "task" is started!
1196  */
1197 GNUNET_SCHEDULER_TaskIdentifier
1198 add_without_sets (struct GNUNET_TIME_Relative delay,
1199                   int rfd,
1200                   int wfd,
1201                   GNUNET_SCHEDULER_Task task, void *task_cls)
1202 {
1203   struct Task *t;
1204 #if EXECINFO
1205   void *backtrace_array[MAX_TRACE_DEPTH];
1206 #endif
1207
1208   GNUNET_assert (active_task != NULL);
1209   GNUNET_assert (NULL != task);
1210   t = GNUNET_malloc (sizeof (struct Task));
1211   t->callback = task;
1212   t->callback_cls = task_cls;
1213 #if EXECINFO
1214   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1215   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1216 #endif
1217   t->read_fd = rfd;
1218   t->write_fd = wfd;
1219   t->id = ++last_id;
1220 #if PROFILE_DELAYS
1221   t->start_time = GNUNET_TIME_absolute_get ();
1222 #endif
1223   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
1224   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1225   t->priority = check_priority (current_priority);
1226   t->next = pending;
1227   pending = t;
1228   max_priority_added = GNUNET_MAX (max_priority_added,
1229                                           t->priority);
1230 #if DEBUG_TASKS
1231   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1232               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1233 #endif
1234 #if EXECINFO
1235   int i;
1236
1237   for (i=0;i<t->num_backtrace_strings;i++)
1238       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1239                   "Task %llu trace %d: %s\n",
1240                   t->id,
1241                   i,
1242                   t->backtrace_strings[i]);
1243 #endif
1244   return t->id;
1245 }
1246
1247
1248
1249 /**
1250  * Schedule a new task to be run with a specified delay or when the
1251  * specified file descriptor is ready for reading.  The delay can be
1252  * used as a timeout on the socket being ready.  The task will be
1253  * scheduled for execution once either the delay has expired or the
1254  * socket operation is ready.  It will be run with the priority of
1255  * the calling task.
1256  *
1257  * @param delay when should this operation time out? Use 
1258  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1259  * @param rfd read file-descriptor
1260  * @param task main function of the task
1261  * @param task_cls closure of task
1262  * @return unique task identifier for the job
1263  *         only valid until "task" is started!
1264  */
1265 GNUNET_SCHEDULER_TaskIdentifier
1266 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1267                                struct GNUNET_NETWORK_Handle * rfd,
1268                                GNUNET_SCHEDULER_Task task, void *task_cls)
1269 {
1270   return add_without_sets (delay,
1271                            GNUNET_NETWORK_get_fd (rfd),
1272                            -1,
1273                            task,
1274                            task_cls);
1275 }
1276
1277
1278 /**
1279  * Schedule a new task to be run with a specified delay or when the
1280  * specified file descriptor is ready for writing.  The delay can be
1281  * used as a timeout on the socket being ready.  The task will be
1282  * scheduled for execution once either the delay has expired or the
1283  * socket operation is ready.  It will be run with the priority of
1284  * the calling task.
1285  *
1286  * @param delay when should this operation time out? Use 
1287  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1288  * @param wfd write file-descriptor
1289  * @param task main function of the task
1290  * @param task_cls closure of task
1291  * @return unique task identifier for the job
1292  *         only valid until "task" is started!
1293  */
1294 GNUNET_SCHEDULER_TaskIdentifier
1295 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1296                                 struct GNUNET_NETWORK_Handle * wfd,
1297                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1298 {
1299   return add_without_sets (delay,
1300                            -1,
1301                            GNUNET_NETWORK_get_fd (wfd),
1302                            task,
1303                            task_cls);
1304 }
1305
1306
1307 /**
1308  * Schedule a new task to be run with a specified delay or when the
1309  * specified file descriptor is ready for reading.  The delay can be
1310  * used as a timeout on the socket being ready.  The task will be
1311  * scheduled for execution once either the delay has expired or the
1312  * socket operation is ready. It will be run with the priority of
1313  * the calling task.
1314  *
1315  * @param delay when should this operation time out? Use 
1316  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1317  * @param rfd read file-descriptor
1318  * @param task main function of the task
1319  * @param task_cls closure of task
1320  * @return unique task identifier for the job
1321  *         only valid until "task" is started!
1322  */
1323 GNUNET_SCHEDULER_TaskIdentifier
1324 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1325                                 const struct GNUNET_DISK_FileHandle * rfd,
1326                                 GNUNET_SCHEDULER_Task task, void *task_cls)
1327 {
1328 #if MINGW
1329   struct GNUNET_NETWORK_FDSet *rs;
1330   GNUNET_SCHEDULER_TaskIdentifier ret;
1331
1332   GNUNET_assert (rfd != NULL);
1333   rs = GNUNET_NETWORK_fdset_create ();
1334   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
1335   ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1336                                      GNUNET_SCHEDULER_NO_TASK, delay,
1337                                      rs, NULL, task, task_cls);
1338   GNUNET_NETWORK_fdset_destroy (rs);
1339   return ret;
1340 #else
1341   int fd;
1342
1343   GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
1344   return add_without_sets (delay,
1345                            fd,
1346                            -1,
1347                            task,
1348                            task_cls);
1349
1350 #endif
1351 }
1352
1353
1354 /**
1355  * Schedule a new task to be run with a specified delay or when the
1356  * specified file descriptor is ready for writing.  The delay can be
1357  * used as a timeout on the socket being ready.  The task will be
1358  * scheduled for execution once either the delay has expired or the
1359  * socket operation is ready. It will be run with the priority of
1360  * the calling task.
1361  *
1362  * @param delay when should this operation time out? Use 
1363  *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
1364  * @param wfd write file-descriptor
1365  * @param task main function of the task
1366  * @param task_cls closure of task
1367  * @return unique task identifier for the job
1368  *         only valid until "task" is started!
1369  */
1370 GNUNET_SCHEDULER_TaskIdentifier
1371 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1372                                  const struct GNUNET_DISK_FileHandle * wfd,
1373                                  GNUNET_SCHEDULER_Task task, void *task_cls)
1374 {
1375 #if MINGW
1376   struct GNUNET_NETWORK_FDSet *ws;
1377   GNUNET_SCHEDULER_TaskIdentifier ret;
1378
1379   GNUNET_assert (wfd != NULL);
1380   ws = GNUNET_NETWORK_fdset_create ();
1381   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
1382   ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
1383                                      GNUNET_SCHEDULER_NO_TASK,
1384                                      delay, NULL, ws, task, task_cls);
1385   GNUNET_NETWORK_fdset_destroy (ws);
1386   return ret;
1387 #else
1388   int fd;
1389
1390   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
1391   return add_without_sets (delay,
1392                            -1,
1393                            fd,
1394                            task,
1395                            task_cls);
1396
1397 #endif
1398 }
1399
1400
1401
1402 /**
1403  * Schedule a new task to be run with a specified delay or when any of
1404  * the specified file descriptor sets is ready.  The delay can be used
1405  * as a timeout on the socket(s) being ready.  The task will be
1406  * scheduled for execution once either the delay has expired or any of
1407  * the socket operations is ready.  This is the most general
1408  * function of the "add" family.  Note that the "prerequisite_task"
1409  * must be satisfied in addition to any of the other conditions.  In
1410  * other words, the task will be started when
1411  * <code>
1412  * (prerequisite-run)
1413  * && (delay-ready
1414  *     || any-rs-ready
1415  *     || any-ws-ready
1416  *     || (shutdown-active && run-on-shutdown) )
1417  * </code>
1418  *
1419  * @param prio how important is this task?
1420  * @param prerequisite_task run this task after the task with the given
1421  *        task identifier completes (and any of our other
1422  *        conditions, such as delay, read or write-readiness
1423  *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
1424  *        on completion of other tasks.
1425  * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
1426  *        which means that the task will only be run after we receive SIGTERM
1427  * @param rs set of file descriptors we want to read (can be NULL)
1428  * @param ws set of file descriptors we want to write (can be NULL)
1429  * @param task main function of the task
1430  * @param task_cls closure of task
1431  * @return unique task identifier for the job
1432  *         only valid until "task" is started!
1433  */
1434 GNUNET_SCHEDULER_TaskIdentifier
1435 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1436                              GNUNET_SCHEDULER_TaskIdentifier
1437                              prerequisite_task,
1438                              struct GNUNET_TIME_Relative delay,
1439                              const struct GNUNET_NETWORK_FDSet * rs,
1440                              const struct GNUNET_NETWORK_FDSet * ws,
1441                              GNUNET_SCHEDULER_Task task, void *task_cls)
1442 {
1443   struct Task *t;
1444 #if EXECINFO
1445   void *backtrace_array[MAX_TRACE_DEPTH];
1446 #endif
1447
1448   GNUNET_assert (active_task != NULL);
1449   GNUNET_assert (NULL != task);
1450   t = GNUNET_malloc (sizeof (struct Task));
1451   t->callback = task;
1452   t->callback_cls = task_cls;
1453 #if EXECINFO
1454   t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
1455   t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
1456 #endif
1457   t->read_fd = -1;
1458   t->write_fd = -1;
1459   if (rs != NULL)
1460     {
1461       t->read_set = GNUNET_NETWORK_fdset_create ();
1462       GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1463     }
1464   if (ws != NULL)
1465     {
1466       t->write_set = GNUNET_NETWORK_fdset_create ();
1467       GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1468     }
1469   t->id = ++last_id;
1470 #if PROFILE_DELAYS
1471   t->start_time = GNUNET_TIME_absolute_get ();
1472 #endif
1473   t->prereq_id = prerequisite_task;
1474   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1475   t->priority =
1476     check_priority ((prio ==
1477                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority
1478                     : prio);
1479   t->next = pending;
1480   pending = t;
1481   max_priority_added = GNUNET_MAX (max_priority_added,
1482                                           t->priority);
1483 #if DEBUG_TASKS
1484   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1485               "Adding task: %llu / %p\n", t->id, t->callback_cls);
1486 #endif
1487 #if EXECINFO
1488   int i;
1489
1490   for (i=0;i<t->num_backtrace_strings;i++)
1491       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1492                   "Task %llu trace %d: %s\n",
1493                   t->id,
1494                   i,
1495                   t->backtrace_strings[i]);
1496 #endif
1497   return t->id;
1498 }
1499
1500 /* end of scheduler.c */