separate the select driver's fd sets from the driver-internal fdsets
[oweals/gnunet.git] / src / util / scheduler.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2009-2017 GNUnet e.V.
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file util/scheduler.c
22  * @brief schedule computations using continuation passing style
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "disk.h"
28
29 #define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
30
31 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
32
33
34 #if HAVE_EXECINFO_H
35 #include "execinfo.h"
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_NO
42
43 /**
44  * Obtain trace information for all scheduler calls that schedule tasks.
45  */
46 #define EXECINFO GNUNET_NO
47
48 /**
49  * Check each file descriptor before adding
50  */
51 #define DEBUG_FDS GNUNET_NO
52
53 /**
54  * Depth of the traces collected via EXECINFO.
55  */
56 #define MAX_TRACE_DEPTH 50
57 #endif
58
59 /**
60  * Should we figure out which tasks are delayed for a while
61  * before they are run? (Consider using in combination with EXECINFO).
62  */
63 #define PROFILE_DELAYS GNUNET_NO
64
65 /**
66  * Task that were in the queue for longer than this are reported if
67  * PROFILE_DELAYS is active.
68  */
69 #define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
70
71
72 /**
73  * Argument to be passed from the driver to
74  * #GNUNET_SCHEDULER_run_from_driver().  Contains the
75  * scheduler's internal state.
76  */
77 struct GNUNET_SCHEDULER_Handle
78 {
79   /**
80    * Passed here to avoid constantly allocating/deallocating
81    * this element, but generally we want to get rid of this.
82    * @deprecated
83    */
84   struct GNUNET_NETWORK_FDSet *rs;
85
86   /**
87    * Passed here to avoid constantly allocating/deallocating
88    * this element, but generally we want to get rid of this.
89    * @deprecated
90    */
91   struct GNUNET_NETWORK_FDSet *ws;
92 };
93
94
95 /**
96  * Entry in list of pending tasks.
97  */
98 struct GNUNET_SCHEDULER_Task
99 {
100   /**
101    * This is a linked list.
102    */
103   struct GNUNET_SCHEDULER_Task *next;
104
105   /**
106    * This is a linked list.
107    */
108   struct GNUNET_SCHEDULER_Task *prev;
109
110   /**
111    * Function to run when ready.
112    */
113   GNUNET_SCHEDULER_TaskCallback callback;
114
115   /**
116    * Closure for the @e callback.
117    */
118   void *callback_cls;
119
120   /**
121    * Set of file descriptors this task is waiting
122    * for for reading.  Once ready, this is updated
123    * to reflect the set of file descriptors ready
124    * for operation.
125    */
126   struct GNUNET_NETWORK_FDSet *read_set;
127
128   /**
129    * Set of file descriptors this task is waiting for for writing.
130    * Once ready, this is updated to reflect the set of file
131    * descriptors ready for operation.
132    */
133   struct GNUNET_NETWORK_FDSet *write_set;
134
135   /**
136    * Information about which FDs are ready for this task (and why).
137    */
138   const struct GNUNET_SCHEDULER_FdInfo *fds;
139
140   /**
141    * Storage location used for @e fds if we want to avoid
142    * a separate malloc() call in the common case that this
143    * task is only about a single FD.
144    */
145   struct GNUNET_SCHEDULER_FdInfo fdx;
146
147   /**
148    * Absolute timeout value for the task, or
149    * #GNUNET_TIME_UNIT_FOREVER_ABS for "no timeout".
150    */
151   struct GNUNET_TIME_Absolute timeout;
152
153 #if PROFILE_DELAYS
154   /**
155    * When was the task scheduled?
156    */
157   struct GNUNET_TIME_Absolute start_time;
158 #endif
159
160   /**
161    * Size of the @e fds array.
162    */
163   unsigned int fds_len;
164
165   /**
166    * Why is the task ready?  Set after task is added to ready queue.
167    * Initially set to zero.  All reasons that have already been
168    * satisfied (i.e.  read or write ready) will be set over time.
169    */
170   enum GNUNET_SCHEDULER_Reason reason;
171
172   /**
173    * Task priority.
174    */
175   enum GNUNET_SCHEDULER_Priority priority;
176
177   /**
178    * Set if we only wait for reading from a single FD, otherwise -1.
179    */
180   int read_fd;
181
182   /**
183    * Set if we only wait for writing to a single FD, otherwise -1.
184    */
185   int write_fd;
186
187   /**
188    * Should the existence of this task in the queue be counted as
189    * reason to not shutdown the scheduler?
190    */
191   int lifeness;
192
193   /**
194    * Is this task run on shutdown?
195    */
196   int on_shutdown;
197
198   /**
199    * Is this task in the ready list?
200    */
201   int in_ready_list;
202
203 #if EXECINFO
204   /**
205    * Array of strings which make up a backtrace from the point when this
206    * task was scheduled (essentially, who scheduled the task?)
207    */
208   char **backtrace_strings;
209
210   /**
211    * Size of the backtrace_strings array
212    */
213   int num_backtrace_strings;
214 #endif
215
216 };
217
218
219 struct Scheduled
220 {
221   struct Scheduled *prev;
222
223   struct Scheduled *next;
224
225   struct GNUNET_SCHEDULER_Task *task;
226
227   struct GNUNET_SCHEDULER_FdInfo *fdi;
228 };
229
230
231 /**
232  * Driver context used by GNUNET_SCHEDULER_run
233  */
234 struct DriverContext
235 {
236   struct Scheduled *scheduled_in_head;
237
238   struct Scheduled *scheduled_in_tail;
239
240   struct Scheduled *scheduled_out_head;
241
242   struct Scheduled *scheduled_out_tail;
243
244   struct GNUNET_TIME_Relative timeout;
245 };
246
247
248 /**
249  * The driver used for the event loop. Will be handed over to
250  * the scheduler in #GNUNET_SCHEDULER_run_from_driver(), peristed
251  * there in this variable for later use in functions like
252  * #GNUNET_SCHEDULER_add_select(), #add_without_sets() and
253  * #GNUNET_SCHEDULER_cancel().
254  */
255 static const struct GNUNET_SCHEDULER_Driver *scheduler_driver;
256
257 /**
258  * Head of list of tasks waiting for an event.
259  */
260 static struct GNUNET_SCHEDULER_Task *pending_head;
261
262 /**
263  * Tail of list of tasks waiting for an event.
264  */
265 static struct GNUNET_SCHEDULER_Task *pending_tail;
266
267 /**
268  * Head of list of tasks waiting for shutdown.
269  */
270 static struct GNUNET_SCHEDULER_Task *shutdown_head;
271
272 /**
273  * Tail of list of tasks waiting for shutdown.
274  */
275 static struct GNUNET_SCHEDULER_Task *shutdown_tail;
276
277 /**
278  * List of tasks waiting ONLY for a timeout event.
279  * Sorted by timeout (earliest first).  Used so that
280  * we do not traverse the list of these tasks when
281  * building select sets (we just look at the head
282  * to determine the respective timeout ONCE).
283  */
284 static struct GNUNET_SCHEDULER_Task *pending_timeout_head;
285
286 /**
287  * List of tasks waiting ONLY for a timeout event.
288  * Sorted by timeout (earliest first).  Used so that
289  * we do not traverse the list of these tasks when
290  * building select sets (we just look at the head
291  * to determine the respective timeout ONCE).
292  */
293 static struct GNUNET_SCHEDULER_Task *pending_timeout_tail;
294
295 /**
296  * Last inserted task waiting ONLY for a timeout event.
297  * Used to (heuristically) speed up insertion.
298  */
299 static struct GNUNET_SCHEDULER_Task *pending_timeout_last;
300
301 /**
302  * ID of the task that is running right now.
303  */
304 static struct GNUNET_SCHEDULER_Task *active_task;
305
306 /**
307  * Head of list of tasks ready to run right now, grouped by importance.
308  */
309 static struct GNUNET_SCHEDULER_Task *ready_head[GNUNET_SCHEDULER_PRIORITY_COUNT];
310
311 /**
312  * Tail of list of tasks ready to run right now, grouped by importance.
313  */
314 static struct GNUNET_SCHEDULER_Task *ready_tail[GNUNET_SCHEDULER_PRIORITY_COUNT];
315
316 /**
317  * Number of tasks on the ready list.
318  */
319 static unsigned int ready_count;
320
321 /**
322  * How many tasks have we run so far?
323  */
324 static unsigned long long tasks_run;
325
326 /**
327  * Priority of the task running right now.  Only
328  * valid while a task is running.
329  */
330 static enum GNUNET_SCHEDULER_Priority current_priority;
331
332 /**
333  * Priority of the highest task added in the current select
334  * iteration.
335  */
336 static enum GNUNET_SCHEDULER_Priority max_priority_added;
337
338 /**
339  * Value of the 'lifeness' flag for the current task.
340  */
341 static int current_lifeness;
342
343 /**
344  * Function to use as a select() in the scheduler.
345  * If NULL, we use GNUNET_NETWORK_socket_select().
346  */
347 static GNUNET_SCHEDULER_select scheduler_select;
348
349 /**
350  * Task context of the current task.
351  */
352 static struct GNUNET_SCHEDULER_TaskContext tc;
353
354 /**
355  * Closure for #scheduler_select.
356  */
357 static void *scheduler_select_cls;
358
359 /**
360  * Scheduler handle used for the driver functions
361  */
362 static struct GNUNET_SCHEDULER_Handle sh;
363
364
365 /**
366  * Sets the select function to use in the scheduler (scheduler_select).
367  *
368  * @param new_select new select function to use
369  * @param new_select_cls closure for @a new_select
370  * @return previously used select function, NULL for default
371  */
372 void
373 GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
374                              void *new_select_cls)
375 {
376   scheduler_select = new_select;
377   scheduler_select_cls = new_select_cls;
378 }
379
380
381 /**
382  * Check that the given priority is legal (and return it).
383  *
384  * @param p priority value to check
385  * @return p on success, 0 on error
386  */
387 static enum GNUNET_SCHEDULER_Priority
388 check_priority (enum GNUNET_SCHEDULER_Priority p)
389 {
390   if ((p >= 0) && (p < GNUNET_SCHEDULER_PRIORITY_COUNT))
391     return p;
392   GNUNET_assert (0);
393   return 0;                     /* make compiler happy */
394 }
395
396
397 /**
398  * chooses the nearest timeout from all pending tasks, to be used
399  * to tell the driver the next wakeup time (using its set_wakeup
400  * callback)
401  */
402 struct GNUNET_TIME_Absolute
403 get_timeout ()
404 {
405   struct GNUNET_SCHEDULER_Task *pos;
406   struct GNUNET_TIME_Absolute now;
407   struct GNUNET_TIME_Absolute timeout;
408
409   pos = pending_timeout_head;
410   now = GNUNET_TIME_absolute_get ();
411   timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
412   if (NULL != pos)
413   {
414     if (0 != pos->reason)
415     {
416       timeout = now;
417     }
418     else
419     {
420       timeout = pos->timeout;
421     }
422   }
423   for (pos = pending_head; NULL != pos; pos = pos->next)
424   {
425     if (0 != pos->reason)
426     {
427       timeout = now;
428     }
429     else if ((pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
430              (timeout.abs_value_us > pos->timeout.abs_value_us))
431     {
432       timeout = pos->timeout;
433     }
434   }
435   return timeout;
436 }
437
438
439 /**
440  * Put a task that is ready for execution into the ready queue.
441  *
442  * @param task task ready for execution
443  */
444 static void
445 queue_ready_task (struct GNUNET_SCHEDULER_Task *task)
446 {
447   enum GNUNET_SCHEDULER_Priority p = check_priority (task->priority);
448
449   GNUNET_CONTAINER_DLL_insert (ready_head[p],
450                                ready_tail[p],
451                                task);
452   task->in_ready_list = GNUNET_YES;
453   ready_count++;
454 }
455
456
457 /**
458  * Request the shutdown of a scheduler.  Marks all tasks
459  * awaiting shutdown as ready. Note that tasks
460  * scheduled with #GNUNET_SCHEDULER_add_shutdown() AFTER this call
461  * will be delayed until the next shutdown signal.
462  */
463 void
464 GNUNET_SCHEDULER_shutdown ()
465 {
466   struct GNUNET_SCHEDULER_Task *pos;
467
468   while (NULL != (pos = shutdown_head))
469   {
470     GNUNET_CONTAINER_DLL_remove (shutdown_head,
471                                  shutdown_tail,
472                                  pos);
473     pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
474     queue_ready_task (pos);
475   }
476 }
477
478
479 /**
480  * Destroy a task (release associated resources)
481  *
482  * @param t task to destroy
483  */
484 static void
485 destroy_task (struct GNUNET_SCHEDULER_Task *t)
486 {
487   if (NULL != t->read_set)
488     GNUNET_NETWORK_fdset_destroy (t->read_set);
489   if (NULL != t->write_set)
490     GNUNET_NETWORK_fdset_destroy (t->write_set);
491 #if EXECINFO
492   GNUNET_free (t->backtrace_strings);
493 #endif
494   GNUNET_free (t);
495 }
496
497
498 /**
499  * Output stack trace of task @a t.
500  *
501  * @param t task to dump stack trace of
502  */
503 static void
504 dump_backtrace (struct GNUNET_SCHEDULER_Task *t)
505 {
506 #if EXECINFO
507   unsigned int i;
508
509   for (i = 0; i < t->num_backtrace_strings; i++)
510     LOG (GNUNET_ERROR_TYPE_WARNING,
511    "Task %p trace %u: %s\n",
512    t,
513    i,
514    t->backtrace_strings[i]);
515 #endif
516 }
517
518
519 /**
520  * Pipe used to communicate shutdown via signal.
521  */
522 static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
523
524 /**
525  * Process ID of this process at the time we installed the various
526  * signal handlers.
527  */
528 static pid_t my_pid;
529
530 /**
531  * Signal handler called for SIGPIPE.
532  */
533 #ifndef MINGW
534 static void
535 sighandler_pipe ()
536 {
537   return;
538 }
539 #endif
540
541
542 /**
543  * Wait for a short time.
544  * Sleeps for @a ms ms (as that should be long enough for virtually all
545  * modern systems to context switch and allow another process to do
546  * some 'real' work).
547  *
548  * @param ms how many ms to wait
549  */
550 static void
551 short_wait (unsigned int ms)
552 {
553   struct GNUNET_TIME_Relative timeout;
554
555   timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
556   (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
557 }
558
559
560 /**
561  * Signal handler called for signals that should cause us to shutdown.
562  */
563 static void
564 sighandler_shutdown ()
565 {
566   static char c;
567   int old_errno = errno;        /* backup errno */
568
569   if (getpid () != my_pid)
570     exit (1);                   /* we have fork'ed since the signal handler was created,
571                                  * ignore the signal, see https://gnunet.org/vfork discussion */
572   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
573                           (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
574                           &c, sizeof (c));
575   errno = old_errno;
576 }
577
578
579 /**
580  * Check if the system is still alive. Trigger shutdown if we
581  * have tasks, but none of them give us lifeness.
582  *
583  * @return #GNUNET_OK to continue the main loop,
584  *         #GNUNET_NO to exit
585  */
586 //static int
587 //check_lifeness ()
588 //{
589 //  struct GNUNET_SCHEDULER_Task *t;
590 //
591 //  if (ready_count > 0)
592 //    return GNUNET_OK;
593 //  for (t = pending_head; NULL != t; t = t->next)
594 //    if (t->lifeness == GNUNET_YES)
595 //      return GNUNET_OK;
596 //  for (t = shutdown_head; NULL != t; t = t->next)
597 //    if (t->lifeness == GNUNET_YES)
598 //      return GNUNET_OK;
599 //  for (t = pending_timeout_head; NULL != t; t = t->next)
600 //    if (t->lifeness == GNUNET_YES)
601 //      return GNUNET_OK;
602 //  if (NULL != shutdown_head)
603 //  {
604 //    GNUNET_SCHEDULER_shutdown ();
605 //    return GNUNET_OK;
606 //  }
607 //  return GNUNET_NO;
608 //}
609
610
611 /**
612  * Initialize and run scheduler.  This function will return when all
613  * tasks have completed.  On systems with signals, receiving a SIGTERM
614  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown()
615  * to be run after the active task is complete.  As a result, SIGTERM
616  * causes all active tasks to be scheduled with reason
617  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
618  * afterwards will execute normally!). Note that any particular signal
619  * will only shut down one scheduler; applications should always only
620  * create a single scheduler.
621  *
622  * @param task task to run immediately
623  * @param task_cls closure of @a task
624  */
625 void
626 GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_TaskCallback task,
627                       void *task_cls)
628 {
629   struct GNUNET_SCHEDULER_Driver *driver;
630   struct DriverContext context = {.scheduled_in_head = NULL,
631                                   .scheduled_in_tail = NULL,
632                                   .scheduled_out_head = NULL,
633                                   .scheduled_out_tail = NULL,
634                                   .timeout = GNUNET_TIME_UNIT_FOREVER_REL};
635   
636   driver = GNUNET_SCHEDULER_driver_select ();
637   driver->cls = &context;
638
639   GNUNET_SCHEDULER_run_with_driver (driver, task, task_cls);
640   
641   GNUNET_free (driver);
642 }
643
644
645 /**
646  * Obtain the task context, giving the reason why the current task was
647  * started.
648  *
649  * @return current tasks' scheduler context
650  */
651 const struct GNUNET_SCHEDULER_TaskContext *
652 GNUNET_SCHEDULER_get_task_context ()
653 {
654   GNUNET_assert (NULL != active_task);
655   return &tc;
656 }
657
658
659 /**
660  * Get information about the current load of this scheduler.  Use this
661  * function to determine if an elective task should be added or simply
662  * dropped (if the decision should be made based on the number of
663  * tasks ready to run).
664  *
665  * @param p priority level to look at
666  * @return number of tasks pending right now
667  */
668 unsigned int
669 GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
670 {
671   struct GNUNET_SCHEDULER_Task *pos;
672   unsigned int ret;
673
674   GNUNET_assert (NULL != active_task);
675   if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
676     return ready_count;
677   if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
678     p = current_priority;
679   ret = 0;
680   for (pos = ready_head[check_priority (p)]; NULL != pos; pos = pos->next)
681     ret++;
682   return ret;
683 }
684
685
686 void
687 initFdInfo(struct GNUNET_SCHEDULER_Task *t,
688            const struct GNUNET_NETWORK_Handle *read_nh,
689            const struct GNUNET_NETWORK_Handle *write_nh,
690            const struct GNUNET_DISK_FileHandle *read_fh,
691            const struct GNUNET_DISK_FileHandle *write_fh)
692 {
693   // either only network handles or only file handles are allowed
694   GNUNET_assert (!((NULL != read_nh || NULL != write_nh) && (NULL != read_fh || NULL != write_fh)));
695
696   if (NULL != read_nh && NULL != write_nh)
697   {
698     t->fds_len = 2;
699     t->fds = GNUNET_new_array (2, struct GNUNET_SCHEDULER_FdInfo);
700     const struct GNUNET_SCHEDULER_FdInfo read_fdi = { .fd = read_nh, .et = GNUNET_SCHEDULER_ET_IN, .sock = GNUNET_NETWORK_get_fd (read_nh)};
701     const struct GNUNET_SCHEDULER_FdInfo write_fdi = { .fd = write_nh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = GNUNET_NETWORK_get_fd (write_nh)};
702
703     const struct GNUNET_SCHEDULER_FdInfo array[2] = {read_fdi, write_fdi};
704     t->fds = array;
705   }
706   else if (NULL != read_fh && NULL != write_fh)
707   {
708     t->fds_len = 2;
709     t->fds = GNUNET_new_array (2, struct GNUNET_SCHEDULER_FdInfo);
710     const struct GNUNET_SCHEDULER_FdInfo read_fdi = { .fh = read_fh, .et = GNUNET_SCHEDULER_ET_IN};
711     const struct GNUNET_SCHEDULER_FdInfo write_fdi = { .fh = write_fh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = GNUNET_NETWORK_get_fd (write_nh)};
712     const struct GNUNET_SCHEDULER_FdInfo array[2] = {read_fdi, write_fdi};
713     t->fds = array;
714   }
715   else if (NULL != read_nh)
716   {
717     struct GNUNET_SCHEDULER_FdInfo read_fdi = { .fd = read_nh, .et = GNUNET_SCHEDULER_ET_IN, .sock = GNUNET_NETWORK_get_fd (read_nh)};
718     t->fdx = read_fdi;
719     t->fds = &t->fdx;
720   }
721   else if (NULL != write_nh)
722   {
723     struct GNUNET_SCHEDULER_FdInfo write_fdi = { .fd = write_nh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = GNUNET_NETWORK_get_fd (write_nh)};
724     t->fdx = write_fdi;
725     t->fds = &t->fdx;
726   }
727   else if (NULL != read_fh)
728   {
729     struct GNUNET_SCHEDULER_FdInfo read_fdi = { .fh = read_fh, .et = GNUNET_SCHEDULER_ET_IN, .sock = read_fh->fd};
730     t->fdx = read_fdi;
731     t->fds = &t->fdx;
732   }
733   else if (NULL != write_fh)
734   {
735     struct GNUNET_SCHEDULER_FdInfo write_fdi = { .fh = write_fh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = write_fh->fd};
736     t->fdx = write_fdi;
737     t->fds = &t->fdx;
738   }
739 }
740
741
742 int scheduler_multi_function_call(struct GNUNET_SCHEDULER_Task *t, int (*driver_func)())
743 {
744   if (t->fds_len > 1)
745   {
746     int success = GNUNET_YES;
747     for (int i = 0; i < t->fds_len;i++)
748     {
749       success = driver_func (scheduler_driver->cls, t , t->fds+i) && success;
750     }
751     return success;
752   }
753   else
754   {
755     return driver_func (scheduler_driver->cls, t , t->fds);
756   }
757 }
758
759
760 void
761 shutdown_task (void *cls)
762 {
763   char c;
764   const struct GNUNET_DISK_FileHandle *pr;
765   
766   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
767                                 GNUNET_DISK_PIPE_END_READ);
768   GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
769   /* consume the signal */
770   GNUNET_DISK_file_read (pr, &c, sizeof (c));
771   /* mark all active tasks as ready due to shutdown */
772   GNUNET_SCHEDULER_shutdown ();
773 }
774
775
776 /**
777  * Cancel the task with the specified identifier.
778  * The task must not yet have run.
779  *
780  * @param task id of the task to cancel
781  * @return original closure of the task
782  */
783 void *
784 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
785 {
786   enum GNUNET_SCHEDULER_Priority p;
787   void *ret;
788
789   GNUNET_assert ( (NULL != active_task) ||
790       (GNUNET_NO == task->lifeness) );
791   if (! task->in_ready_list)
792   {
793     if ( (-1 == task->read_fd) &&
794          (-1 == task->write_fd) &&
795          (NULL == task->read_set) &&
796          (NULL == task->write_set) )
797     {
798       if (GNUNET_YES == task->on_shutdown)
799         GNUNET_CONTAINER_DLL_remove (shutdown_head,
800                                      shutdown_tail,
801                                      task);
802       else
803       {
804         GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
805                                      pending_timeout_tail,
806                                      task);
807         if (pending_timeout_last == task)
808           pending_timeout_last = NULL;
809         else
810           scheduler_driver->set_wakeup (scheduler_driver->cls,
811                                         get_timeout ());
812       }
813       if (task == pending_timeout_last)
814         pending_timeout_last = NULL;
815     }
816     else
817     {
818       GNUNET_CONTAINER_DLL_remove (pending_head,
819                                    pending_tail,
820                                    task);
821       scheduler_multi_function_call(task, scheduler_driver->del);
822     }
823   }
824   else
825   {
826     p = check_priority (task->priority);
827     GNUNET_CONTAINER_DLL_remove (ready_head[p],
828                                  ready_tail[p],
829                                  task);
830     ready_count--;
831   }
832   LOG (GNUNET_ERROR_TYPE_WARNING,
833        "Canceling task %p\n",
834        task);
835   ret = task->callback_cls;
836   destroy_task (task);
837   return ret;
838 }
839
840
841 /**
842  * Initialize backtrace data for task @a t
843  *
844  * @param t task to initialize
845  */
846 static void
847 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
848 {
849 #if EXECINFO
850   void *backtrace_array[MAX_TRACE_DEPTH];
851
852   t->num_backtrace_strings
853     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
854   t->backtrace_strings =
855       backtrace_symbols (backtrace_array,
856        t->num_backtrace_strings);
857   dump_backtrace (t);
858 #endif
859 }
860
861
862 /**
863  * Continue the current execution with the given function.  This is
864  * similar to the other "add" functions except that there is no delay
865  * and the reason code can be specified.
866  *
867  * @param task main function of the task
868  * @param task_cls closure for @a task
869  * @param reason reason for task invocation
870  * @param priority priority to use for the task
871  */
872 void
873 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
874                                                void *task_cls,
875                                                enum GNUNET_SCHEDULER_Reason reason,
876                                                enum GNUNET_SCHEDULER_Priority priority)
877 {
878   struct GNUNET_SCHEDULER_Task *t;
879
880   GNUNET_assert (NULL != task);
881   GNUNET_assert ((NULL != active_task) ||
882                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
883   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
884   t->read_fd = -1;
885   t->write_fd = -1;
886   t->callback = task;
887   t->callback_cls = task_cls;
888 #if PROFILE_DELAYS
889   t->start_time = GNUNET_TIME_absolute_get ();
890 #endif
891   t->reason = reason;
892   t->priority = priority;
893   t->lifeness = current_lifeness;
894   LOG (GNUNET_ERROR_TYPE_WARNING,
895        "Adding continuation task %p\n",
896        t);
897   init_backtrace (t);
898   queue_ready_task (t);
899 }
900
901
902 /**
903  * Schedule a new task to be run at the specified time.  The task
904  * will be scheduled for execution at time @a at.
905  *
906  * @param at time when the operation should run
907  * @param priority priority to use for the task
908  * @param task main function of the task
909  * @param task_cls closure of @a task
910  * @return unique task identifier for the job
911  *         only valid until @a task is started!
912  */
913 struct GNUNET_SCHEDULER_Task *
914 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
915                                        enum GNUNET_SCHEDULER_Priority priority,
916                                        GNUNET_SCHEDULER_TaskCallback task,
917                                        void *task_cls)
918 {
919   struct GNUNET_SCHEDULER_Task *t;
920   struct GNUNET_SCHEDULER_Task *pos;
921   struct GNUNET_SCHEDULER_Task *prev;
922
923   GNUNET_assert (NULL != active_task);
924   GNUNET_assert (NULL != task);
925   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
926   t->callback = task;
927   t->callback_cls = task_cls;
928   t->read_fd = -1;
929   t->write_fd = -1;
930 #if PROFILE_DELAYS
931   t->start_time = GNUNET_TIME_absolute_get ();
932 #endif
933   t->timeout = at;
934   t->priority = priority;
935   t->lifeness = current_lifeness;
936   /* try tail first (optimization in case we are
937    * appending to a long list of tasks with timeouts) */
938   if ( (NULL == pending_timeout_head) ||
939        (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
940   {
941     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
942                                  pending_timeout_tail,
943                                  t);
944     scheduler_driver->set_wakeup (scheduler_driver->cls,
945                                   at);
946   }
947   else
948   {
949     /* first move from heuristic start backwards to before start time */
950     prev = pending_timeout_last;
951     while ( (NULL != prev) &&
952             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
953       prev = prev->prev;
954     /* now, move from heuristic start (or head of list) forward to insertion point */
955     if (NULL == prev)
956       pos = pending_timeout_head;
957     else
958       pos = prev->next;
959     while ( (NULL != pos) &&
960             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
961               (0 != pos->reason) ) )
962     {
963       prev = pos;
964       pos = pos->next;
965     }
966     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
967                                        pending_timeout_tail,
968                                        prev,
969                                        t);
970     scheduler_driver->set_wakeup (scheduler_driver->cls,
971                                   get_timeout());
972   }
973   /* finally, update heuristic insertion point to last insertion... */
974   pending_timeout_last = t;
975
976   LOG (GNUNET_ERROR_TYPE_WARNING,
977        "Adding task: %p\n",
978        t);
979   init_backtrace (t);
980   return t;
981 }
982
983
984 /**
985  * Schedule a new task to be run with a specified delay.  The task
986  * will be scheduled for execution once the delay has expired.
987  *
988  * @param delay when should this operation time out?
989  * @param priority priority to use for the task
990  * @param task main function of the task
991  * @param task_cls closure of @a task
992  * @return unique task identifier for the job
993  *         only valid until @a task is started!
994  */
995 struct GNUNET_SCHEDULER_Task *
996 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
997               enum GNUNET_SCHEDULER_Priority priority,
998               GNUNET_SCHEDULER_TaskCallback task,
999                                             void *task_cls)
1000 {
1001   return GNUNET_SCHEDULER_add_at_with_priority (GNUNET_TIME_relative_to_absolute (delay),
1002                                                 priority,
1003                                                 task,
1004                                                 task_cls);
1005 }
1006
1007
1008 /**
1009  * Schedule a new task to be run with a specified priority.
1010  *
1011  * @param prio how important is the new task?
1012  * @param task main function of the task
1013  * @param task_cls closure of @a task
1014  * @return unique task identifier for the job
1015  *         only valid until @a task is started!
1016  */
1017 struct GNUNET_SCHEDULER_Task *
1018 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1019                                     GNUNET_SCHEDULER_TaskCallback task,
1020                                     void *task_cls)
1021 {
1022   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1023                                                      prio,
1024                                                      task,
1025                                                      task_cls);
1026 }
1027
1028
1029 /**
1030  * Schedule a new task to be run at the specified time.  The task
1031  * will be scheduled for execution once specified time has been
1032  * reached. It will be run with the DEFAULT priority.
1033  *
1034  * @param at time at which this operation should run
1035  * @param task main function of the task
1036  * @param task_cls closure of @a task
1037  * @return unique task identifier for the job
1038  *         only valid until @a task is started!
1039  */
1040 struct GNUNET_SCHEDULER_Task *
1041 GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1042                          GNUNET_SCHEDULER_TaskCallback task,
1043                          void *task_cls)
1044 {
1045   return GNUNET_SCHEDULER_add_at_with_priority (at,
1046                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1047                                                 task,
1048                                                 task_cls);
1049 }
1050
1051
1052 /**
1053  * Schedule a new task to be run with a specified delay.  The task
1054  * will be scheduled for execution once the delay has expired. It
1055  * will be run with the DEFAULT priority.
1056  *
1057  * @param delay when should this operation time out?
1058  * @param task main function of the task
1059  * @param task_cls closure of @a task
1060  * @return unique task identifier for the job
1061  *         only valid until @a task is started!
1062  */
1063 struct GNUNET_SCHEDULER_Task *
1064 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1065                               GNUNET_SCHEDULER_TaskCallback task,
1066             void *task_cls)
1067 {
1068   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1069                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1070                  task,
1071                                                      task_cls);
1072 }
1073
1074
1075 /**
1076  * Schedule a new task to be run as soon as possible.  Note that this
1077  * does not guarantee that this will be the next task that is being
1078  * run, as other tasks with higher priority (or that are already ready
1079  * to run) might get to run first.  Just as with delays, clients must
1080  * not rely on any particular order of execution between tasks
1081  * scheduled concurrently.
1082  *
1083  * The task will be run with the DEFAULT priority.
1084  *
1085  * @param task main function of the task
1086  * @param task_cls closure of @a task
1087  * @return unique task identifier for the job
1088  *         only valid until @a task is started!
1089  */
1090 struct GNUNET_SCHEDULER_Task *
1091 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1092                           void *task_cls)
1093 {
1094   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
1095                                        task,
1096                                        task_cls);
1097 }
1098
1099
1100 /**
1101  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1102  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1103  * invoked.
1104  *
1105  * @param task main function of the task
1106  * @param task_cls closure of @a task
1107  * @return unique task identifier for the job
1108  *         only valid until @a task is started!
1109  */
1110 struct GNUNET_SCHEDULER_Task *
1111 GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1112                                void *task_cls)
1113 {
1114   struct GNUNET_SCHEDULER_Task *t;
1115
1116   GNUNET_assert (NULL != active_task);
1117   GNUNET_assert (NULL != task);
1118   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1119   t->callback = task;
1120   t->callback_cls = task_cls;
1121   t->read_fd = -1;
1122   t->write_fd = -1;
1123 #if PROFILE_DELAYS
1124   t->start_time = GNUNET_TIME_absolute_get ();
1125 #endif
1126   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1127   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1128   t->on_shutdown = GNUNET_YES;
1129   t->lifeness = GNUNET_YES;
1130   GNUNET_CONTAINER_DLL_insert (shutdown_head,
1131                                shutdown_tail,
1132                                t);
1133   LOG (GNUNET_ERROR_TYPE_WARNING,
1134        "Adding shutdown task: %p\n",
1135        t);
1136   init_backtrace (t);
1137   return t;
1138 }
1139
1140
1141 /**
1142  * Schedule a new task to be run as soon as possible with the
1143  * (transitive) ignore-shutdown flag either explicitly set or
1144  * explicitly enabled.  This task (and all tasks created from it,
1145  * other than by another call to this function) will either count or
1146  * not count for the "lifeness" of the process.  This API is only
1147  * useful in a few special cases.
1148  *
1149  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1150  * @param task main function of the task
1151  * @param task_cls closure of @a task
1152  * @return unique task identifier for the job
1153  *         only valid until @a task is started!
1154  */
1155 struct GNUNET_SCHEDULER_Task *
1156 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1157                                         GNUNET_SCHEDULER_TaskCallback task,
1158                                         void *task_cls)
1159 {
1160   struct GNUNET_SCHEDULER_Task *ret;
1161
1162   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1163   ret->lifeness = lifeness;
1164   return ret;
1165 }
1166
1167
1168 /**
1169  * Schedule a new task to be run with a specified delay or when any of
1170  * the specified file descriptor sets is ready.  The delay can be used
1171  * as a timeout on the socket(s) being ready.  The task will be
1172  * scheduled for execution once either the delay has expired or any of
1173  * the socket operations is ready.  This is the most general
1174  * function of the "add" family.  Note that the "prerequisite_task"
1175  * must be satisfied in addition to any of the other conditions.  In
1176  * other words, the task will be started when
1177  * <code>
1178  * (prerequisite-run)
1179  * && (delay-ready
1180  *     || any-rs-ready
1181  *     || any-ws-ready)
1182  * </code>
1183  *
1184  * @param delay how long should we wait?
1185  * @param priority priority to use
1186  * @param rfd file descriptor we want to read (can be -1)
1187  * @param wfd file descriptors we want to write (can be -1)
1188  * @param task main function of the task
1189  * @param task_cls closure of @a task
1190  * @return unique task identifier for the job
1191  *         only valid until @a task is started!
1192  */
1193 #ifndef MINGW
1194 static struct GNUNET_SCHEDULER_Task *
1195 add_without_sets (struct GNUNET_TIME_Relative delay,
1196                   enum GNUNET_SCHEDULER_Priority priority,
1197                   const struct GNUNET_NETWORK_Handle *read_nh,
1198                   const struct GNUNET_NETWORK_Handle *write_nh,
1199                   const struct GNUNET_DISK_FileHandle *read_fh,
1200                   const struct GNUNET_DISK_FileHandle *write_fh,
1201                   GNUNET_SCHEDULER_TaskCallback task,
1202                   void *task_cls)
1203 {
1204   struct GNUNET_SCHEDULER_Task *t;
1205
1206   GNUNET_assert (NULL != active_task);
1207   GNUNET_assert (NULL != task);
1208   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1209   initFdInfo (t, read_nh, write_nh, read_fh, write_fh);
1210   t->callback = task;
1211   t->callback_cls = task_cls;
1212 #if DEBUG_FDS
1213   if (-1 != rfd)
1214   {
1215     int flags = fcntl (rfd, F_GETFD);
1216
1217     if ((flags == -1) && (errno == EBADF))
1218     {
1219       LOG (GNUNET_ERROR_TYPE_ERROR,
1220            "Got invalid file descriptor %d!\n",
1221            rfd);
1222       init_backtrace (t);
1223       GNUNET_assert (0);
1224     }
1225   }
1226   if (-1 != wfd)
1227   {
1228     int flags = fcntl (wfd, F_GETFD);
1229
1230     if (flags == -1 && errno == EBADF)
1231     {
1232       LOG (GNUNET_ERROR_TYPE_ERROR,
1233            "Got invalid file descriptor %d!\n",
1234            wfd);
1235       init_backtrace (t);
1236       GNUNET_assert (0);
1237     }
1238   }
1239 #endif
1240
1241 #if PROFILE_DELAYS
1242   t->start_time = GNUNET_TIME_absolute_get ();
1243 #endif
1244   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1245   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1246   t->lifeness = current_lifeness;
1247   GNUNET_CONTAINER_DLL_insert (pending_head,
1248                                pending_tail,
1249                                t);
1250   scheduler_multi_function_call(t, scheduler_driver->add);
1251   scheduler_driver->set_wakeup (scheduler_driver->cls,
1252                                 get_timeout ());
1253   max_priority_added = GNUNET_MAX (max_priority_added,
1254                                    t->priority);
1255   LOG (GNUNET_ERROR_TYPE_DEBUG,
1256        "Adding task %p\n",
1257        t);
1258   init_backtrace (t);
1259   return t;
1260 }
1261 #endif
1262
1263
1264 /**
1265  * Schedule a new task to be run with a specified delay or when the
1266  * specified file descriptor is ready for reading.  The delay can be
1267  * used as a timeout on the socket being ready.  The task will be
1268  * scheduled for execution once either the delay has expired or the
1269  * socket operation is ready.  It will be run with the DEFAULT priority.
1270  *
1271  * @param delay when should this operation time out?
1272  * @param rfd read file-descriptor
1273  * @param task main function of the task
1274  * @param task_cls closure of @a task
1275  * @return unique task identifier for the job
1276  *         only valid until @a task is started!
1277  */
1278 struct GNUNET_SCHEDULER_Task *
1279 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1280                                struct GNUNET_NETWORK_Handle *rfd,
1281                                GNUNET_SCHEDULER_TaskCallback task,
1282                                void *task_cls)
1283 {
1284   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1285                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1286                   rfd, task, task_cls);
1287 }
1288
1289
1290 /**
1291  * Schedule a new task to be run with a specified priority and to be
1292  * run after the specified delay or when the specified file descriptor
1293  * is ready for reading.  The delay can be used as a timeout on the
1294  * socket being ready.  The task will be scheduled for execution once
1295  * either the delay has expired or the socket operation is ready.  It
1296  * will be run with the DEFAULT priority.
1297  *
1298  * @param delay when should this operation time out?
1299  * @param priority priority to use for the task
1300  * @param rfd read file-descriptor
1301  * @param task main function of the task
1302  * @param task_cls closure of @a task
1303  * @return unique task identifier for the job
1304  *         only valid until @a task is started!
1305  */
1306 struct GNUNET_SCHEDULER_Task *
1307 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1308                enum GNUNET_SCHEDULER_Priority priority,
1309                struct GNUNET_NETWORK_Handle *rfd,
1310                GNUNET_SCHEDULER_TaskCallback task,
1311                                              void *task_cls)
1312 {
1313   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1314                                                  rfd,
1315                                                  GNUNET_YES,
1316                                                  GNUNET_NO,
1317                                                  task, 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 delay when should this operation time out?
1330  * @param wfd write file-descriptor
1331  * @param task main function of the task
1332  * @param task_cls closure of @a task
1333  * @return unique task identifier for the job
1334  *         only valid until @a task is started!
1335  */
1336 struct GNUNET_SCHEDULER_Task *
1337 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1338                                 struct GNUNET_NETWORK_Handle *wfd,
1339                                 GNUNET_SCHEDULER_TaskCallback task,
1340                                 void *task_cls)
1341 {
1342   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1343                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1344                                                  wfd,
1345                                                  GNUNET_NO, GNUNET_YES,
1346                                                  task, task_cls);
1347 }
1348
1349 /**
1350  * Schedule a new task to be run with a specified delay or when the
1351  * specified file descriptor is ready.  The delay can be
1352  * used as a timeout on the socket being ready.  The task will be
1353  * scheduled for execution once either the delay has expired or the
1354  * socket operation is ready.
1355  *
1356  * @param delay when should this operation time out?
1357  * @param priority priority of the task
1358  * @param fd file-descriptor
1359  * @param on_read whether to poll the file-descriptor for readability
1360  * @param on_write whether to poll the file-descriptor for writability
1361  * @param task main function of the task
1362  * @param task_cls closure of task
1363  * @return unique task identifier for the job
1364  *         only valid until "task" is started!
1365  */
1366 struct GNUNET_SCHEDULER_Task *
1367 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1368                                          enum GNUNET_SCHEDULER_Priority priority,
1369                                          struct GNUNET_NETWORK_Handle *fd,
1370                                          int on_read,
1371                                          int on_write,
1372                                          GNUNET_SCHEDULER_TaskCallback task,
1373                                          void *task_cls)
1374 {
1375 #if MINGW
1376   struct GNUNET_NETWORK_FDSet *s;
1377   struct GNUNET_SCHEDULER_Task * ret;
1378
1379   GNUNET_assert (NULL != fd);
1380   s = GNUNET_NETWORK_fdset_create ();
1381   GNUNET_NETWORK_fdset_set (s, fd);
1382   ret = GNUNET_SCHEDULER_add_select (
1383       priority, delay,
1384       on_read  ? s : NULL,
1385       on_write ? s : NULL,
1386       task, task_cls);
1387   GNUNET_NETWORK_fdset_destroy (s);
1388   return ret;
1389 #else
1390   GNUNET_assert (on_read || on_write);
1391   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1392   return add_without_sets (delay, priority,
1393                            on_read  ? fd : NULL,
1394                            on_write ? fd : NULL,
1395                            NULL,
1396                            NULL,
1397                            task, task_cls);
1398 #endif
1399 }
1400
1401
1402 /**
1403  * Schedule a new task to be run with a specified delay or when the
1404  * specified file descriptor is ready for reading.  The delay can be
1405  * used as a timeout on the socket being ready.  The task will be
1406  * scheduled for execution once either the delay has expired or the
1407  * socket operation is ready. It will be run with the DEFAULT priority.
1408  *
1409  * @param delay when should this operation time out?
1410  * @param rfd read file-descriptor
1411  * @param task main function of the task
1412  * @param task_cls closure of @a task
1413  * @return unique task identifier for the job
1414  *         only valid until @a task is started!
1415  */
1416 struct GNUNET_SCHEDULER_Task *
1417 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1418                                 const struct GNUNET_DISK_FileHandle *rfd,
1419                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1420 {
1421   return GNUNET_SCHEDULER_add_file_with_priority (
1422       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1423       rfd, GNUNET_YES, GNUNET_NO,
1424       task, task_cls);
1425 }
1426
1427
1428 /**
1429  * Schedule a new task to be run with a specified delay or when the
1430  * specified file descriptor is ready for writing.  The delay can be
1431  * used as a timeout on the socket being ready.  The task will be
1432  * scheduled for execution once either the delay has expired or the
1433  * socket operation is ready. It will be run with the DEFAULT priority.
1434  *
1435  * @param delay when should this operation time out?
1436  * @param wfd write file-descriptor
1437  * @param task main function of the task
1438  * @param task_cls closure of @a task
1439  * @return unique task identifier for the job
1440  *         only valid until @a task is started!
1441  */
1442 struct GNUNET_SCHEDULER_Task *
1443 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1444                                  const struct GNUNET_DISK_FileHandle *wfd,
1445                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1446 {
1447   return GNUNET_SCHEDULER_add_file_with_priority (
1448       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1449       wfd, GNUNET_NO, GNUNET_YES,
1450       task, task_cls);
1451 }
1452
1453
1454 /**
1455  * Schedule a new task to be run with a specified delay or when the
1456  * specified file descriptor is ready.  The delay can be
1457  * used as a timeout on the socket being ready.  The task will be
1458  * scheduled for execution once either the delay has expired or the
1459  * socket operation is ready.
1460  *
1461  * @param delay when should this operation time out?
1462  * @param priority priority of the task
1463  * @param fd file-descriptor
1464  * @param on_read whether to poll the file-descriptor for readability
1465  * @param on_write whether to poll the file-descriptor for writability
1466  * @param task main function of the task
1467  * @param task_cls closure of @a task
1468  * @return unique task identifier for the job
1469  *         only valid until @a task is started!
1470  */
1471 struct GNUNET_SCHEDULER_Task *
1472 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1473                                          enum GNUNET_SCHEDULER_Priority priority,
1474                                          const struct GNUNET_DISK_FileHandle *fd,
1475                                          int on_read, int on_write,
1476                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1477 {
1478 #if MINGW
1479   struct GNUNET_NETWORK_FDSet *s;
1480   struct GNUNET_SCHEDULER_Task * ret;
1481
1482   GNUNET_assert (NULL != fd);
1483   s = GNUNET_NETWORK_fdset_create ();
1484   GNUNET_NETWORK_fdset_handle_set (s, fd);
1485   ret = GNUNET_SCHEDULER_add_select (
1486       priority, delay,
1487       on_read  ? s : NULL,
1488       on_write ? s : NULL,
1489       task, task_cls);
1490   GNUNET_NETWORK_fdset_destroy (s);
1491   return ret;
1492 #else
1493   GNUNET_assert (on_read || on_write);
1494   GNUNET_assert (fd->fd >= 0);
1495   return add_without_sets (delay, priority,
1496                            NULL,
1497                            NULL,
1498                            on_read ? fd : NULL,
1499                            on_write ? fd : NULL,
1500                            task, task_cls);
1501 #endif
1502 }
1503
1504
1505 /**
1506  * Schedule a new task to be run with a specified delay or when any of
1507  * the specified file descriptor sets is ready.  The delay can be used
1508  * as a timeout on the socket(s) being ready.  The task will be
1509  * scheduled for execution once either the delay has expired or any of
1510  * the socket operations is ready.  This is the most general
1511  * function of the "add" family.  Note that the "prerequisite_task"
1512  * must be satisfied in addition to any of the other conditions.  In
1513  * other words, the task will be started when
1514  * <code>
1515  * (prerequisite-run)
1516  * && (delay-ready
1517  *     || any-rs-ready
1518  *     || any-ws-ready) )
1519  * </code>
1520  *
1521  * @param prio how important is this task?
1522  * @param delay how long should we wait?
1523  * @param rs set of file descriptors we want to read (can be NULL)
1524  * @param ws set of file descriptors we want to write (can be NULL)
1525  * @param task main function of the task
1526  * @param task_cls closure of @a task
1527  * @return unique task identifier for the job
1528  *         only valid until @a task is started!
1529  */
1530 struct GNUNET_SCHEDULER_Task *
1531 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1532                              struct GNUNET_TIME_Relative delay,
1533                              const struct GNUNET_NETWORK_FDSet *rs,
1534                              const struct GNUNET_NETWORK_FDSet *ws,
1535                              GNUNET_SCHEDULER_TaskCallback task,
1536                              void *task_cls)
1537 {
1538   struct GNUNET_SCHEDULER_Task *t;
1539
1540   if ( (NULL == rs) &&
1541        (NULL == ws) )
1542     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1543                                                        prio,
1544                                                        task,
1545                                                        task_cls);
1546   GNUNET_assert (NULL != active_task);
1547   GNUNET_assert (NULL != task);
1548   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1549   t->callback = task;
1550   t->callback_cls = task_cls;
1551   t->read_fd = -1;
1552   t->write_fd = -1;
1553   if (NULL != rs)
1554   {
1555     t->read_set = GNUNET_NETWORK_fdset_create ();
1556     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1557   }
1558   if (NULL != ws)
1559   {
1560     t->write_set = GNUNET_NETWORK_fdset_create ();
1561     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1562   }
1563 #if PROFILE_DELAYS
1564   t->start_time = GNUNET_TIME_absolute_get ();
1565 #endif
1566   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1567   t->priority =
1568       check_priority ((prio ==
1569                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1570                       prio);
1571   t->lifeness = current_lifeness;
1572   GNUNET_CONTAINER_DLL_insert (pending_head,
1573                                pending_tail,
1574                                t);
1575   scheduler_multi_function_call(t, scheduler_driver->add);
1576   scheduler_driver->set_wakeup (scheduler_driver->cls,
1577                                 get_timeout ());
1578   max_priority_added = GNUNET_MAX (max_priority_added,
1579            t->priority);
1580   LOG (GNUNET_ERROR_TYPE_WARNING,
1581        "Adding task %p\n",
1582        t);
1583   init_backtrace (t);
1584   return t;
1585 }
1586
1587
1588 /**
1589  * Function used by event-loop implementations to signal the scheduler
1590  * that a particular @a task is ready due to an event of type @a et.
1591  *
1592  * This function will then queue the task to notify the application
1593  * that the task is ready (with the respective priority).
1594  *
1595  * @param task the task that is ready, NULL for wake up calls
1596  * @param et information about why the task is ready
1597  */
1598 void
1599 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1600                              enum GNUNET_SCHEDULER_EventType et)
1601 {
1602   enum GNUNET_SCHEDULER_Reason reason;
1603   struct GNUNET_TIME_Absolute now;
1604
1605   LOG (GNUNET_ERROR_TYPE_WARNING,
1606        "task ready: %p\n",
1607        task);
1608
1609   now = GNUNET_TIME_absolute_get ();
1610   reason = task->reason;
1611   if (now.abs_value_us >= task->timeout.abs_value_us)
1612     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1613   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1614        (0 != (GNUNET_SCHEDULER_ET_IN & et)) )
1615     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1616   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1617        (0 != (GNUNET_SCHEDULER_ET_OUT & et)) )
1618     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1619   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1620   task->reason = reason;
1621   task->fds = &task->fdx;
1622   task->fdx.et = et;
1623   task->fds_len = 1;
1624   GNUNET_CONTAINER_DLL_remove (pending_head,
1625                                pending_tail,
1626                                task);
1627   queue_ready_task (task);
1628 }
1629
1630
1631 /**
1632  * Function called by the driver to tell the scheduler to run some of
1633  * the tasks that are ready.  This function may return even though
1634  * there are tasks left to run just to give other tasks a chance as
1635  * well.  If we return #GNUNET_YES, the driver should call this
1636  * function again as soon as possible, while if we return #GNUNET_NO
1637  * it must block until the operating system has more work as the
1638  * scheduler has no more work to do right now.
1639  *
1640  * @param sh scheduler handle that was given to the `loop`
1641  * @return #GNUNET_OK if there are more tasks that are ready,
1642  *          and thus we would like to run more (yield to avoid
1643  *          blocking other activities for too long)
1644  *         #GNUNET_NO if we are done running tasks (yield to block)
1645  *         #GNUNET_SYSERR on error
1646  */
1647 int
1648 GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh)
1649 {
1650   // FIXME: we have to check lifeness here!
1651   enum GNUNET_SCHEDULER_Priority p;
1652   struct GNUNET_SCHEDULER_Task *pos;
1653   struct GNUNET_TIME_Absolute now;
1654
1655   /* check for tasks that reached the timeout! */
1656   now = GNUNET_TIME_absolute_get ();
1657   while (NULL != (pos = pending_timeout_head))
1658   {
1659     if (now.abs_value_us >= pos->timeout.abs_value_us)
1660       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1661     if (0 == pos->reason)
1662       break;
1663     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1664                                  pending_timeout_tail,
1665                                  pos);
1666     if (pending_timeout_last == pos)
1667       pending_timeout_last = NULL;
1668     queue_ready_task (pos);
1669   }
1670
1671   if (0 == ready_count)
1672     return GNUNET_NO;
1673
1674   /* find out which task priority level we are going to
1675      process this time */
1676   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
1677   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
1678   /* yes, p>0 is correct, 0 is "KEEP" which should
1679    * always be an empty queue (see assertion)! */
1680   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
1681   {
1682     pos = ready_head[p];
1683     if (NULL != pos)
1684       break;
1685   }
1686   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
1687
1688   /* process all tasks at this priority level, then yield */
1689   while (NULL != (pos = ready_head[p]))
1690   {
1691     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1692          ready_tail[p],
1693          pos);
1694     ready_count--;
1695     current_priority = pos->priority;
1696     current_lifeness = pos->lifeness;
1697     active_task = pos;
1698 #if PROFILE_DELAYS
1699     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
1700   DELAY_THRESHOLD.rel_value_us)
1701     {
1702       LOG (GNUNET_ERROR_TYPE_WARNING,
1703      "Task %p took %s to be scheduled\n",
1704      pos,
1705      GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
1706                GNUNET_YES));
1707     }
1708 #endif
1709     tc.reason = pos->reason;
1710     GNUNET_NETWORK_fdset_zero (sh->rs);
1711     GNUNET_NETWORK_fdset_zero (sh->ws);
1712     tc.fds_len = pos->fds_len;
1713     tc.fds = pos->fds;
1714     tc.read_ready = (NULL == pos->read_set) ? sh->rs : pos->read_set;
1715     if ( (-1 != pos->read_fd) &&
1716    (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) )
1717       GNUNET_NETWORK_fdset_set_native (sh->rs,
1718                pos->read_fd);
1719     tc.write_ready = (NULL == pos->write_set) ? sh->ws : pos->write_set;
1720     if ((-1 != pos->write_fd) &&
1721   (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
1722       GNUNET_NETWORK_fdset_set_native (sh->ws,
1723                pos->write_fd);
1724     LOG (GNUNET_ERROR_TYPE_WARNING,
1725    "Running task from driver: %p\n",
1726    pos);
1727     pos->callback (pos->callback_cls);
1728     active_task = NULL;
1729     dump_backtrace (pos);
1730     destroy_task (pos);
1731     tasks_run++;
1732   }
1733   scheduler_driver->set_wakeup (scheduler_driver->cls,
1734                                 get_timeout ());
1735   if (0 == ready_count)
1736     return GNUNET_NO;
1737   return GNUNET_OK;
1738 }
1739
1740
1741 /**
1742  * Initialize and run scheduler.  This function will return when all
1743  * tasks have completed.  On systems with signals, receiving a SIGTERM
1744  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown
1745  * to be run after the active task is complete.  As a result, SIGTERM
1746  * causes all shutdown tasks to be scheduled with reason
1747  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
1748  * afterwards will execute normally!).  Note that any particular
1749  * signal will only shut down one scheduler; applications should
1750  * always only create a single scheduler.
1751  *
1752  * @param driver drive to use for the event loop
1753  * @param task task to run first (and immediately)
1754  * @param task_cls closure of @a task
1755  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1756  */
1757 int
1758 GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver,
1759                                   GNUNET_SCHEDULER_TaskCallback task,
1760                                   void *task_cls)
1761 {
1762   int ret;
1763   struct GNUNET_SIGNAL_Context *shc_int;
1764   struct GNUNET_SIGNAL_Context *shc_term;
1765 #if (SIGTERM != GNUNET_TERM_SIG)
1766   struct GNUNET_SIGNAL_Context *shc_gterm;
1767 #endif
1768 #ifndef MINGW
1769   struct GNUNET_SIGNAL_Context *shc_quit;
1770   struct GNUNET_SIGNAL_Context *shc_hup;
1771   struct GNUNET_SIGNAL_Context *shc_pipe;
1772 #endif
1773   struct GNUNET_SCHEDULER_Task tsk;
1774   const struct GNUNET_DISK_FileHandle *pr;
1775
1776   /* general set-up */
1777   GNUNET_assert (NULL == active_task);
1778   GNUNET_assert (NULL == shutdown_pipe_handle);
1779   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
1780                                            GNUNET_NO,
1781                                            GNUNET_NO,
1782                                            GNUNET_NO);
1783   GNUNET_assert (NULL != shutdown_pipe_handle);
1784   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
1785                                 GNUNET_DISK_PIPE_END_READ);
1786   my_pid = getpid ();
1787   scheduler_driver = driver;
1788
1789   /* install signal handlers */
1790   LOG (GNUNET_ERROR_TYPE_WARNING,
1791        "Registering signal handlers\n");
1792   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
1793              &sighandler_shutdown);
1794   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
1795               &sighandler_shutdown);
1796 #if (SIGTERM != GNUNET_TERM_SIG)
1797   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
1798                &sighandler_shutdown);
1799 #endif
1800 #ifndef MINGW
1801   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
1802               &sighandler_pipe);
1803   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
1804               &sighandler_shutdown);
1805   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
1806              &sighandler_shutdown);
1807 #endif
1808
1809   /* Setup initial tasks */
1810   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
1811   current_lifeness = GNUNET_YES;
1812   memset (&tsk,
1813     0,
1814     sizeof (tsk));
1815   active_task = &tsk;
1816   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
1817                                                  task_cls,
1818                                                  GNUNET_SCHEDULER_REASON_STARTUP,
1819                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
1820   GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
1821                                           &GNUNET_OS_install_parent_control_handler,
1822                                           NULL);
1823   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1824                                   pr,
1825                                   &shutdown_task,
1826                                   NULL);
1827   active_task = NULL;
1828   /* begin main event loop */
1829   sh.rs = GNUNET_NETWORK_fdset_create ();
1830   sh.ws = GNUNET_NETWORK_fdset_create ();
1831   //GNUNET_NETWORK_fdset_handle_set (sh.rs, pr);
1832   ret = driver->loop (driver->cls,
1833                       &sh);
1834   LOG (GNUNET_ERROR_TYPE_WARNING,
1835        "loop finished!");
1836   GNUNET_NETWORK_fdset_destroy (sh.rs);
1837   GNUNET_NETWORK_fdset_destroy (sh.ws);
1838
1839   /* uninstall signal handlers */
1840   GNUNET_SIGNAL_handler_uninstall (shc_int);
1841   GNUNET_SIGNAL_handler_uninstall (shc_term);
1842 #if (SIGTERM != GNUNET_TERM_SIG)
1843   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
1844 #endif
1845 #ifndef MINGW
1846   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
1847   GNUNET_SIGNAL_handler_uninstall (shc_quit);
1848   GNUNET_SIGNAL_handler_uninstall (shc_hup);
1849 #endif
1850   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
1851   shutdown_pipe_handle = NULL;
1852   return ret;
1853 }
1854
1855
1856 int
1857 select_add (void *cls,
1858             struct GNUNET_SCHEDULER_Task *task,
1859             struct GNUNET_SCHEDULER_FdInfo *fdi)
1860 {
1861   struct DriverContext *context = cls;
1862   GNUNET_assert (NULL != context);
1863
1864   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (0 >= fdi->sock))
1865   {
1866     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
1867     return GNUNET_SYSERR;
1868   }
1869
1870   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
1871   scheduled->task = task;
1872   scheduled->fdi = fdi;
1873
1874   switch (fdi->et)
1875   {
1876   case GNUNET_SCHEDULER_ET_IN:
1877   {
1878     GNUNET_CONTAINER_DLL_insert (context->scheduled_in_head,
1879                                  context->scheduled_in_tail,
1880                                  scheduled);
1881     break;
1882   }
1883   case GNUNET_SCHEDULER_ET_OUT:
1884   {
1885     GNUNET_CONTAINER_DLL_insert (context->scheduled_out_head,
1886                                  context->scheduled_out_tail,
1887                                  scheduled);
1888     break;
1889   }
1890   default:
1891   {
1892     // FIXME: other event types not implemented yet
1893     GNUNET_assert (0);
1894   }
1895   }
1896   return GNUNET_OK;
1897 }
1898
1899
1900 int
1901 select_del (void *cls,
1902             struct GNUNET_SCHEDULER_Task *task,
1903             struct GNUNET_SCHEDULER_FdInfo *fdi)
1904 {
1905   struct DriverContext *context = cls;
1906   GNUNET_assert (NULL != context);
1907
1908   int ret = GNUNET_SYSERR;
1909   struct Scheduled *pos;
1910   switch (fdi->et)
1911   {
1912   case GNUNET_SCHEDULER_ET_IN:
1913   {
1914     for (pos = context->scheduled_in_head; NULL != pos; pos = pos->next)
1915     {
1916       if (pos->task == task)
1917       {
1918         GNUNET_CONTAINER_DLL_remove (context->scheduled_in_head,
1919                                      context->scheduled_in_tail,
1920                                      pos);
1921         ret = GNUNET_OK;
1922       }
1923     }
1924     break; 
1925   }
1926   case GNUNET_SCHEDULER_ET_OUT:
1927   {
1928     for (pos = context->scheduled_out_head; NULL != pos; pos = pos->next)
1929     {
1930       if (pos->task == task)
1931       {
1932         GNUNET_CONTAINER_DLL_remove (context->scheduled_out_head,
1933                                      context->scheduled_out_tail,
1934                                      pos);
1935         ret = GNUNET_OK;
1936       }
1937     }
1938     break;
1939   }
1940   default:
1941   {
1942     // FIXME: other event types not implemented yet
1943     GNUNET_assert (0);
1944   }
1945   }
1946   return ret;
1947 }
1948
1949
1950 //int
1951 //select_loop_condition (const struct DriverContext *context)
1952 //{
1953 //  struct GNUNET_TIME_absolute_
1954 //}
1955
1956
1957 int
1958 select_loop (void *cls,
1959              struct GNUNET_SCHEDULER_Handle *sh)
1960 {
1961   struct GNUNET_NETWORK_FDSet *rs;
1962   struct GNUNET_NETWORK_FDSet *ws;
1963   struct DriverContext *context;
1964   int select_result;
1965   unsigned long long last_tr;
1966   unsigned int busy_wait_warning;
1967   
1968   context = cls;
1969   GNUNET_assert (NULL != context);
1970   rs = GNUNET_NETWORK_fdset_create ();
1971   ws = GNUNET_NETWORK_fdset_create ();
1972   last_tr = 0;
1973   busy_wait_warning = 0;
1974   while ((NULL != context->scheduled_in_head) ||
1975          (NULL != context->scheduled_out_head))
1976   {
1977     GNUNET_NETWORK_fdset_zero (rs);
1978     GNUNET_NETWORK_fdset_zero (ws);
1979     struct Scheduled *pos;
1980     for (pos = context->scheduled_in_head; NULL != pos; pos = pos->next)
1981     {
1982       GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
1983     }
1984     for (pos = context->scheduled_out_head; NULL != pos; pos = pos->next)
1985     {
1986       GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
1987     }
1988     if (ready_count > 0)
1989     {
1990       /* no blocking, more work already ready! */
1991       context->timeout = GNUNET_TIME_UNIT_ZERO;
1992     }
1993     if (NULL == scheduler_select)
1994     {
1995       select_result = GNUNET_NETWORK_socket_select (rs,
1996                                                     ws,
1997                                                     NULL,
1998                                                     context->timeout);
1999     }
2000     else
2001     {
2002       select_result = scheduler_select (scheduler_select_cls,
2003                                         rs,
2004                                         ws,
2005                                         NULL,
2006                                         context->timeout);
2007     }
2008     if (select_result == GNUNET_SYSERR)
2009     {
2010       if (errno == EINTR)
2011         continue;
2012
2013       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
2014 #ifndef MINGW
2015 #if USE_LSOF
2016       char lsof[512];
2017
2018       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
2019       (void) close (1);
2020       (void) dup2 (2, 1);
2021       if (0 != system (lsof))
2022         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2023                       "system");
2024 #endif
2025 #endif
2026 #if DEBUG_FDS
2027       struct GNUNET_SCHEDULER_Task *t;
2028       for (t = pending_head; NULL != t; t = t->next)
2029       {
2030         if (-1 != t->read_fd)
2031         {
2032           int flags = fcntl (t->read_fd, F_GETFD);
2033           if ((flags == -1) && (errno == EBADF))
2034           {
2035             LOG (GNUNET_ERROR_TYPE_ERROR,
2036                  "Got invalid file descriptor %d!\n",
2037                  t->read_fd);
2038             dump_backtrace (t);
2039           }
2040         }
2041         if (-1 != t->write_fd)
2042         {
2043           int flags = fcntl (t->write_fd, F_GETFD);
2044           if ((flags == -1) && (errno == EBADF))
2045           {
2046             LOG (GNUNET_ERROR_TYPE_ERROR,
2047                  "Got invalid file descriptor %d!\n",
2048                  t->write_fd);
2049             dump_backtrace (t);
2050           }
2051         }
2052       }
2053 #endif
2054       GNUNET_assert (0);
2055       return GNUNET_SYSERR;
2056     }
2057     if ( (0 == select_result) &&
2058          (0 == context->timeout.rel_value_us) &&
2059          (busy_wait_warning > 16) )
2060     {
2061       LOG (GNUNET_ERROR_TYPE_WARNING,
2062            "Looks like we're busy waiting...\n");
2063       short_wait (100);                /* mitigate */
2064     }
2065     for (pos = context->scheduled_in_head; NULL != pos; pos = pos->next)
2066     {
2067       if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, pos->fdi->sock))
2068       {
2069         GNUNET_SCHEDULER_task_ready (pos->task, GNUNET_SCHEDULER_ET_IN);
2070         GNUNET_CONTAINER_DLL_remove (context->scheduled_in_head,
2071                                      context->scheduled_in_tail,
2072                                      pos);
2073       }
2074     }
2075     for (pos = context->scheduled_out_head; NULL != pos; pos = pos->next)
2076     {
2077       if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, pos->fdi->sock))
2078       {
2079         GNUNET_SCHEDULER_task_ready (pos->task, GNUNET_SCHEDULER_ET_OUT);
2080         GNUNET_CONTAINER_DLL_remove (context->scheduled_out_head,
2081                                      context->scheduled_out_tail,
2082                                      pos);
2083       }
2084     }
2085     GNUNET_SCHEDULER_run_from_driver (sh);
2086     if (last_tr == tasks_run)
2087     {
2088       short_wait (1);
2089       busy_wait_warning++;
2090     }
2091     else
2092     {
2093       last_tr = tasks_run;
2094       busy_wait_warning = 0;
2095     }
2096   }
2097   return GNUNET_OK; 
2098 }
2099
2100
2101 void
2102 select_set_wakeup(void *cls,
2103                   struct GNUNET_TIME_Absolute dt)
2104 {
2105   struct DriverContext *context = cls;
2106   GNUNET_assert (NULL != context);
2107  
2108   context->timeout = GNUNET_TIME_absolute_get_remaining (dt);
2109 }
2110
2111
2112 /**
2113  * Obtain the driver for using select() as the event loop.
2114  *
2115  * @return NULL on error
2116  */
2117 struct GNUNET_SCHEDULER_Driver *
2118 GNUNET_SCHEDULER_driver_select ()
2119 {
2120   struct GNUNET_SCHEDULER_Driver *select_driver;
2121   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2122
2123   select_driver->loop = &select_loop;
2124   select_driver->add = &select_add;
2125   select_driver->del = &select_del;
2126   select_driver->set_wakeup = &select_set_wakeup;
2127
2128   return select_driver;
2129 }
2130
2131
2132 /* end of scheduler.c */