asserts
[oweals/gnunet.git] / src / util / scheduler.c
index 308257abeacb0e984a7557af0833c8d755efeb8e..c46c895009e565f247de1efbbda22cf9a2cdf8cf 100644 (file)
  */
 #include "platform.h"
 #include "gnunet_common.h"
+#include "gnunet_os_lib.h"
 #include "gnunet_scheduler_lib.h"
 #include "gnunet_signal_lib.h"
 #include "gnunet_time_lib.h"
+#ifdef LINUX
+#include "execinfo.h"
+#define EXECINFO GNUNET_NO
+#define MAX_TRACE_DEPTH 50
+#endif
+
+#define DEBUG_TASKS GNUNET_NO
 
 /**
  * Linked list of pending tasks.
@@ -58,10 +66,9 @@ struct Task
   struct GNUNET_NETWORK_FDSet *read_set;
 
   /**
-   * Set of file descriptors this task is waiting
-   * for for writing.  Once ready, this is updated
-   * to reflect the set of file descriptors ready
-   * for operation.
+   * Set of file descriptors this task is waiting for for writing.
+   * Once ready, this is updated to reflect the set of file
+   * descriptors ready for operation.
    */
   struct GNUNET_NETWORK_FDSet *write_set;
 
@@ -93,10 +100,18 @@ struct Task
    */
   enum GNUNET_SCHEDULER_Priority priority;
 
+#if EXECINFO
   /**
-   * Should this task be run on shutdown?
+   * Array of strings which make up a backtrace from the point when this
+   * task was scheduled (essentially, who scheduled the task?)
    */
-  int run_on_shutdown;
+  char **backtrace_strings;
+
+  /**
+   * Size of the backtrace_strings array
+   */
+  int num_backtrace_strings;
+#endif
 
 };
 
@@ -112,6 +127,11 @@ struct GNUNET_SCHEDULER_Handle
    */
   struct Task *pending;
 
+  /**
+   * ID of the task that is running right now.
+   */
+  struct Task *active_task;
+
   /**
    * List of tasks ready to run right now,
    * grouped by importance.
@@ -132,12 +152,6 @@ struct GNUNET_SCHEDULER_Handle
    */
   GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
 
-  /**
-   * GNUNET_NO if we are running normally,
-   * GNUNET_YES if we are in shutdown mode.
-   */
-  int shutdown;
-
   /**
    * Number of tasks on the ready list.
    */
@@ -154,6 +168,11 @@ struct GNUNET_SCHEDULER_Handle
    */
   enum GNUNET_SCHEDULER_Priority current_priority;
 
+  /**
+   * How 'nice' are we right now?
+   */
+  int nice_level;
+
 };
 
 
@@ -230,16 +249,15 @@ is_pending (struct GNUNET_SCHEDULER_Handle *sched,
  */
 static void
 update_sets (struct GNUNET_SCHEDULER_Handle *sched,
-             struct GNUNET_NETWORK_FDSet * rs, 
-            struct GNUNET_NETWORK_FDSet * ws, 
-            struct GNUNET_TIME_Relative *timeout)
+             struct GNUNET_NETWORK_FDSet *rs,
+             struct GNUNET_NETWORK_FDSet *ws,
+             struct GNUNET_TIME_Relative *timeout)
 {
   struct Task *pos;
 
   pos = sched->pending;
   while (pos != NULL)
     {
-
       if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
           (GNUNET_YES == is_pending (sched, pos->prereq_id)))
         {
@@ -253,11 +271,14 @@ update_sets (struct GNUNET_SCHEDULER_Handle *sched,
 
           to = GNUNET_TIME_absolute_get_remaining (pos->timeout);
           if (timeout->value > to.value)
-              *timeout = to;
+            *timeout = to;
         }
-
-      GNUNET_NETWORK_fdset_add (rs, pos->read_set);
-      GNUNET_NETWORK_fdset_add (ws, pos->write_set);
+      if (pos->read_set != NULL)
+        GNUNET_NETWORK_fdset_add (rs, pos->read_set);
+      if (pos->write_set != NULL)
+        GNUNET_NETWORK_fdset_add (ws, pos->write_set);
+      if (pos->reason != 0)
+        *timeout = GNUNET_TIME_UNIT_ZERO;
       pos = pos->next;
     }
 }
