e1b6b4b51ace1138cb208398e4a48287ae0826cf
[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 init_fd_info (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     struct GNUNET_SCHEDULER_FdInfo *fds = GNUNET_new_array (2, struct GNUNET_SCHEDULER_FdInfo);
699     struct GNUNET_SCHEDULER_FdInfo read_fdi = {.fd = read_nh, .et = GNUNET_SCHEDULER_ET_IN, .sock = GNUNET_NETWORK_get_fd (read_nh)};
700     struct GNUNET_SCHEDULER_FdInfo write_fdi = {.fd = write_nh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = GNUNET_NETWORK_get_fd (write_nh)};
701     fds[0] = read_fdi;
702     fds[1] = write_fdi;
703     t->fds = fds;
704     t->fds_len = 2;
705   }
706   else if (NULL != read_fh && NULL != write_fh)
707   {
708     struct GNUNET_SCHEDULER_FdInfo *fds = GNUNET_new_array (2, struct GNUNET_SCHEDULER_FdInfo);
709     struct GNUNET_SCHEDULER_FdInfo read_fdi = {.fh = read_fh, .et = GNUNET_SCHEDULER_ET_IN, .sock = read_fh->fd};
710     struct GNUNET_SCHEDULER_FdInfo write_fdi = {.fh = write_fh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = write_fh->fd};
711     fds[0] = read_fdi;
712     fds[1] = write_fdi;
713     t->fds = fds;
714     t->fds_len = 2;
715   }
716   else if (NULL != read_nh)
717   {
718     struct GNUNET_SCHEDULER_FdInfo read_fdi = {.fd = read_nh, .et = GNUNET_SCHEDULER_ET_IN, .sock = GNUNET_NETWORK_get_fd (read_nh)};
719     t->fdx = read_fdi;
720     t->fds = &t->fdx;
721     t->read_fd = t->fdx.sock;
722   }
723   else if (NULL != write_nh)
724   {
725     struct GNUNET_SCHEDULER_FdInfo write_fdi = {.fd = write_nh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = GNUNET_NETWORK_get_fd (write_nh)};
726     t->fdx = write_fdi;
727     t->fds = &t->fdx;
728     t->write_fd = t->fdx.sock;
729   }
730   else if (NULL != read_fh)
731   {
732     struct GNUNET_SCHEDULER_FdInfo read_fdi = {.fh = read_fh, .et = GNUNET_SCHEDULER_ET_IN, .sock = read_fh->fd};
733     t->fdx = read_fdi;
734     t->fds = &t->fdx;
735     t->read_fd = t->fdx.sock;
736   }
737   else if (NULL != write_fh)
738   {
739     struct GNUNET_SCHEDULER_FdInfo write_fdi = {.fh = write_fh, .et = GNUNET_SCHEDULER_ET_OUT, .sock = write_fh->fd};
740     t->fdx = write_fdi;
741     t->fds = &t->fdx;
742     t->write_fd = t->fdx.sock;
743   }
744 }
745
746
747 int scheduler_multi_function_call(struct GNUNET_SCHEDULER_Task *t, int (*driver_func)())
748 {
749   if (t->fds_len > 1)
750   {
751     int success = GNUNET_YES;
752     for (int i = 0; i < t->fds_len;i++)
753     {
754       success = driver_func (scheduler_driver->cls, t , t->fds+i) && success;
755     }
756     return success;
757   }
758   else
759   {
760     return driver_func (scheduler_driver->cls, t , t->fds);
761   }
762 }
763
764
765 void
766 shutdown_task (void *cls)
767 {
768   char c;
769   const struct GNUNET_DISK_FileHandle *pr;
770   
771   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
772                                 GNUNET_DISK_PIPE_END_READ);
773   GNUNET_assert (! GNUNET_DISK_handle_invalid (pr));
774   /* consume the signal */
775   GNUNET_DISK_file_read (pr, &c, sizeof (c));
776   /* mark all active tasks as ready due to shutdown */
777   GNUNET_SCHEDULER_shutdown ();
778 }
779
780
781 /**
782  * Cancel the task with the specified identifier.
783  * The task must not yet have run.
784  *
785  * @param task id of the task to cancel
786  * @return original closure of the task
787  */
788 void *
789 GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Task *task)
790 {
791   enum GNUNET_SCHEDULER_Priority p;
792   void *ret;
793
794   GNUNET_assert ( (NULL != active_task) ||
795       (GNUNET_NO == task->lifeness) );
796   if (! task->in_ready_list)
797   {
798     if ( (-1 == task->read_fd) &&
799          (-1 == task->write_fd) &&
800          (NULL == task->read_set) &&
801          (NULL == task->write_set) )
802     {
803       if (GNUNET_YES == task->on_shutdown)
804         GNUNET_CONTAINER_DLL_remove (shutdown_head,
805                                      shutdown_tail,
806                                      task);
807       else
808       {
809         GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
810                                      pending_timeout_tail,
811                                      task);
812         if (pending_timeout_last == task)
813           pending_timeout_last = NULL;
814         else
815           scheduler_driver->set_wakeup (scheduler_driver->cls,
816                                         get_timeout ());
817       }
818       //TODO check if this is redundant
819       if (task == pending_timeout_last)
820         pending_timeout_last = NULL;
821     }
822     else
823     {
824       GNUNET_CONTAINER_DLL_remove (pending_head,
825                                    pending_tail,
826                                    task);
827       scheduler_multi_function_call(task, scheduler_driver->del);
828     }
829   }
830   else
831   {
832     p = check_priority (task->priority);
833     GNUNET_CONTAINER_DLL_remove (ready_head[p],
834                                  ready_tail[p],
835                                  task);
836     ready_count--;
837   }
838   LOG (GNUNET_ERROR_TYPE_WARNING,
839        "Canceling task %p\n",
840        task);
841   ret = task->callback_cls;
842   destroy_task (task);
843   return ret;
844 }
845
846
847 /**
848  * Initialize backtrace data for task @a t
849  *
850  * @param t task to initialize
851  */
852 static void
853 init_backtrace (struct GNUNET_SCHEDULER_Task *t)
854 {
855 #if EXECINFO
856   void *backtrace_array[MAX_TRACE_DEPTH];
857
858   t->num_backtrace_strings
859     = backtrace (backtrace_array, MAX_TRACE_DEPTH);
860   t->backtrace_strings =
861       backtrace_symbols (backtrace_array,
862        t->num_backtrace_strings);
863   dump_backtrace (t);
864 #endif
865 }
866
867
868 /**
869  * Continue the current execution with the given function.  This is
870  * similar to the other "add" functions except that there is no delay
871  * and the reason code can be specified.
872  *
873  * @param task main function of the task
874  * @param task_cls closure for @a task
875  * @param reason reason for task invocation
876  * @param priority priority to use for the task
877  */
878 void
879 GNUNET_SCHEDULER_add_with_reason_and_priority (GNUNET_SCHEDULER_TaskCallback task,
880                                                void *task_cls,
881                                                enum GNUNET_SCHEDULER_Reason reason,
882                                                enum GNUNET_SCHEDULER_Priority priority)
883 {
884   struct GNUNET_SCHEDULER_Task *t;
885
886   GNUNET_assert (NULL != task);
887   GNUNET_assert ((NULL != active_task) ||
888                  (GNUNET_SCHEDULER_REASON_STARTUP == reason));
889   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
890   t->read_fd = -1;
891   t->write_fd = -1;
892   t->callback = task;
893   t->callback_cls = task_cls;
894 #if PROFILE_DELAYS
895   t->start_time = GNUNET_TIME_absolute_get ();
896 #endif
897   t->reason = reason;
898   t->priority = priority;
899   t->lifeness = current_lifeness;
900   LOG (GNUNET_ERROR_TYPE_WARNING,
901        "Adding continuation task %p\n",
902        t);
903   init_backtrace (t);
904   queue_ready_task (t);
905 }
906
907
908 /**
909  * Schedule a new task to be run at the specified time.  The task
910  * will be scheduled for execution at time @a at.
911  *
912  * @param at time when the operation should run
913  * @param priority priority to use for the task
914  * @param task main function of the task
915  * @param task_cls closure of @a task
916  * @return unique task identifier for the job
917  *         only valid until @a task is started!
918  */
919 struct GNUNET_SCHEDULER_Task *
920 GNUNET_SCHEDULER_add_at_with_priority (struct GNUNET_TIME_Absolute at,
921                                        enum GNUNET_SCHEDULER_Priority priority,
922                                        GNUNET_SCHEDULER_TaskCallback task,
923                                        void *task_cls)
924 {
925   struct GNUNET_SCHEDULER_Task *t;
926   struct GNUNET_SCHEDULER_Task *pos;
927   struct GNUNET_SCHEDULER_Task *prev;
928
929   GNUNET_assert (NULL != active_task);
930   GNUNET_assert (NULL != task);
931   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
932   t->callback = task;
933   t->callback_cls = task_cls;
934   t->read_fd = -1;
935   t->write_fd = -1;
936 #if PROFILE_DELAYS
937   t->start_time = GNUNET_TIME_absolute_get ();
938 #endif
939   t->timeout = at;
940   t->priority = priority;
941   t->lifeness = current_lifeness;
942   /* try tail first (optimization in case we are
943    * appending to a long list of tasks with timeouts) */
944   if ( (NULL == pending_timeout_head) ||
945        (at.abs_value_us < pending_timeout_head->timeout.abs_value_us) )
946   {
947     GNUNET_CONTAINER_DLL_insert (pending_timeout_head,
948                                  pending_timeout_tail,
949                                  t);
950     scheduler_driver->set_wakeup (scheduler_driver->cls,
951                                   at);
952   }
953   else
954   {
955     /* first move from heuristic start backwards to before start time */
956     prev = pending_timeout_last;
957     while ( (NULL != prev) &&
958             (prev->timeout.abs_value_us > t->timeout.abs_value_us) )
959       prev = prev->prev;
960     /* now, move from heuristic start (or head of list) forward to insertion point */
961     if (NULL == prev)
962       pos = pending_timeout_head;
963     else
964       pos = prev->next;
965     while ( (NULL != pos) &&
966             ( (pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
967               (0 != pos->reason) ) )
968     {
969       prev = pos;
970       pos = pos->next;
971     }
972     GNUNET_CONTAINER_DLL_insert_after (pending_timeout_head,
973                                        pending_timeout_tail,
974                                        prev,
975                                        t);
976     scheduler_driver->set_wakeup (scheduler_driver->cls,
977                                   get_timeout());
978   }
979   /* finally, update heuristic insertion point to last insertion... */
980   pending_timeout_last = t;
981
982   LOG (GNUNET_ERROR_TYPE_WARNING,
983        "Adding task: %p\n",
984        t);
985   init_backtrace (t);
986   return t;
987 }
988
989
990 /**
991  * Schedule a new task to be run with a specified delay.  The task
992  * will be scheduled for execution once the delay has expired.
993  *
994  * @param delay when should this operation time out?
995  * @param priority priority to use for the task
996  * @param task main function of the task
997  * @param task_cls closure of @a task
998  * @return unique task identifier for the job
999  *         only valid until @a task is started!
1000  */
1001 struct GNUNET_SCHEDULER_Task *
1002 GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
1003               enum GNUNET_SCHEDULER_Priority priority,
1004               GNUNET_SCHEDULER_TaskCallback task,
1005                                             void *task_cls)
1006 {
1007   return GNUNET_SCHEDULER_add_at_with_priority (GNUNET_TIME_relative_to_absolute (delay),
1008                                                 priority,
1009                                                 task,
1010                                                 task_cls);
1011 }
1012
1013
1014 /**
1015  * Schedule a new task to be run with a specified priority.
1016  *
1017  * @param prio how important is the new task?
1018  * @param task main function of the task
1019  * @param task_cls closure of @a task
1020  * @return unique task identifier for the job
1021  *         only valid until @a task is started!
1022  */
1023 struct GNUNET_SCHEDULER_Task *
1024 GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
1025                                     GNUNET_SCHEDULER_TaskCallback task,
1026                                     void *task_cls)
1027 {
1028   return GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_UNIT_ZERO,
1029                                                      prio,
1030                                                      task,
1031                                                      task_cls);
1032 }
1033
1034
1035 /**
1036  * Schedule a new task to be run at the specified time.  The task
1037  * will be scheduled for execution once specified time has been
1038  * reached. It will be run with the DEFAULT priority.
1039  *
1040  * @param at time at which this operation should run
1041  * @param task main function of the task
1042  * @param task_cls closure of @a task
1043  * @return unique task identifier for the job
1044  *         only valid until @a task is started!
1045  */
1046 struct GNUNET_SCHEDULER_Task *
1047 GNUNET_SCHEDULER_add_at (struct GNUNET_TIME_Absolute at,
1048                          GNUNET_SCHEDULER_TaskCallback task,
1049                          void *task_cls)
1050 {
1051   return GNUNET_SCHEDULER_add_at_with_priority (at,
1052                                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1053                                                 task,
1054                                                 task_cls);
1055 }
1056
1057
1058 /**
1059  * Schedule a new task to be run with a specified delay.  The task
1060  * will be scheduled for execution once the delay has expired. It
1061  * will be run with the DEFAULT priority.
1062  *
1063  * @param delay when should this operation time out?
1064  * @param task main function of the task
1065  * @param task_cls closure of @a task
1066  * @return unique task identifier for the job
1067  *         only valid until @a task is started!
1068  */
1069 struct GNUNET_SCHEDULER_Task *
1070 GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
1071                               GNUNET_SCHEDULER_TaskCallback task,
1072             void *task_cls)
1073 {
1074   return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1075                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1076                  task,
1077                                                      task_cls);
1078 }
1079
1080
1081 /**
1082  * Schedule a new task to be run as soon as possible.  Note that this
1083  * does not guarantee that this will be the next task that is being
1084  * run, as other tasks with higher priority (or that are already ready
1085  * to run) might get to run first.  Just as with delays, clients must
1086  * not rely on any particular order of execution between tasks
1087  * scheduled concurrently.
1088  *
1089  * The task will be run with the DEFAULT priority.
1090  *
1091  * @param task main function of the task
1092  * @param task_cls closure of @a task
1093  * @return unique task identifier for the job
1094  *         only valid until @a task is started!
1095  */
1096 struct GNUNET_SCHEDULER_Task *
1097 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_TaskCallback task,
1098                           void *task_cls)
1099 {
1100   return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO,
1101                                        task,
1102                                        task_cls);
1103 }
1104
1105
1106 /**
1107  * Schedule a new task to be run on shutdown, that is when a CTRL-C
1108  * signal is received, or when #GNUNET_SCHEDULER_shutdown() is being
1109  * invoked.
1110  *
1111  * @param task main function of the task
1112  * @param task_cls closure of @a task
1113  * @return unique task identifier for the job
1114  *         only valid until @a task is started!
1115  */
1116 struct GNUNET_SCHEDULER_Task *
1117 GNUNET_SCHEDULER_add_shutdown (GNUNET_SCHEDULER_TaskCallback task,
1118                                void *task_cls)
1119 {
1120   struct GNUNET_SCHEDULER_Task *t;
1121
1122   GNUNET_assert (NULL != active_task);
1123   GNUNET_assert (NULL != task);
1124   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1125   t->callback = task;
1126   t->callback_cls = task_cls;
1127   t->read_fd = -1;
1128   t->write_fd = -1;
1129 #if PROFILE_DELAYS
1130   t->start_time = GNUNET_TIME_absolute_get ();
1131 #endif
1132   t->timeout = GNUNET_TIME_UNIT_FOREVER_ABS;
1133   t->priority = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
1134   t->on_shutdown = GNUNET_YES;
1135   t->lifeness = GNUNET_YES;
1136   GNUNET_CONTAINER_DLL_insert (shutdown_head,
1137                                shutdown_tail,
1138                                t);
1139   LOG (GNUNET_ERROR_TYPE_WARNING,
1140        "Adding shutdown task: %p\n",
1141        t);
1142   init_backtrace (t);
1143   return t;
1144 }
1145
1146
1147 /**
1148  * Schedule a new task to be run as soon as possible with the
1149  * (transitive) ignore-shutdown flag either explicitly set or
1150  * explicitly enabled.  This task (and all tasks created from it,
1151  * other than by another call to this function) will either count or
1152  * not count for the "lifeness" of the process.  This API is only
1153  * useful in a few special cases.
1154  *
1155  * @param lifeness #GNUNET_YES if the task counts for lifeness, #GNUNET_NO if not.
1156  * @param task main function of the task
1157  * @param task_cls closure of @a task
1158  * @return unique task identifier for the job
1159  *         only valid until @a task is started!
1160  */
1161 struct GNUNET_SCHEDULER_Task *
1162 GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
1163                                         GNUNET_SCHEDULER_TaskCallback task,
1164                                         void *task_cls)
1165 {
1166   struct GNUNET_SCHEDULER_Task *ret;
1167
1168   ret = GNUNET_SCHEDULER_add_now (task, task_cls);
1169   ret->lifeness = lifeness;
1170   return ret;
1171 }
1172
1173
1174 /**
1175  * Schedule a new task to be run with a specified delay or when any of
1176  * the specified file descriptor sets is ready.  The delay can be used
1177  * as a timeout on the socket(s) being ready.  The task will be
1178  * scheduled for execution once either the delay has expired or any of
1179  * the socket operations is ready.  This is the most general
1180  * function of the "add" family.  Note that the "prerequisite_task"
1181  * must be satisfied in addition to any of the other conditions.  In
1182  * other words, the task will be started when
1183  * <code>
1184  * (prerequisite-run)
1185  * && (delay-ready
1186  *     || any-rs-ready
1187  *     || any-ws-ready)
1188  * </code>
1189  *
1190  * @param delay how long should we wait?
1191  * @param priority priority to use
1192  * @param rfd file descriptor we want to read (can be -1)
1193  * @param wfd file descriptors we want to write (can be -1)
1194  * @param task main function of the task
1195  * @param task_cls closure of @a task
1196  * @return unique task identifier for the job
1197  *         only valid until @a task is started!
1198  */
1199 #ifndef MINGW
1200 static struct GNUNET_SCHEDULER_Task *
1201 add_without_sets (struct GNUNET_TIME_Relative delay,
1202                   enum GNUNET_SCHEDULER_Priority priority,
1203                   const struct GNUNET_NETWORK_Handle *read_nh,
1204                   const struct GNUNET_NETWORK_Handle *write_nh,
1205                   const struct GNUNET_DISK_FileHandle *read_fh,
1206                   const struct GNUNET_DISK_FileHandle *write_fh,
1207                   GNUNET_SCHEDULER_TaskCallback task,
1208                   void *task_cls)
1209 {
1210   struct GNUNET_SCHEDULER_Task *t;
1211
1212   GNUNET_assert (NULL != active_task);
1213   GNUNET_assert (NULL != task);
1214   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1215   init_fd_info (t, read_nh, write_nh, read_fh, write_fh);
1216   t->callback = task;
1217   t->callback_cls = task_cls;
1218 #if DEBUG_FDS
1219   if (-1 != rfd)
1220   {
1221     int flags = fcntl (rfd, F_GETFD);
1222
1223     if ((flags == -1) && (errno == EBADF))
1224     {
1225       LOG (GNUNET_ERROR_TYPE_ERROR,
1226            "Got invalid file descriptor %d!\n",
1227            rfd);
1228       init_backtrace (t);
1229       GNUNET_assert (0);
1230     }
1231   }
1232   if (-1 != wfd)
1233   {
1234     int flags = fcntl (wfd, F_GETFD);
1235
1236     if (flags == -1 && errno == EBADF)
1237     {
1238       LOG (GNUNET_ERROR_TYPE_ERROR,
1239            "Got invalid file descriptor %d!\n",
1240            wfd);
1241       init_backtrace (t);
1242       GNUNET_assert (0);
1243     }
1244   }
1245 #endif
1246 #if PROFILE_DELAYS
1247   t->start_time = GNUNET_TIME_absolute_get ();
1248 #endif
1249   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1250   t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
1251   t->lifeness = current_lifeness;
1252   GNUNET_CONTAINER_DLL_insert (pending_head,
1253                                pending_tail,
1254                                t);
1255   scheduler_multi_function_call(t, scheduler_driver->add);
1256   scheduler_driver->set_wakeup (scheduler_driver->cls,
1257                                 get_timeout ());
1258   max_priority_added = GNUNET_MAX (max_priority_added,
1259                                    t->priority);
1260   LOG (GNUNET_ERROR_TYPE_DEBUG,
1261        "Adding task %p\n",
1262        t);
1263   init_backtrace (t);
1264   return t;
1265 }
1266 #endif
1267
1268
1269 /**
1270  * Schedule a new task to be run with a specified delay or when the
1271  * specified file descriptor is ready for reading.  The delay can be
1272  * used as a timeout on the socket being ready.  The task will be
1273  * scheduled for execution once either the delay has expired or the
1274  * socket operation is ready.  It will be run with the DEFAULT priority.
1275  *
1276  * @param delay when should this operation time out?
1277  * @param rfd read file-descriptor
1278  * @param task main function of the task
1279  * @param task_cls closure of @a task
1280  * @return unique task identifier for the job
1281  *         only valid until @a task is started!
1282  */
1283 struct GNUNET_SCHEDULER_Task *
1284 GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
1285                                struct GNUNET_NETWORK_Handle *rfd,
1286                                GNUNET_SCHEDULER_TaskCallback task,
1287                                void *task_cls)
1288 {
1289   return GNUNET_SCHEDULER_add_read_net_with_priority (delay,
1290                   GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1291                   rfd, task, task_cls);
1292 }
1293
1294
1295 /**
1296  * Schedule a new task to be run with a specified priority and to be
1297  * run after the specified delay or when the specified file descriptor
1298  * is ready for reading.  The delay can be used as a timeout on the
1299  * socket being ready.  The task will be scheduled for execution once
1300  * either the delay has expired or the socket operation is ready.  It
1301  * will be run with the DEFAULT priority.
1302  *
1303  * @param delay when should this operation time out?
1304  * @param priority priority to use for the task
1305  * @param rfd read file-descriptor
1306  * @param task main function of the task
1307  * @param task_cls closure of @a task
1308  * @return unique task identifier for the job
1309  *         only valid until @a task is started!
1310  */
1311 struct GNUNET_SCHEDULER_Task *
1312 GNUNET_SCHEDULER_add_read_net_with_priority (struct GNUNET_TIME_Relative delay,
1313                enum GNUNET_SCHEDULER_Priority priority,
1314                struct GNUNET_NETWORK_Handle *rfd,
1315                GNUNET_SCHEDULER_TaskCallback task,
1316                                              void *task_cls)
1317 {
1318   return GNUNET_SCHEDULER_add_net_with_priority (delay, priority,
1319                                                  rfd,
1320                                                  GNUNET_YES,
1321                                                  GNUNET_NO,
1322                                                  task, task_cls);
1323 }
1324
1325
1326 /**
1327  * Schedule a new task to be run with a specified delay or when the
1328  * specified file descriptor is ready for writing.  The delay can be
1329  * used as a timeout on the socket being ready.  The task will be
1330  * scheduled for execution once either the delay has expired or the
1331  * socket operation is ready.  It will be run with the priority of
1332  * the calling task.
1333  *
1334  * @param delay when should this operation time out?
1335  * @param wfd write file-descriptor
1336  * @param task main function of the task
1337  * @param task_cls closure of @a task
1338  * @return unique task identifier for the job
1339  *         only valid until @a task is started!
1340  */
1341 struct GNUNET_SCHEDULER_Task *
1342 GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
1343                                 struct GNUNET_NETWORK_Handle *wfd,
1344                                 GNUNET_SCHEDULER_TaskCallback task,
1345                                 void *task_cls)
1346 {
1347   return GNUNET_SCHEDULER_add_net_with_priority (delay,
1348                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1349                                                  wfd,
1350                                                  GNUNET_NO, GNUNET_YES,
1351                                                  task, task_cls);
1352 }
1353
1354 /**
1355  * Schedule a new task to be run with a specified delay or when the
1356  * specified file descriptor is ready.  The delay can be
1357  * used as a timeout on the socket being ready.  The task will be
1358  * scheduled for execution once either the delay has expired or the
1359  * socket operation is ready.
1360  *
1361  * @param delay when should this operation time out?
1362  * @param priority priority of the task
1363  * @param fd file-descriptor
1364  * @param on_read whether to poll the file-descriptor for readability
1365  * @param on_write whether to poll the file-descriptor for writability
1366  * @param task main function of the task
1367  * @param task_cls closure of task
1368  * @return unique task identifier for the job
1369  *         only valid until "task" is started!
1370  */
1371 struct GNUNET_SCHEDULER_Task *
1372 GNUNET_SCHEDULER_add_net_with_priority  (struct GNUNET_TIME_Relative delay,
1373                                          enum GNUNET_SCHEDULER_Priority priority,
1374                                          struct GNUNET_NETWORK_Handle *fd,
1375                                          int on_read,
1376                                          int on_write,
1377                                          GNUNET_SCHEDULER_TaskCallback task,
1378                                          void *task_cls)
1379 {
1380 #if MINGW
1381   struct GNUNET_NETWORK_FDSet *s;
1382   struct GNUNET_SCHEDULER_Task * ret;
1383
1384   GNUNET_assert (NULL != fd);
1385   s = GNUNET_NETWORK_fdset_create ();
1386   GNUNET_NETWORK_fdset_set (s, fd);
1387   ret = GNUNET_SCHEDULER_add_select (
1388       priority, delay,
1389       on_read  ? s : NULL,
1390       on_write ? s : NULL,
1391       task, task_cls);
1392   GNUNET_NETWORK_fdset_destroy (s);
1393   return ret;
1394 #else
1395   GNUNET_assert (on_read || on_write);
1396   GNUNET_assert (GNUNET_NETWORK_get_fd (fd) >= 0);
1397   return add_without_sets (delay, priority,
1398                            on_read  ? fd : NULL,
1399                            on_write ? fd : NULL,
1400                            NULL,
1401                            NULL,
1402                            task, task_cls);
1403 #endif
1404 }
1405
1406
1407 /**
1408  * Schedule a new task to be run with a specified delay or when the
1409  * specified file descriptor is ready for reading.  The delay can be
1410  * used as a timeout on the socket being ready.  The task will be
1411  * scheduled for execution once either the delay has expired or the
1412  * socket operation is ready. It will be run with the DEFAULT priority.
1413  *
1414  * @param delay when should this operation time out?
1415  * @param rfd read file-descriptor
1416  * @param task main function of the task
1417  * @param task_cls closure of @a task
1418  * @return unique task identifier for the job
1419  *         only valid until @a task is started!
1420  */
1421 struct GNUNET_SCHEDULER_Task *
1422 GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
1423                                 const struct GNUNET_DISK_FileHandle *rfd,
1424                                 GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1425 {
1426   return GNUNET_SCHEDULER_add_file_with_priority (
1427       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1428       rfd, GNUNET_YES, GNUNET_NO,
1429       task, task_cls);
1430 }
1431
1432
1433 /**
1434  * Schedule a new task to be run with a specified delay or when the
1435  * specified file descriptor is ready for writing.  The delay can be
1436  * used as a timeout on the socket being ready.  The task will be
1437  * scheduled for execution once either the delay has expired or the
1438  * socket operation is ready. It will be run with the DEFAULT priority.
1439  *
1440  * @param delay when should this operation time out?
1441  * @param wfd write file-descriptor
1442  * @param task main function of the task
1443  * @param task_cls closure of @a task
1444  * @return unique task identifier for the job
1445  *         only valid until @a task is started!
1446  */
1447 struct GNUNET_SCHEDULER_Task *
1448 GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
1449                                  const struct GNUNET_DISK_FileHandle *wfd,
1450                                  GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1451 {
1452   return GNUNET_SCHEDULER_add_file_with_priority (
1453       delay, GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1454       wfd, GNUNET_NO, GNUNET_YES,
1455       task, task_cls);
1456 }
1457
1458
1459 /**
1460  * Schedule a new task to be run with a specified delay or when the
1461  * specified file descriptor is ready.  The delay can be
1462  * used as a timeout on the socket being ready.  The task will be
1463  * scheduled for execution once either the delay has expired or the
1464  * socket operation is ready.
1465  *
1466  * @param delay when should this operation time out?
1467  * @param priority priority of the task
1468  * @param fd file-descriptor
1469  * @param on_read whether to poll the file-descriptor for readability
1470  * @param on_write whether to poll the file-descriptor for writability
1471  * @param task main function of the task
1472  * @param task_cls closure of @a task
1473  * @return unique task identifier for the job
1474  *         only valid until @a task is started!
1475  */
1476 struct GNUNET_SCHEDULER_Task *
1477 GNUNET_SCHEDULER_add_file_with_priority (struct GNUNET_TIME_Relative delay,
1478                                          enum GNUNET_SCHEDULER_Priority priority,
1479                                          const struct GNUNET_DISK_FileHandle *fd,
1480                                          int on_read, int on_write,
1481                                          GNUNET_SCHEDULER_TaskCallback task, void *task_cls)
1482 {
1483 #if MINGW
1484   struct GNUNET_NETWORK_FDSet *s;
1485   struct GNUNET_SCHEDULER_Task * ret;
1486
1487   GNUNET_assert (NULL != fd);
1488   s = GNUNET_NETWORK_fdset_create ();
1489   GNUNET_NETWORK_fdset_handle_set (s, fd);
1490   ret = GNUNET_SCHEDULER_add_select (
1491       priority, delay,
1492       on_read  ? s : NULL,
1493       on_write ? s : NULL,
1494       task, task_cls);
1495   GNUNET_NETWORK_fdset_destroy (s);
1496   return ret;
1497 #else
1498   GNUNET_assert (on_read || on_write);
1499   GNUNET_assert (fd->fd >= 0);
1500   return add_without_sets (delay, priority,
1501                            NULL,
1502                            NULL,
1503                            on_read ? fd : NULL,
1504                            on_write ? fd : NULL,
1505                            task, task_cls);
1506 #endif
1507 }
1508
1509
1510 /**
1511  * Schedule a new task to be run with a specified delay or when any of
1512  * the specified file descriptor sets is ready.  The delay can be used
1513  * as a timeout on the socket(s) being ready.  The task will be
1514  * scheduled for execution once either the delay has expired or any of
1515  * the socket operations is ready.  This is the most general
1516  * function of the "add" family.  Note that the "prerequisite_task"
1517  * must be satisfied in addition to any of the other conditions.  In
1518  * other words, the task will be started when
1519  * <code>
1520  * (prerequisite-run)
1521  * && (delay-ready
1522  *     || any-rs-ready
1523  *     || any-ws-ready) )
1524  * </code>
1525  *
1526  * @param prio how important is this task?
1527  * @param delay how long should we wait?
1528  * @param rs set of file descriptors we want to read (can be NULL)
1529  * @param ws set of file descriptors we want to write (can be NULL)
1530  * @param task main function of the task
1531  * @param task_cls closure of @a task
1532  * @return unique task identifier for the job
1533  *         only valid until @a task is started!
1534  */
1535 struct GNUNET_SCHEDULER_Task *
1536 GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
1537                              struct GNUNET_TIME_Relative delay,
1538                              const struct GNUNET_NETWORK_FDSet *rs,
1539                              const struct GNUNET_NETWORK_FDSet *ws,
1540                              GNUNET_SCHEDULER_TaskCallback task,
1541                              void *task_cls)
1542 {
1543   struct GNUNET_SCHEDULER_Task *t;
1544
1545   if ( (NULL == rs) &&
1546        (NULL == ws) )
1547     return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
1548                                                        prio,
1549                                                        task,
1550                                                        task_cls);
1551   GNUNET_assert (NULL != active_task);
1552   GNUNET_assert (NULL != task);
1553   t = GNUNET_new (struct GNUNET_SCHEDULER_Task);
1554   t->callback = task;
1555   t->callback_cls = task_cls;
1556   t->read_fd = -1;
1557   t->write_fd = -1;
1558   if (NULL != rs)
1559   {
1560     t->read_set = GNUNET_NETWORK_fdset_create ();
1561     GNUNET_NETWORK_fdset_copy (t->read_set, rs);
1562   }
1563   if (NULL != ws)
1564   {
1565     t->write_set = GNUNET_NETWORK_fdset_create ();
1566     GNUNET_NETWORK_fdset_copy (t->write_set, ws);
1567   }
1568 #if PROFILE_DELAYS
1569   t->start_time = GNUNET_TIME_absolute_get ();
1570 #endif
1571   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
1572   t->priority =
1573       check_priority ((prio ==
1574                        GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
1575                       prio);
1576   t->lifeness = current_lifeness;
1577   GNUNET_CONTAINER_DLL_insert (pending_head,
1578                                pending_tail,
1579                                t);
1580   scheduler_multi_function_call(t, scheduler_driver->add);
1581   scheduler_driver->set_wakeup (scheduler_driver->cls,
1582                                 get_timeout ());
1583   max_priority_added = GNUNET_MAX (max_priority_added,
1584            t->priority);
1585   LOG (GNUNET_ERROR_TYPE_WARNING,
1586        "Adding task %p\n",
1587        t);
1588   init_backtrace (t);
1589   return t;
1590 }
1591
1592
1593 /**
1594  * Function used by event-loop implementations to signal the scheduler
1595  * that a particular @a task is ready due to an event of type @a et.
1596  *
1597  * This function will then queue the task to notify the application
1598  * that the task is ready (with the respective priority).
1599  *
1600  * @param task the task that is ready, NULL for wake up calls
1601  * @param et information about why the task is ready
1602  */
1603 void
1604 GNUNET_SCHEDULER_task_ready (struct GNUNET_SCHEDULER_Task *task,
1605                              enum GNUNET_SCHEDULER_EventType et)
1606 {
1607   enum GNUNET_SCHEDULER_Reason reason;
1608   struct GNUNET_TIME_Absolute now;
1609
1610   LOG (GNUNET_ERROR_TYPE_WARNING,
1611        "task ready: %p\n",
1612        task);
1613
1614   now = GNUNET_TIME_absolute_get ();
1615   reason = task->reason;
1616   if (now.abs_value_us >= task->timeout.abs_value_us)
1617     reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1618   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
1619        (0 != (GNUNET_SCHEDULER_ET_IN & et)) )
1620     reason |= GNUNET_SCHEDULER_REASON_READ_READY;
1621   if ( (0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
1622        (0 != (GNUNET_SCHEDULER_ET_OUT & et)) )
1623     reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
1624   reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
1625   task->reason = reason;
1626   task->fds = &task->fdx; // FIXME: if task contains a list of fds, this is wrong!
1627   task->fdx.et = et;
1628   task->fds_len = 1;
1629   GNUNET_CONTAINER_DLL_remove (pending_head,
1630                                pending_tail,
1631                                task);
1632   queue_ready_task (task);
1633 }
1634
1635
1636 /**
1637  * Function called by the driver to tell the scheduler to run some of
1638  * the tasks that are ready.  This function may return even though
1639  * there are tasks left to run just to give other tasks a chance as
1640  * well.  If we return #GNUNET_YES, the driver should call this
1641  * function again as soon as possible, while if we return #GNUNET_NO
1642  * it must block until the operating system has more work as the
1643  * scheduler has no more work to do right now.
1644  *
1645  * @param sh scheduler handle that was given to the `loop`
1646  * @return #GNUNET_OK if there are more tasks that are ready,
1647  *          and thus we would like to run more (yield to avoid
1648  *          blocking other activities for too long)
1649  *         #GNUNET_NO if we are done running tasks (yield to block)
1650  *         #GNUNET_SYSERR on error
1651  */
1652 int
1653 GNUNET_SCHEDULER_run_from_driver (struct GNUNET_SCHEDULER_Handle *sh)
1654 {
1655   enum GNUNET_SCHEDULER_Priority p;
1656   struct GNUNET_SCHEDULER_Task *pos;
1657   struct GNUNET_TIME_Absolute now;
1658
1659   /* check for tasks that reached the timeout! */
1660   now = GNUNET_TIME_absolute_get ();
1661   while (NULL != (pos = pending_timeout_head))
1662   {
1663     if (now.abs_value_us >= pos->timeout.abs_value_us)
1664       pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
1665     if (0 == pos->reason)
1666       break;
1667     GNUNET_CONTAINER_DLL_remove (pending_timeout_head,
1668                                  pending_timeout_tail,
1669                                  pos);
1670     if (pending_timeout_last == pos)
1671       pending_timeout_last = NULL;
1672     queue_ready_task (pos);
1673   }
1674
1675   if (0 == ready_count)
1676     return GNUNET_NO;
1677
1678   /* find out which task priority level we are going to
1679      process this time */
1680   max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
1681   GNUNET_assert (NULL == ready_head[GNUNET_SCHEDULER_PRIORITY_KEEP]);
1682   /* yes, p>0 is correct, 0 is "KEEP" which should
1683    * always be an empty queue (see assertion)! */
1684   for (p = GNUNET_SCHEDULER_PRIORITY_COUNT - 1; p > 0; p--)
1685   {
1686     pos = ready_head[p];
1687     if (NULL != pos)
1688       break;
1689   }
1690   GNUNET_assert (NULL != pos);        /* ready_count wrong? */
1691
1692   /* process all tasks at this priority level, then yield */
1693   while (NULL != (pos = ready_head[p]))
1694   {
1695     GNUNET_CONTAINER_DLL_remove (ready_head[p],
1696          ready_tail[p],
1697          pos);
1698     ready_count--;
1699     current_priority = pos->priority;
1700     current_lifeness = pos->lifeness;
1701     active_task = pos;
1702 #if PROFILE_DELAYS
1703     if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
1704         DELAY_THRESHOLD.rel_value_us)
1705     {
1706       LOG (GNUNET_ERROR_TYPE_WARNING,
1707            "Task %p took %s to be scheduled\n",
1708            pos,
1709            GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time),
1710                                                    GNUNET_YES));
1711     }
1712 #endif
1713     tc.reason = pos->reason;
1714     GNUNET_NETWORK_fdset_zero (sh->rs);
1715     GNUNET_NETWORK_fdset_zero (sh->ws);
1716     tc.fds_len = pos->fds_len;
1717     tc.fds = pos->fds;
1718     tc.read_ready = (NULL == pos->read_set) ? sh->rs : pos->read_set;
1719     if ( (-1 != pos->read_fd) &&
1720          (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)) )
1721       GNUNET_NETWORK_fdset_set_native (sh->rs,
1722                                        pos->read_fd);
1723     tc.write_ready = (NULL == pos->write_set) ? sh->ws : pos->write_set;
1724     if ( (-1 != pos->write_fd) &&
1725          (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) )
1726       GNUNET_NETWORK_fdset_set_native (sh->ws,
1727                                        pos->write_fd);
1728     LOG (GNUNET_ERROR_TYPE_WARNING,
1729          "Running task from driver: %p\n",
1730          pos);
1731     pos->callback (pos->callback_cls);
1732     active_task = NULL;
1733     dump_backtrace (pos);
1734     destroy_task (pos);
1735     tasks_run++;
1736   }
1737   if (GNUNET_NO == check_lifeness ())
1738   {
1739     /* cancel fd tasks without lifeness */
1740     pos = pending_head;
1741     while (NULL != pos)
1742     {
1743       struct GNUNET_SCHEDULER_Task *tsk = pos;
1744       pos = pos->next;
1745       if (GNUNET_NO == tsk->lifeness)
1746       {
1747         GNUNET_SCHEDULER_cancel (tsk);
1748       }
1749     }
1750     //sighandler_shutdown();
1751   }
1752   if (0 == ready_count)
1753   {
1754     scheduler_driver->set_wakeup (scheduler_driver->cls,
1755                                   get_timeout ());
1756     return GNUNET_NO;
1757   }
1758   scheduler_driver->set_wakeup (scheduler_driver->cls,
1759                                 GNUNET_TIME_absolute_get ()); 
1760   return GNUNET_OK;
1761 }
1762
1763
1764 /**
1765  * Initialize and run scheduler.  This function will return when all
1766  * tasks have completed.  On systems with signals, receiving a SIGTERM
1767  * (and other similar signals) will cause #GNUNET_SCHEDULER_shutdown
1768  * to be run after the active task is complete.  As a result, SIGTERM
1769  * causes all shutdown tasks to be scheduled with reason
1770  * #GNUNET_SCHEDULER_REASON_SHUTDOWN.  (However, tasks added
1771  * afterwards will execute normally!).  Note that any particular
1772  * signal will only shut down one scheduler; applications should
1773  * always only create a single scheduler.
1774  *
1775  * @param driver drive to use for the event loop
1776  * @param task task to run first (and immediately)
1777  * @param task_cls closure of @a task
1778  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1779  */
1780 int
1781 GNUNET_SCHEDULER_run_with_driver (const struct GNUNET_SCHEDULER_Driver *driver,
1782                                   GNUNET_SCHEDULER_TaskCallback task,
1783                                   void *task_cls)
1784 {
1785   int ret;
1786   struct GNUNET_SIGNAL_Context *shc_int;
1787   struct GNUNET_SIGNAL_Context *shc_term;
1788 #if (SIGTERM != GNUNET_TERM_SIG)
1789   struct GNUNET_SIGNAL_Context *shc_gterm;
1790 #endif
1791 #ifndef MINGW
1792   struct GNUNET_SIGNAL_Context *shc_quit;
1793   struct GNUNET_SIGNAL_Context *shc_hup;
1794   struct GNUNET_SIGNAL_Context *shc_pipe;
1795 #endif
1796   struct GNUNET_SCHEDULER_Task tsk;
1797   const struct GNUNET_DISK_FileHandle *pr;
1798
1799   /* general set-up */
1800   GNUNET_assert (NULL == active_task);
1801   GNUNET_assert (NULL == shutdown_pipe_handle);
1802   shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO,
1803                                            GNUNET_NO,
1804                                            GNUNET_NO,
1805                                            GNUNET_NO);
1806   GNUNET_assert (NULL != shutdown_pipe_handle);
1807   pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
1808                                 GNUNET_DISK_PIPE_END_READ);
1809   my_pid = getpid ();
1810   scheduler_driver = driver;
1811
1812   /* install signal handlers */
1813   LOG (GNUNET_ERROR_TYPE_WARNING,
1814        "Registering signal handlers\n");
1815   shc_int = GNUNET_SIGNAL_handler_install (SIGINT,
1816              &sighandler_shutdown);
1817   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM,
1818               &sighandler_shutdown);
1819 #if (SIGTERM != GNUNET_TERM_SIG)
1820   shc_gterm = GNUNET_SIGNAL_handler_install (GNUNET_TERM_SIG,
1821                &sighandler_shutdown);
1822 #endif
1823 #ifndef MINGW
1824   shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE,
1825               &sighandler_pipe);
1826   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT,
1827               &sighandler_shutdown);
1828   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP,
1829              &sighandler_shutdown);
1830 #endif
1831
1832   /* Setup initial tasks */
1833   //current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
1834   //current_lifeness = GNUNET_YES;
1835   //memset (&tsk,
1836   //        0,
1837   //        sizeof (tsk));
1838   //active_task = &tsk;
1839   //GNUNET_SCHEDULER_add_with_reason_and_priority (task,
1840   //                                               task_cls,
1841   //                                               GNUNET_SCHEDULER_REASON_STARTUP,
1842   //                                               GNUNET_SCHEDULER_PRIORITY_DEFAULT);
1843   //GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
1844   //                                        &GNUNET_OS_install_parent_control_handler,
1845   //                                        NULL);
1846   //GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1847   //                                pr,
1848   //                                &shutdown_task,
1849   //                                NULL);
1850   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
1851   current_lifeness = GNUNET_NO;
1852   memset (&tsk,
1853     0,
1854     sizeof (tsk));
1855   active_task = &tsk;
1856   GNUNET_SCHEDULER_add_now (&GNUNET_OS_install_parent_control_handler,
1857                             NULL);
1858   GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
1859                                   pr,
1860                                   &shutdown_task,
1861                                   NULL);
1862   current_lifeness = GNUNET_YES;
1863   GNUNET_SCHEDULER_add_with_reason_and_priority (task,
1864                                                  task_cls,
1865                                                  GNUNET_SCHEDULER_REASON_STARTUP,
1866                                                  GNUNET_SCHEDULER_PRIORITY_DEFAULT);
1867   active_task = NULL;
1868   /* begin main event loop */
1869   sh.rs = GNUNET_NETWORK_fdset_create ();
1870   sh.ws = GNUNET_NETWORK_fdset_create ();
1871   GNUNET_NETWORK_fdset_handle_set (sh.rs, pr);
1872   ret = driver->loop (driver->cls,
1873                       &sh);
1874   LOG (GNUNET_ERROR_TYPE_WARNING,
1875        "loop finished!\n");
1876   GNUNET_NETWORK_fdset_destroy (sh.rs);
1877   GNUNET_NETWORK_fdset_destroy (sh.ws);
1878
1879   /* uninstall signal handlers */
1880   GNUNET_SIGNAL_handler_uninstall (shc_int);
1881   GNUNET_SIGNAL_handler_uninstall (shc_term);
1882 #if (SIGTERM != GNUNET_TERM_SIG)
1883   GNUNET_SIGNAL_handler_uninstall (shc_gterm);
1884 #endif
1885 #ifndef MINGW
1886   GNUNET_SIGNAL_handler_uninstall (shc_pipe);
1887   GNUNET_SIGNAL_handler_uninstall (shc_quit);
1888   GNUNET_SIGNAL_handler_uninstall (shc_hup);
1889 #endif
1890   GNUNET_DISK_pipe_close (shutdown_pipe_handle);
1891   shutdown_pipe_handle = NULL;
1892   return ret;
1893 }
1894
1895
1896 int
1897 select_add (void *cls,
1898             struct GNUNET_SCHEDULER_Task *task,
1899             struct GNUNET_SCHEDULER_FdInfo *fdi)
1900 {
1901   struct DriverContext *context = cls;
1902   GNUNET_assert (NULL != context);
1903
1904   if (!((NULL != fdi->fd) ^ (NULL != fdi->fh)) || (0 >= fdi->sock))
1905   {
1906     /* exactly one out of {fd, hf} must be != NULL and the OS handle must be valid */
1907     return GNUNET_SYSERR;
1908   }
1909
1910   struct Scheduled *scheduled = GNUNET_new (struct Scheduled);
1911   scheduled->task = task;
1912   scheduled->fdi = fdi;
1913
1914   switch (fdi->et)
1915   {
1916   case GNUNET_SCHEDULER_ET_IN:
1917   {
1918     GNUNET_CONTAINER_DLL_insert (context->scheduled_in_head,
1919                                  context->scheduled_in_tail,
1920                                  scheduled);
1921     break;
1922   }
1923   case GNUNET_SCHEDULER_ET_OUT:
1924   {
1925     GNUNET_CONTAINER_DLL_insert (context->scheduled_out_head,
1926                                  context->scheduled_out_tail,
1927                                  scheduled);
1928     break;
1929   }
1930   default:
1931   {
1932     // FIXME: other event types not implemented yet
1933     GNUNET_assert (0);
1934   }
1935   }
1936   return GNUNET_OK;
1937 }
1938
1939
1940 int
1941 select_del (void *cls,
1942             struct GNUNET_SCHEDULER_Task *task,
1943             struct GNUNET_SCHEDULER_FdInfo *fdi)
1944 {
1945   struct DriverContext *context = cls;
1946   GNUNET_assert (NULL != context);
1947
1948   int ret = GNUNET_SYSERR;
1949   struct Scheduled *pos;
1950   // FIXME: are multiple ORed event types allowed?
1951   switch (fdi->et)
1952   {
1953   case GNUNET_SCHEDULER_ET_IN:
1954   {
1955     for (pos = context->scheduled_in_head; NULL != pos; pos = pos->next)
1956     {
1957       if (pos->task == task)
1958       {
1959         GNUNET_CONTAINER_DLL_remove (context->scheduled_in_head,
1960                                      context->scheduled_in_tail,
1961                                      pos);
1962         ret = GNUNET_OK;
1963       }
1964     }
1965     break; 
1966   }
1967   case GNUNET_SCHEDULER_ET_OUT:
1968   {
1969     for (pos = context->scheduled_out_head; NULL != pos; pos = pos->next)
1970     {
1971       if (pos->task == task)
1972       {
1973         GNUNET_CONTAINER_DLL_remove (context->scheduled_out_head,
1974                                      context->scheduled_out_tail,
1975                                      pos);
1976         ret = GNUNET_OK;
1977       }
1978     }
1979     break;
1980   }
1981   default:
1982   {
1983     // FIXME: other event types not implemented yet
1984     GNUNET_assert (0);
1985   }
1986   }
1987   return ret;
1988 }
1989
1990
1991 int
1992 select_loop (void *cls,
1993              struct GNUNET_SCHEDULER_Handle *sh)
1994 {
1995   struct GNUNET_NETWORK_FDSet *rs;
1996   struct GNUNET_NETWORK_FDSet *ws;
1997   struct DriverContext *context;
1998   int select_result;
1999   unsigned long long last_tr;
2000   unsigned int busy_wait_warning;
2001   
2002   context = cls;
2003   GNUNET_assert (NULL != context);
2004   rs = GNUNET_NETWORK_fdset_create ();
2005   ws = GNUNET_NETWORK_fdset_create ();
2006   last_tr = 0;
2007   busy_wait_warning = 0;
2008   while ((NULL != context->scheduled_in_head) || (NULL != context->scheduled_out_head))
2009   {
2010     GNUNET_NETWORK_fdset_zero (rs);
2011     GNUNET_NETWORK_fdset_zero (ws);
2012     struct Scheduled *pos;
2013     for (pos = context->scheduled_in_head; NULL != pos; pos = pos->next)
2014     {
2015       GNUNET_NETWORK_fdset_set_native (rs, pos->fdi->sock);
2016     }
2017     for (pos = context->scheduled_out_head; NULL != pos; pos = pos->next)
2018     {
2019       GNUNET_NETWORK_fdset_set_native (ws, pos->fdi->sock);
2020     }
2021     if (NULL == scheduler_select)
2022     {
2023       select_result = GNUNET_NETWORK_socket_select (rs,
2024                                                     ws,
2025                                                     NULL,
2026                                                     context->timeout);
2027     }
2028     else
2029     {
2030       select_result = scheduler_select (scheduler_select_cls,
2031                                         rs,
2032                                         ws,
2033                                         NULL,
2034                                         context->timeout);
2035     }
2036     if (select_result == GNUNET_SYSERR)
2037     {
2038       if (errno == EINTR)
2039         continue;
2040
2041       LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
2042 #ifndef MINGW
2043 #if USE_LSOF
2044       char lsof[512];
2045
2046       snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
2047       (void) close (1);
2048       (void) dup2 (2, 1);
2049       if (0 != system (lsof))
2050         LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
2051                       "system");
2052 #endif
2053 #endif
2054 #if DEBUG_FDS
2055       struct GNUNET_SCHEDULER_Task *t;
2056       for (t = pending_head; NULL != t; t = t->next)
2057       {
2058         if (-1 != t->read_fd)
2059         {
2060           int flags = fcntl (t->read_fd, F_GETFD);
2061           if ((flags == -1) && (errno == EBADF))
2062           {
2063             LOG (GNUNET_ERROR_TYPE_ERROR,
2064                  "Got invalid file descriptor %d!\n",
2065                  t->read_fd);
2066             dump_backtrace (t);
2067           }
2068         }
2069         if (-1 != t->write_fd)
2070         {
2071           int flags = fcntl (t->write_fd, F_GETFD);
2072           if ((flags == -1) && (errno == EBADF))
2073           {
2074             LOG (GNUNET_ERROR_TYPE_ERROR,
2075                  "Got invalid file descriptor %d!\n",
2076                  t->write_fd);
2077             dump_backtrace (t);
2078           }
2079         }
2080       }
2081 #endif
2082       GNUNET_assert (0);
2083       return GNUNET_SYSERR;
2084     }
2085     if ( (0 == select_result) &&
2086          (0 == context->timeout.rel_value_us) &&
2087          (busy_wait_warning > 16) )
2088     {
2089       LOG (GNUNET_ERROR_TYPE_WARNING,
2090            "Looks like we're busy waiting...\n");
2091       short_wait (100);                /* mitigate */
2092     }
2093     for (pos = context->scheduled_in_head; NULL != pos; pos = pos->next)
2094     {
2095       if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, pos->fdi->sock))
2096       {
2097         GNUNET_CONTAINER_DLL_remove (context->scheduled_in_head,
2098                                      context->scheduled_in_tail,
2099                                      pos);
2100         GNUNET_SCHEDULER_task_ready (pos->task, GNUNET_SCHEDULER_ET_IN);
2101       }
2102     }
2103     for (pos = context->scheduled_out_head; NULL != pos; pos = pos->next)
2104     {
2105       if (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, pos->fdi->sock))
2106       {
2107         GNUNET_CONTAINER_DLL_remove (context->scheduled_out_head,
2108                                      context->scheduled_out_tail,
2109                                      pos);
2110         GNUNET_SCHEDULER_task_ready (pos->task, GNUNET_SCHEDULER_ET_OUT);
2111       }
2112     }
2113     GNUNET_SCHEDULER_run_from_driver (sh);
2114     if (last_tr == tasks_run)
2115     {
2116       short_wait (1);
2117       busy_wait_warning++;
2118     }
2119     else
2120     {
2121       last_tr = tasks_run;
2122       busy_wait_warning = 0;
2123     }
2124     LOG (GNUNET_ERROR_TYPE_WARNING,
2125          "scheduled_in_head = %p, scheduled_out_head = %p\n",
2126          context->scheduled_in_head,
2127          context->scheduled_out_head);
2128   }
2129   return GNUNET_OK; 
2130 }
2131
2132
2133 void
2134 select_set_wakeup(void *cls,
2135                   struct GNUNET_TIME_Absolute dt)
2136 {
2137   struct DriverContext *context = cls;
2138   GNUNET_assert (NULL != context);
2139  
2140   context->timeout = GNUNET_TIME_absolute_get_remaining (dt);
2141 }
2142
2143
2144 /**
2145  * Obtain the driver for using select() as the event loop.
2146  *
2147  * @return NULL on error
2148  */
2149 struct GNUNET_SCHEDULER_Driver *
2150 GNUNET_SCHEDULER_driver_select ()
2151 {
2152   struct GNUNET_SCHEDULER_Driver *select_driver;
2153   select_driver = GNUNET_new (struct GNUNET_SCHEDULER_Driver);
2154
2155   select_driver->loop = &select_loop;
2156   select_driver->add = &select_add;
2157   select_driver->del = &select_del;
2158   select_driver->set_wakeup = &select_set_wakeup;
2159
2160   return select_driver;
2161 }
2162
2163
2164 /* end of scheduler.c */