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