@@ -273,9 +294,11 @@ update_sets (struct GNUNET_SCHEDULER_Handle *sched,
  * @return GNUNET_YES if there was some overlap
  */
 static int
-set_overlaps (const struct GNUNET_NETWORK_FDSet * ready, 
-             struct GNUNET_NETWORK_FDSet * want)
+set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
+              struct GNUNET_NETWORK_FDSet *want)
 {
+  if (NULL == want)
+    return GNUNET_NO;
   if (GNUNET_NETWORK_fdset_overlap (ready, want))
     {
       /* copy all over (yes, there maybe unrelated bits,
@@ -303,13 +326,8 @@ is_ready (struct GNUNET_SCHEDULER_Handle *sched,
           struct Task *task,
           struct GNUNET_TIME_Absolute now,
           const struct GNUNET_NETWORK_FDSet *rs,
-         const struct GNUNET_NETWORK_FDSet * ws)
+          const struct GNUNET_NETWORK_FDSet *ws)
 {
-  if ((GNUNET_NO == task->run_on_shutdown) && (GNUNET_YES == sched->shutdown))
-    return GNUNET_NO;
-  if ((GNUNET_YES == task->run_on_shutdown) &&
-      (GNUNET_YES == sched->shutdown))
-    task->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
   if (now.value >= task->timeout.value)
     task->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
   if ((0 == (task->reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
@@ -337,11 +355,13 @@ is_ready (struct GNUNET_SCHEDULER_Handle *sched,
  * @param task task ready for execution
  */
 static void
-queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, 
-                 struct Task *task)
+queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle, struct Task *task)
 {
-  task->next = handle->ready[check_priority (task->priority)];
-  handle->ready[check_priority (task->priority)] = task;
+  enum GNUNET_SCHEDULER_Priority p = task->priority;
+  if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
+  task->next = handle->ready[check_priority (p)];
+  handle->ready[check_priority (p)] = task;
   handle->ready_count++;
 }
 
