asserts
[oweals/gnunet.git] / src / util / scheduler.c
index ef9f8841d9d8f10ef588512883621d6fc30ff9a5..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
 
@@ -60,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;
 
@@ -95,6 +100,19 @@ struct Task
    */
   enum GNUNET_SCHEDULER_Priority priority;
 
+#if EXECINFO
+  /**
+   * Array of strings which make up a backtrace from the point when this
+   * task was scheduled (essentially, who scheduled the task?)
+   */
+  char **backtrace_strings;
+
+  /**
+   * Size of the backtrace_strings array
+   */
+  int num_backtrace_strings;
+#endif
+
 };
 
 
@@ -109,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.
@@ -145,6 +168,11 @@ struct GNUNET_SCHEDULER_Handle
    */
   enum GNUNET_SCHEDULER_Priority current_priority;
 
+  /**
+   * How 'nice' are we right now?
+   */
+  int nice_level;
+
 };
 
 
@@ -329,8 +357,11 @@ is_ready (struct GNUNET_SCHEDULER_Handle *sched,
 static void
 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++;
 }
 
@@ -360,7 +391,7 @@ check_ready (struct GNUNET_SCHEDULER_Handle *handle,
     {
 #if DEBUG_TASKS
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Checking readyness of task: %llu / %p\n",
+                  "Checking readiness of task: %llu / %p\n",
                   pos->id, pos->callback_cls);
 #endif
       next = pos->next;
@@ -393,6 +424,7 @@ void
 GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
 {
   struct Task *pos;
+  int i;
 
   pos = sched->pending;
   while (pos != NULL)
@@ -400,9 +432,21 @@ GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched)
       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
-         readyness-factors */
+         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;
+       }
+    }  
 }
 
 
@@ -418,6 +462,9 @@ destroy_task (struct Task *t)
     GNUNET_NETWORK_fdset_destroy (t->read_set);
   if (NULL != t->write_set)
     GNUNET_NETWORK_fdset_destroy (t->write_set);
+#if EXECINFO
+  GNUNET_free (t->backtrace_strings);
+#endif
   GNUNET_free (t);
 }
 
@@ -454,8 +501,12 @@ 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;
@@ -465,13 +516,13 @@ run_ready (struct GNUNET_SCHEDULER_Handle *sched)
       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));
 }
 
-#ifndef MINGW
 /**
  * Pipe used to communicate shutdown via signal.
  */
@@ -490,7 +541,6 @@ sighandler_shutdown ()
                           (sigpipe, GNUNET_DISK_PIPE_END_WRITE), &c,
                           sizeof (c));
 }
-#endif
 
 
 /**
@@ -526,7 +576,6 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
 
   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);
@@ -534,6 +583,7 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
   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
@@ -565,7 +615,6 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "select");
           break;
         }
-#ifndef MINGW
       if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
         {
           /* consume the signal */
@@ -573,7 +622,6 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
           /* mark all active tasks as ready due to shutdown */
           GNUNET_SCHEDULER_shutdown (&sched);
         }
-#endif
       if (last_tr == sched.tasks_run)
         {
           busy_wait_warning++;
@@ -592,19 +640,33 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
       check_ready (&sched, rs, ws);
       run_ready (&sched);
     }
-#ifndef MINGW
   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);
+#endif
   GNUNET_DISK_pipe_close (sigpipe);
   sigpipe = NULL;
-#endif
   GNUNET_NETWORK_fdset_destroy (rs);
   GNUNET_NETWORK_fdset_destroy (ws);
 }
 
 
+/**
+ * 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 scheduler to query
+ * @return reason(s) why the current task is run
+ */
+enum GNUNET_SCHEDULER_Reason
+GNUNET_SCHEDULER_get_reason (struct GNUNET_SCHEDULER_Handle *sched)
+{
+  return sched->active_task->reason;
+}
+
 
 /**
  * Get information about the current load of this scheduler.  Use this
@@ -718,8 +780,14 @@ GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
                                    enum GNUNET_SCHEDULER_Reason reason)
 {
   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;
@@ -743,7 +811,7 @@ GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched,
  * @param sched scheduler to use
  * @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 (this will cause the task to run as
  *        soon as possible).
@@ -815,6 +883,31 @@ GNUNET_SCHEDULER_add_delayed (struct GNUNET_SCHEDULER_Handle * sched,
 }
 
 
+
+/**
+ * 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,
+                                      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
@@ -989,7 +1082,7 @@ GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched,
  * @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 delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
@@ -1012,10 +1105,18 @@ GNUNET_SCHEDULER_add_select (struct GNUNET_SCHEDULER_Handle * sched,
                              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 ();