@@ -356,8 +376,8 @@ queue_ready_task (struct GNUNET_SCHEDULER_Handle *handle,
  */
 static void
 check_ready (struct GNUNET_SCHEDULER_Handle *handle,
-             const struct GNUNET_NETWORK_FDSet * rs,
-            const struct GNUNET_NETWORK_FDSet * ws)
+             const struct GNUNET_NETWORK_FDSet *rs,
+             const struct GNUNET_NETWORK_FDSet *ws)
 {
   struct Task *pos;
   struct Task *prev;
@@ -369,6 +389,11 @@ check_ready (struct GNUNET_SCHEDULER_Handle *handle,
   pos = handle->pending;
   while (pos != NULL)
     {
+#if DEBUG_TASKS
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Checking readiness of task: %llu / %p\n",
+                  pos->id, pos->callback_cls);
+#endif
       next = pos->next;
       if (GNUNET_YES == is_ready (handle, pos, now, rs, ws))
         {
@@ -386,17 +411,60 @@ check_ready (struct GNUNET_SCHEDULER_Handle *handle,
 }
 
 
+/**
+ * Request the shutdown of a scheduler.  Marks all currently
+ * pending tasks as ready because of shutdown.  This will
+ * cause all tasks to run (as soon as possible, respecting
+ * priorities and prerequisite tasks).  Note that tasks
+ * scheduled AFTER this call may still be delayed arbitrarily.
+ *
+ * @param sched the scheduler
+ */
+void
+GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
+{
+  struct Task *pos;
+  int i;
+
+  pos = sched->pending;
+  while (pos != NULL)
+    {
+      pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
+      /* we don't move the task into the ready queue yet; check_ready
+         will do that later, possibly adding additional
+         readiness-factors */
+      pos = pos->next;
+    }
+  for (i=0;i<GNUNET_SCHEDULER_PRIORITY_COUNT;i++)
+    {
+      pos = sched->ready[i];
+      while (pos != NULL)
+       {
+         pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
+         /* we don't move the task into the ready queue yet; check_ready
+            will do that later, possibly adding additional
+            readiness-factors */
+         pos = pos->next;
+       }
+    }  
+}
+
+
 /**
  * Destroy a task (release associated resources)
  *
  * @param t task to destroy
  */
-static void destroy_task (struct Task *t)
+static void
+destroy_task (struct Task *t)
 {
-  if (t->read_set)
+  if (NULL != t->read_set)
     GNUNET_NETWORK_fdset_destroy (t->read_set);
-  if (t->write_set)
+  if (NULL != t->write_set)
     GNUNET_NETWORK_fdset_destroy (t->write_set);
+#if EXECINFO
+  GNUNET_free (t->backtrace_strings);
+#endif
   GNUNET_free (t);
 }
 
@@ -406,7 +474,7 @@ static void destroy_task (struct Task *t)
  * empty.  Keep running tasks until we are either no longer running
  * "URGENT" tasks or until we have at least one "pending" task (which
  * may become ready, hence we should select on it).  Naturally, if
- * there are no more ready tasks, we also return.
+ * there are no more ready tasks, we also return.  
  *
  * @param sched the scheduler
  */
@@ -433,24 +501,32 @@ run_ready (struct GNUNET_SCHEDULER_Handle *sched)
       GNUNET_assert (pos != NULL);      /* ready_count wrong? */
       sched->ready[p] = pos->next;
       sched->ready_count--;
-      sched->current_priority = p;
-      GNUNET_assert (pos->priority == p);
+      if (sched->current_priority != pos->priority)
+       {
+         sched->current_priority = pos->priority;
+         (void) GNUNET_OS_set_process_priority (0, pos->priority);
+       }
+      sched->active_task = pos;
       tc.sched = sched;
       tc.reason = pos->reason;
       tc.read_ready = pos->read_set;
       tc.write_ready = pos->write_set;
       pos->callback (pos->callback_cls, &tc);
+#if DEBUG_TASKS
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Running task: %llu / %p\n", pos->id, pos->callback_cls);
+#endif
+      sched->active_task = NULL;
       destroy_task (pos);
       sched->tasks_run++;
     }
   while ((sched->pending == NULL) || (p == GNUNET_SCHEDULER_PRIORITY_URGENT));
 }
 
-
 /**
- * Have we (ever) received a SIGINT/TERM/QUIT/HUP?
+ * Pipe used to communicate shutdown via signal.
  */
-static volatile int sig_shutdown;
+static struct GNUNET_DISK_PipeHandle *sigpipe;
 
 
 /**
@@ -459,21 +535,30 @@ static volatile int sig_shutdown;
 static void
 sighandler_shutdown ()
 {
-  sig_shutdown = 1;
+  static char c;
+
+  GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
+                          (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
+                          sizeof (c));
 }
 
 
 /**
- * Initialize a scheduler using this thread.  This function will
- * return when either a shutdown was initiated (via signal) and all
- * tasks marked to "run_on_shutdown" have been completed or when all
- * tasks in general have been completed.
+ * Initialize and run scheduler.  This function will return when all
+ * tasks have completed.  On systems with signals, receiving a SIGTERM
+ * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
+ * to be run after the active task is complete.  As a result, SIGTERM
+ * causes all active tasks to be scheduled with reason
+ * "GNUNET_SCHEDULER_REASON_SHUTDOWN".  (However, tasks added
+ * afterwards will execute normally!). Note that any particular signal
+ * will only shut down one scheduler; applications should always only
+ * create a single scheduler.
  *
  * @param task task to run immediately
- * @param cls closure of task
+ * @param task_cls closure of task
  */
 void
-GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
+GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
 {
   struct GNUNET_SCHEDULER_Handle sched;
   struct GNUNET_NETWORK_FDSet *rs;
@@ -484,53 +569,45 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
   struct GNUNET_SIGNAL_Context *shc_term;
   struct GNUNET_SIGNAL_Context *shc_quit;
   struct GNUNET_SIGNAL_Context *shc_hup;
-  struct Task *tpos;
   unsigned long long last_tr;
   unsigned int busy_wait_warning;
+  const struct GNUNET_DISK_FileHandle *pr;
+  char c;
 
-  sig_shutdown = 0;
   rs = GNUNET_NETWORK_fdset_create ();
   ws = GNUNET_NETWORK_fdset_create ();
-#ifndef MINGW
+  GNUNET_assert (sigpipe == NULL);
+  sigpipe = GNUNET_DISK_pipe (GNUNET_NO);
+  GNUNET_assert (sigpipe != NULL);
+  pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
+  GNUNET_assert (pr != NULL);
   shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
   shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
+#ifndef MINGW
   shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
 #endif
   memset (&sched, 0, sizeof (sched));
   sched.current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
   GNUNET_SCHEDULER_add_continuation (&sched,
-                                     GNUNET_YES,
                                      task,
-                                     cls, GNUNET_SCHEDULER_REASON_STARTUP);
+                                     task_cls,
+                                     GNUNET_SCHEDULER_REASON_STARTUP);
   last_tr = 0;
   busy_wait_warning = 0;
-  while ((GNUNET_NO == sched.shutdown) &&
-         (!sig_shutdown) &&
-         ((sched.pending != NULL) || (sched.ready_count > 0)))
+  while ((sched.pending != NULL) || (sched.ready_count > 0))
     {
       GNUNET_NETWORK_fdset_zero (rs);
       GNUNET_NETWORK_fdset_zero (ws);
-      timeout = GNUNET_TIME_relative_get_forever();
+      timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+      update_sets (&sched, rs, ws, &timeout);
+      GNUNET_NETWORK_fdset_handle_set (rs, pr);
       if (sched.ready_count > 0)
         {
           /* no blocking, more work already ready! */
-          timeout = GNUNET_TIME_relative_get_zero();
+          timeout = GNUNET_TIME_UNIT_ZERO;
         }
-      update_sets (&sched, rs, ws, &timeout);
       ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
-      if (last_tr == sched.tasks_run)
-       busy_wait_warning++;
-      else
-       last_tr = sched.tasks_run;
-      if ( (ret == 0) &&
-          (timeout.value == 0) &&
-          (sched.ready_count == 0) &&
-          (busy_wait_warning > 16) )
-       {
-         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                     _("Looks like we're busy waiting...\n"));
-       }
       if (ret == GNUNET_SYSERR)
         {
           if (errno == EINTR)
@@ -538,43 +615,56 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *cls)
           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
           break;
         }
+      if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
+        {
+          /* consume the signal */
+          GNUNET_DISK_file_read (pr, &c, sizeof (c));
+          /* mark all active tasks as ready due to shutdown */
+          GNUNET_SCHEDULER_shutdown (&sched);
+        }
+      if (last_tr == sched.tasks_run)
+        {
+          busy_wait_warning++;
+        }
+      else
+        {
+          last_tr = sched.tasks_run;
+          busy_wait_warning = 0;
+        }
+      if ((ret == 0) && (timeout.value == 0) && (busy_wait_warning > 16))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("Looks like we're busy waiting...\n"));
+          sleep (1);            /* mitigate */
+        }
       check_ready (&sched, rs, ws);
       run_ready (&sched);
     }
-  if (sig_shutdown)
-    sched.shutdown = GNUNET_YES;
   GNUNET_SIGNAL_handler_uninstall (shc_int);
   GNUNET_SIGNAL_handler_uninstall (shc_term);
+#ifndef MINGW
   GNUNET_SIGNAL_handler_uninstall (shc_quit);
   GNUNET_SIGNAL_handler_uninstall (shc_hup);
-  do
-    {
-      run_ready (&sched);
-      check_ready (&sched, NULL, NULL);
-    }
-  while (sched.ready_count > 0);
-  while (NULL != (tpos = sched.pending))
-    {
-      sched.pending = tpos->next;
-      GNUNET_free (tpos);
-    }
+#endif
+  GNUNET_DISK_pipe_close (sigpipe);
+  sigpipe = NULL;
   GNUNET_NETWORK_fdset_destroy (rs);
   GNUNET_NETWORK_fdset_destroy (ws);
 }
 
 
 /**
- * Request the shutdown of a scheduler.  This function can be used to
- * stop a scheduling thread when created with the
- * "GNUNET_SCHEDULER_init_thread" function or from within the signal
- * handler for signals causing shutdowns.
+ * Obtain the reason code for why the current task was
+ * started.  Will return the same value as 
+ * the GNUNET_SCHEDULER_TaskContext's reason field.
  *
- * @param sched the scheduler
+ * @param sched scheduler to query
+ * @return reason(s) why the current task is run
  */
-void
-GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
+enum GNUNET_SCHEDULER_Reason
+GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
 {
-  sched->shutdown = GNUNET_YES;
+  return sched->active_task->reason;
 }
 
 
@@ -664,6 +754,10 @@ GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
   else
     prev->next = t->next;
   ret = t->callback_cls;
+#if DEBUG_TASKS
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Canceling task: %llu / %p\n", task, t->callback_cls);
+#endif
   destroy_task (t);
   return ret;
 }
@@ -675,131 +769,167 @@ GNUNET_SCHEDULER_cancel (struct GNUNET_SCHEDULER_Handle *sched,
  * and the reason code can be specified.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown should this task be run if we are shutting down?
- * @param main main function of the task
- * @param cls closure for 'main'
+ * @param task main function of the task
+ * @param task_cls closure for 'main'
  * @param reason reason for task invocation
  */
 void
 GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
-                                   int run_on_shutdown,
-                                   GNUNET_SCHEDULER_Task main,
-                                   void *cls,
+                                   GNUNET_SCHEDULER_Task task,
+                                   void *task_cls,
                                    enum GNUNET_SCHEDULER_Reason reason)
 {
-  struct Task *task;
-
-  task = GNUNET_malloc (sizeof (struct Task));
-  task->callback = main;
-  task->callback_cls = cls;
-  task->id = ++sched->last_id;
-  task->reason = reason;
-  task->priority = sched->current_priority;
-  task->run_on_shutdown = run_on_shutdown;
-  queue_ready_task (sched, task);
+  struct Task *t;
+#if EXECINFO
+  void *backtrace_array[50];
+#endif
+  t = GNUNET_malloc (sizeof (struct Task));
+#if EXECINFO
+  t->num_backtrace_strings = backtrace(backtrace_array, 50);
+  t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
+#endif
+  t->callback = task;
+  t->callback_cls = task_cls;
+  t->id = ++sched->last_id;
+  t->reason = reason;
+  t->priority = sched->current_priority;
+#if DEBUG_TASKS
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Adding continuation task: %llu / %p\n",
+              t->id, t->callback_cls);
+#endif
+  queue_ready_task (sched, t);
 }
 
 
+
 /**
- * Schedule a new task to be run after the specified
- * prerequisite task has completed.
+ * Schedule a new task to be run after the specified prerequisite task
+ * has completed. It will be run with the priority of the calling
+ * task.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown?
- * @param prio how important is this task?
  * @param prerequisite_task run this task after the task with the given
  *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
+ *        conditions, such as delay, read or write-readiness
  *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param main main function of the task
- * @param cls closure for 'main'
+ *        on completion of other tasks (this will cause the task to run as
+ *        soon as possible).
+ * @param task main function of the task
+ * @param task_cls closure of task
  * @return unique task identifier for the job
- *         only valid until "main" is started!
+ *         only valid until "task" is started!
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_after (struct GNUNET_SCHEDULER_Handle *sched,
-                            int run_on_shutdown,
-                            enum GNUNET_SCHEDULER_Priority prio,
                             GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
-                            GNUNET_SCHEDULER_Task main, void *cls)
+                            GNUNET_SCHEDULER_Task task, void *task_cls)
 {
-  return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
+  return GNUNET_SCHEDULER_add_select (sched,
+                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
                                       prerequisite_task,
                                       GNUNET_TIME_UNIT_ZERO,
-                                      NULL, NULL, main, cls);
+                                      NULL, NULL, task, task_cls);
 }
 
 
+/**
+ * Schedule a new task to be run with a specified priority.
+ *
+ * @param sched scheduler to use
+ * @param prio how important is the new task?
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ *         only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_with_priority (struct GNUNET_SCHEDULER_Handle * sched,
+                                    enum GNUNET_SCHEDULER_Priority prio,
+                                    GNUNET_SCHEDULER_Task task,
+                                    void *task_cls)
+{
+  return GNUNET_SCHEDULER_add_select (sched,
+                                      prio,
+                                      GNUNET_SCHEDULER_NO_TASK,
+                                      GNUNET_TIME_UNIT_ZERO,
+                                      NULL, NULL, task, task_cls);
+}
+
+
+
 /**
  * Schedule a new task to be run with a specified delay.  The task
- * will be scheduled for execution once the delay has expired and the
- * prerequisite task has completed.
+ * will be scheduled for execution once the delay has expired. It
+ * will be run with the priority of the calling task.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown? You can use this
- *        argument to run a function only during shutdown
- *        by setting delay to -1.  Set this
- *        argument to GNUNET_NO to skip this task if
- *        the user requested process termination.
- * @param prio how important is this task?
- * @param prerequisite_task run this task after the task with the given
- *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
- *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
- * @param main main function of the task
- * @param cls closure of task
+ * @param delay when should this operation time out? Use 
+ *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param task main function of the task
+ * @param task_cls closure of task
  * @return unique task identifier for the job
- *         only valid until "main" is started!
+ *         only valid until "task" is started!
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
-                              int run_on_shutdown,
-                              enum GNUNET_SCHEDULER_Priority prio,
-                              GNUNET_SCHEDULER_TaskIdentifier
-                              prerequisite_task,
                               struct GNUNET_TIME_Relative delay,
-                              GNUNET_SCHEDULER_Task main, void *cls)
+                              GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+  return GNUNET_SCHEDULER_add_select (sched,
+                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                      GNUNET_SCHEDULER_NO_TASK, delay,
+                                      NULL, NULL, task, task_cls);
+}
+
+
+
+/**
+ * Schedule a new task to be run as soon as possible. The task
+ * will be run with the priority of the calling task.
+ *
+ * @param sched scheduler to use
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ *         only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_now (struct GNUNET_SCHEDULER_Handle *sched,
+                         GNUNET_SCHEDULER_Task task,
+                         void *task_cls)
 {
-  return GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
-                                      prerequisite_task, delay,
-                                      NULL, NULL, main, cls);
+  return GNUNET_SCHEDULER_add_select (sched,
+                                      GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                      GNUNET_SCHEDULER_NO_TASK,
+                                     GNUNET_TIME_UNIT_ZERO,
+                                      NULL, NULL, task, task_cls);
 }
 
 
+
 /**
  * Schedule a new task to be run with a specified delay or when the
  * specified file descriptor is ready for reading.  The delay can be
  * used as a timeout on the socket being ready.  The task will be
  * scheduled for execution once either the delay has expired or the
- * socket operation is ready.
+ * socket operation is ready.  It will be run with the priority of
+ * the calling task.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown? Set this
- *        argument to GNUNET_NO to skip this task if
- *        the user requested process termination.
- * @param prio how important is this task?
- * @param prerequisite_task run this task after the task with the given
- *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
- *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
+ * @param delay when should this operation time out? Use 
+ *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
  * @param rfd read file-descriptor
- * @param main main function of the task
- * @param cls closure of task
+ * @param task main function of the task
+ * @param task_cls closure of task
  * @return unique task identifier for the job
- *         only valid until "main" is started!
+ *         only valid until "task" is started!
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
-                           int run_on_shutdown,
-                           enum GNUNET_SCHEDULER_Priority prio,
-                           GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
-                           struct GNUNET_TIME_Relative delay,
-                           struct GNUNET_NETWORK_Handle *rfd, GNUNET_SCHEDULER_Task main, void *cls)
+                               struct GNUNET_TIME_Relative delay,
+                               struct GNUNET_NETWORK_Handle * rfd,
+                               GNUNET_SCHEDULER_Task task, void *task_cls)
 {
   struct GNUNET_NETWORK_FDSet *rs;
   GNUNET_SCHEDULER_TaskIdentifier ret;
@@ -807,9 +937,10 @@ GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
   GNUNET_assert (rfd != NULL);
   rs = GNUNET_NETWORK_fdset_create ();
   GNUNET_NETWORK_fdset_set (rs, rfd);
-  ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
-                                      prerequisite_task, delay,
-                                      rs, NULL, main, cls);
+  ret = GNUNET_SCHEDULER_add_select (sched,
+                                     GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                     GNUNET_SCHEDULER_NO_TASK,
+                                     delay, rs, NULL, task, task_cls);
   GNUNET_NETWORK_fdset_destroy (rs);
   return ret;
 }
@@ -820,32 +951,23 @@ GNUNET_SCHEDULER_add_read_net (struct GNUNET_SCHEDULER_Handle * sched,
  * specified file descriptor is ready for writing.  The delay can be
  * used as a timeout on the socket being ready.  The task will be
  * scheduled for execution once either the delay has expired or the
- * socket operation is ready.
+ * socket operation is ready.  It will be run with the priority of
+ * the calling task.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown? Set this
- *        argument to GNUNET_NO to skip this task if
- *        the user requested process termination.
- * @param prio how important is this task?
- * @param prerequisite_task run this task after the task with the given
- *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
- *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
+ * @param delay when should this operation time out? Use 
+ *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
  * @param wfd write file-descriptor
- * @param main main function of the task
- * @param cls closure of task
+ * @param task main function of the task
+ * @param task_cls closure of task
  * @return unique task identifier for the job
- *         only valid until "main" is started!
+ *         only valid until "task" is started!
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
-                            int run_on_shutdown,
-                            enum GNUNET_SCHEDULER_Priority prio,
-                            GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
-                            struct GNUNET_TIME_Relative delay,
-                            struct GNUNET_NETWORK_Handle *wfd, GNUNET_SCHEDULER_Task main, void *cls)
+                                struct GNUNET_TIME_Relative delay,
+                                struct GNUNET_NETWORK_Handle * wfd,
+                                GNUNET_SCHEDULER_Task task, void *task_cls)
 {
   struct GNUNET_NETWORK_FDSet *ws;
   GNUNET_SCHEDULER_TaskIdentifier ret;
@@ -853,115 +975,37 @@ GNUNET_SCHEDULER_add_write_net (struct GNUNET_SCHEDULER_Handle * sched,
   GNUNET_assert (wfd != NULL);
   ws = GNUNET_NETWORK_fdset_create ();
   GNUNET_NETWORK_fdset_set (ws, wfd);
-  ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
-                                      prerequisite_task, delay,
-                                      NULL, ws, main, cls);
+  ret = GNUNET_SCHEDULER_add_select (sched,
+                                     GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                     GNUNET_SCHEDULER_NO_TASK, delay,
+                                     NULL, ws, task, task_cls);
   GNUNET_NETWORK_fdset_destroy (ws);
   return ret;
 }
 
 
-/**
- * Schedule a new task to be run with a specified delay or when any of
- * the specified file descriptor sets is ready.  The delay can be used
- * as a timeout on the socket(s) being ready.  The task will be
- * scheduled for execution once either the delay has expired or any of
- * the socket operations is ready.  This is the most general
- * function of the "add" family.  Note that the "prerequisite_task"
- * must be satisfied in addition to any of the other conditions.  In
- * other words, the task will be started when
- * <code>
- * (prerequisite-run)
- * && (delay-ready
- *     || any-rs-ready
- *     || any-ws-ready
- *     || (shutdown-active && run-on-shutdown) )
- * </code>
- *
- * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown?  Set this
- *        argument to GNUNET_NO to skip this task if
- *        the user requested process termination.
- * @param prio how important is this task?
- * @param prerequisite_task run this task after the task with the given
- *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
- *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever"
- * @param rs set of file descriptors we want to read (can be NULL)
- * @param ws set of file descriptors we want to write (can be NULL)
- * @param main main function of the task
- * @param cls closure of task
- * @return unique task identifier for the job
- *         only valid until "main" is started!
- */
-GNUNET_SCHEDULER_TaskIdentifier
-GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
-                             int run_on_shutdown,
-                             enum GNUNET_SCHEDULER_Priority prio,
-                             GNUNET_SCHEDULER_TaskIdentifier
-                             prerequisite_task,
-                             struct GNUNET_TIME_Relative delay,
-                             const struct GNUNET_NETWORK_FDSet * rs, const struct GNUNET_NETWORK_FDSet * ws,
-                             GNUNET_SCHEDULER_Task main, void *cls)
-{
-  struct Task *task;
-
-  task = GNUNET_malloc (sizeof (struct Task));
-  task->callback = main;
-  task->callback_cls = cls;
-  task->read_set = GNUNET_NETWORK_fdset_create ();
-  if (rs != NULL)
-    GNUNET_NETWORK_fdset_copy (task->read_set, rs);
-  task->write_set = GNUNET_NETWORK_fdset_create ();
-  if (ws != NULL)
-    GNUNET_NETWORK_fdset_copy (task->write_set, ws);
-  task->id = ++sched->last_id;
-  task->prereq_id = prerequisite_task;
-  task->timeout = GNUNET_TIME_relative_to_absolute (delay);
-  task->priority =
-    check_priority ((prio ==
-                     GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
-                    : prio);
-  task->run_on_shutdown = run_on_shutdown;
-  task->next = sched->pending;
-  sched->pending = task;
-  return task->id;
-}
-
 /**
  * Schedule a new task to be run with a specified delay or when the
  * specified file descriptor is ready for reading.  The delay can be
  * used as a timeout on the socket being ready.  The task will be
  * scheduled for execution once either the delay has expired or the
- * socket operation is ready.
+ * socket operation is ready. It will be run with the priority of
+ * the calling task.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown? Set this
- *        argument to GNUNET_NO to skip this task if
- *        the user requested process termination.
- * @param prio how important is this task?
- * @param prerequisite_task run this task after the task with the given
- *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
- *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
+ * @param delay when should this operation time out? Use 
+ *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
  * @param rfd read file-descriptor
- * @param main main function of the task
- * @param cls closure of task
+ * @param task main function of the task
+ * @param task_cls closure of task
  * @return unique task identifier for the job
- *         only valid until "main" is started!
+ *         only valid until "task" is started!
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
-                               int run_on_shutdown,
-                               enum GNUNET_SCHEDULER_Priority prio,
-                               GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
-                               struct GNUNET_TIME_Relative delay,
-                               const struct GNUNET_DISK_FileHandle *rfd,
-                               GNUNET_SCHEDULER_Task main, void *cls)
+                                struct GNUNET_TIME_Relative delay,
+                                const struct GNUNET_DISK_FileHandle * rfd,
+                                GNUNET_SCHEDULER_Task task, void *task_cls)
 {
   struct GNUNET_NETWORK_FDSet *rs;
   GNUNET_SCHEDULER_TaskIdentifier ret;
@@ -969,9 +1013,10 @@ GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
   GNUNET_assert (rfd != NULL);
   rs = GNUNET_NETWORK_fdset_create ();
   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
-  ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
-                                      prerequisite_task, delay,
-                                      rs, NULL, main, cls);
+  ret = GNUNET_SCHEDULER_add_select (sched,
+                                     GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                     GNUNET_SCHEDULER_NO_TASK, delay,
+                                     rs, NULL, task, task_cls);
   GNUNET_NETWORK_fdset_destroy (rs);
   return ret;
 }
@@ -982,33 +1027,23 @@ GNUNET_SCHEDULER_add_read_file (struct GNUNET_SCHEDULER_Handle * sched,
  * specified file descriptor is ready for writing.  The delay can be
  * used as a timeout on the socket being ready.  The task will be
  * scheduled for execution once either the delay has expired or the
- * socket operation is ready.
+ * socket operation is ready. It will be run with the priority of
+ * the calling task.
  *
  * @param sched scheduler to use
- * @param run_on_shutdown run on shutdown? Set this
- *        argument to GNUNET_NO to skip this task if
- *        the user requested process termination.
- * @param prio how important is this task?
- * @param prerequisite_task run this task after the task with the given
- *        task identifier completes (and any of our other
- *        conditions, such as delay, read or write-readyness
- *        are satisfied).  Use  GNUNET_SCHEDULER_NO_TASK to not have any dependency
- *        on completion of other tasks.
- * @param delay how long should we wait? Use  GNUNET_TIME_UNIT_FOREVER_REL for "forever"
+ * @param delay when should this operation time out? Use 
+ *        GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
  * @param wfd write file-descriptor
- * @param main main function of the task
- * @param cls closure of task
+ * @param task main function of the task
+ * @param task_cls closure of task
  * @return unique task identifier for the job
- *         only valid until "main" is started!
+ *         only valid until "task" is started!
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
-                                int run_on_shutdown,
-                                enum GNUNET_SCHEDULER_Priority prio,
-                                GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
-                                struct GNUNET_TIME_Relative delay,
-                                const struct GNUNET_DISK_FileHandle *wfd,
-                                GNUNET_SCHEDULER_Task main, void *cls)
+                                 struct GNUNET_TIME_Relative delay,
+                                 const struct GNUNET_DISK_FileHandle * wfd,
+                                 GNUNET_SCHEDULER_Task task, void *task_cls)
 {
   struct GNUNET_NETWORK_FDSet *ws;
   GNUNET_SCHEDULER_TaskIdentifier ret;
@@ -1016,12 +1051,96 @@ GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
   GNUNET_assert (wfd != NULL);
   ws = GNUNET_NETWORK_fdset_create ();
   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
-  ret = GNUNET_SCHEDULER_add_select (sched, run_on_shutdown, prio,
-                                      prerequisite_task, delay,
-                                      NULL, ws, main, cls);
+  ret = GNUNET_SCHEDULER_add_select (sched,
+                                     GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                     GNUNET_SCHEDULER_NO_TASK,
+                                     delay, NULL, ws, task, task_cls);
   GNUNET_NETWORK_fdset_destroy (ws);
   return ret;
 }
 
 
+
+/**
+ * Schedule a new task to be run with a specified delay or when any of
+ * the specified file descriptor sets is ready.  The delay can be used
+ * as a timeout on the socket(s) being ready.  The task will be
+ * scheduled for execution once either the delay has expired or any of
+ * the socket operations is ready.  This is the most general
+ * function of the "add" family.  Note that the "prerequisite_task"
+ * must be satisfied in addition to any of the other conditions.  In
+ * other words, the task will be started when
+ * <code>
+ * (prerequisite-run)
+ * && (delay-ready
+ *     || any-rs-ready
+ *     || any-ws-ready
+ *     || (shutdown-active && run-on-shutdown) )
+ * </code>
+ *
+ * @param sched scheduler to use
+ * @param prio how important is this task?
+ * @param prerequisite_task run this task after the task with the given
+ *        task identifier completes (and any of our other
+ *        conditions, such as delay, read or write-readiness
+ *        are satisfied).  Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
+ *        on completion of other tasks.
+ * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
+ *        which means that the task will only be run after we receive SIGTERM
+ * @param rs set of file descriptors we want to read (can be NULL)
+ * @param ws set of file descriptors we want to write (can be NULL)
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ *         only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
+                             enum GNUNET_SCHEDULER_Priority prio,
+                             GNUNET_SCHEDULER_TaskIdentifier
+                             prerequisite_task,
+                             struct GNUNET_TIME_Relative delay,
+                             const struct GNUNET_NETWORK_FDSet * rs,
+                             const struct GNUNET_NETWORK_FDSet * ws,
+                             GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+  struct Task *t;
+#if EXECINFO
+  void *backtrace_array[MAX_TRACE_DEPTH];
+#endif
+
+  GNUNET_assert (NULL != task);
+  t = GNUNET_malloc (sizeof (struct Task));
+  t->callback = task;
+  t->callback_cls = task_cls;
+#if EXECINFO
+  t->num_backtrace_strings = backtrace(backtrace_array, MAX_TRACE_DEPTH);
+  t->backtrace_strings = backtrace_symbols(backtrace_array, t->num_backtrace_strings);
+#endif
+  if (rs != NULL)
+    {
+      t->read_set = GNUNET_NETWORK_fdset_create ();
+      GNUNET_NETWORK_fdset_copy (t->read_set, rs);
+    }
+  if (ws != NULL)
+    {
+      t->write_set = GNUNET_NETWORK_fdset_create ();
+      GNUNET_NETWORK_fdset_copy (t->write_set, ws);
+    }
+  t->id = ++sched->last_id;
+  t->prereq_id = prerequisite_task;
+  t->timeout = GNUNET_TIME_relative_to_absolute (delay);
+  t->priority =
+    check_priority ((prio ==
+                     GNUNET_SCHEDULER_PRIORITY_KEEP) ? sched->current_priority
+                    : prio);
+  t->next = sched->pending;
+  sched->pending = t;
+#if DEBUG_TASKS
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Adding task: %llu / %p\n", t->id, t->callback_cls);
+#endif
+  return t->id;
+}
+
 /* end of scheduler.c */