LRN: Fix automake deps to allow -j* builds again
[oweals/gnunet.git] / src / util / scheduler.c
index 49d491bd7843546e48974b39f0a53159256dd52a..dd0ce6054915d6ac82fdc5dd81b28be6fbf8c246 100644 (file)
@@ -1,6 +1,6 @@
 /*
       This file is part of GNUnet
-      (C) 2009 Christian Grothoff (and other contributing authors)
+      (C) 2009, 2011 Christian Grothoff (and other contributing authors)
 
       GNUnet is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published
  * Use lsof to generate file descriptor reports on select error?
  * (turn off for stable releases).
  */
-#define USE_LSOF GNUNET_YES
+#define USE_LSOF GNUNET_NO
 
 /**
  * Obtain trace information for all scheduler calls that schedule tasks.
  */
 #define EXECINFO GNUNET_NO
 
+/**
+ * Check each file descriptor before adding
+ */
+#define DEBUG_FDS GNUNET_NO
+
 /**
  * Depth of the traces collected via EXECINFO.
  */
@@ -144,6 +149,12 @@ struct Task
    * Set if we only wait for writing to a single FD, otherwise -1.
    */
   int write_fd;
+  
+  /**
+   * Should the existence of this task in the queue be counted as
+   * reason to not shutdown the scheduler?
+   */
+  int lifeness;
 
 #if EXECINFO
   /**
@@ -229,6 +240,10 @@ static enum GNUNET_SCHEDULER_Priority current_priority;
  */
 static enum GNUNET_SCHEDULER_Priority max_priority_added;
 
+/**
+ * Value of the 'lifeness' flag for the current task.
+ */
+static int current_lifeness;
 
 /**
  * Check that the given priority is legal (and return it).
@@ -432,7 +447,6 @@ is_ready (struct Task *task,
 /**
  * Put a task that is ready for execution into the ready queue.
  *
- * @param handle the scheduler
  * @param task task ready for execution
  */
 static void
@@ -451,7 +465,6 @@ queue_ready_task (struct Task *task)
  * Check which tasks are ready and move them
  * to the respective ready queue.
  *
- * @param handle the scheduler
  * @param rs FDs ready for reading
  * @param ws FDs ready for writing
  */
@@ -608,8 +621,10 @@ run_ready (struct GNUNET_NETWORK_FDSet *rs,
       if (current_priority != pos->priority)
        {
          current_priority = pos->priority;
-         (void) GNUNET_OS_set_process_priority (GNUNET_OS_process_current (), pos->priority);
+         (void) GNUNET_OS_set_process_priority (GNUNET_OS_process_current (), 
+                                                pos->priority);
        }
+      current_lifeness = pos->lifeness;
       active_task = pos;
 #if PROFILE_DELAYS
       if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value >
@@ -689,6 +704,34 @@ sighandler_shutdown ()
 }
 
 
+/**
+ * Check if the system is still life. Trigger shutdown if we
+ * have tasks, but none of them give us lifeness.  
+ *
+ * @return GNUNET_OK to continue the main loop,
+ *         GNUNET_NO to exit
+ */
+static int
+check_lifeness()
+{
+  struct Task *t;
+  if (ready_count > 0)
+    return GNUNET_OK;
+  for (t = pending; NULL != t; t = t->next)
+    if (t->lifeness == GNUNET_YES)
+      return GNUNET_OK;
+  for (t = pending_timeout; NULL != t; t = t->next)
+    if (t->lifeness == GNUNET_YES)
+       return GNUNET_OK;
+  if ( (NULL != pending) || (NULL != pending_timeout) )
+    {
+      GNUNET_SCHEDULER_shutdown ();
+      return GNUNET_OK;
+    }
+  return GNUNET_NO;
+}
+
+
 /**
  * Initialize and run scheduler.  This function will return when all
  * tasks have completed.  On systems with signals, receiving a SIGTERM
@@ -738,14 +781,20 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
   shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
 #endif
   current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
+  current_lifeness = GNUNET_YES;
   GNUNET_SCHEDULER_add_continuation (task,
                                      task_cls,
                                      GNUNET_SCHEDULER_REASON_STARTUP);
+#if ENABLE_WINDOWS_WORKAROUNDS
+  active_task = (void*) (long) -1; /* force passing of sanity check */
+  GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
+                                         &GNUNET_OS_install_parent_control_handler,
+                                         NULL);
+  active_task = NULL;
+#endif
   last_tr = 0;
   busy_wait_warning = 0;
-  while ((pending != NULL) ||
-        (pending_timeout != NULL) ||
-        (ready_count > 0))
+  while (GNUNET_OK == check_lifeness ())
     {
       GNUNET_NETWORK_fdset_zero (rs);
       GNUNET_NETWORK_fdset_zero (ws);
@@ -768,9 +817,10 @@ GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
 #if USE_LSOF
          char lsof[512];
          snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid());
-         close (1);
-         dup2 (2, 1);
-         ret = system (lsof);            
+         (void) close (1);
+         (void) dup2 (2, 1);
+         if (0 != system (lsof))
+           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "system");    
 #endif
 #endif
           abort ();
@@ -907,7 +957,13 @@ GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_TaskIdentifier task)
   while (t == NULL)
     {
       p++;
-      GNUNET_assert (p < GNUNET_SCHEDULER_PRIORITY_COUNT);
+      if (p >= GNUNET_SCHEDULER_PRIORITY_COUNT)
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     _("Attempt to cancel dead task %llu!\n"),
+                     (unsigned long long) task);
+         GNUNET_assert (0);
+       }
       prev = NULL;
       t = ready[p];
       while (t != NULL)
@@ -989,6 +1045,7 @@ GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_Task task,
 #endif
   t->reason = reason;
   t->priority = current_priority;
+  t->lifeness = current_lifeness;
 #if DEBUG_TASKS
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Adding continuation task: %llu / %p\n",
@@ -1090,6 +1147,7 @@ GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
 #endif
   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
   t->priority = current_priority;
+  t->lifeness = current_lifeness;
   /* try tail first (optimization in case we are
      appending to a long list of tasks with timeouts) */
   prev = pending_timeout_last;
@@ -1157,7 +1215,7 @@ GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
  */
 GNUNET_SCHEDULER_TaskIdentifier
 GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task,
-                                                 void *task_cls)
+                         void *task_cls)
 {
   return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
                                       GNUNET_SCHEDULER_NO_TASK,
@@ -1166,6 +1224,37 @@ GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task,
 }
 
 
+/**
+ * Schedule a new task to be run as soon as possible with the
+ * (transitive) ignore-shutdown flag either explicitly set or
+ * explicitly enabled.  This task (and all tasks created from it,
+ * other than by another call to this function) will either count or
+ * not count for the 'lifeness' of the process.  This API is only
+ * useful in a few special cases.
+ *
+ * @param lifeness GNUNET_YES if the task counts for lifeness, GNUNET_NO if not.
+ * @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_with_lifeness (int lifeness,
+                                       GNUNET_SCHEDULER_Task task,
+                                       void *task_cls)
+{
+  GNUNET_SCHEDULER_TaskIdentifier ret;
+
+  ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
+                                    GNUNET_SCHEDULER_NO_TASK,
+                                    GNUNET_TIME_UNIT_ZERO,
+                                    NULL, NULL, task, task_cls);
+  GNUNET_assert (pending->id == ret);
+  pending->lifeness = lifeness;
+  return ret;
+}
+
+
 
 
 /**
@@ -1194,6 +1283,7 @@ GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task,
  * @return unique task identifier for the job
  *         only valid until "task" is started!
  */
+#ifndef MINGW
 GNUNET_SCHEDULER_TaskIdentifier
 add_without_sets (struct GNUNET_TIME_Relative delay,
                  int rfd,
@@ -1213,8 +1303,45 @@ add_without_sets (struct GNUNET_TIME_Relative delay,
 #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 DEBUG_FDS
+  if (-1 != rfd)
+    {
+      int flags = fcntl(rfd, F_GETFD);
+      if (flags == -1 && errno == EBADF)
+        {
+          GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", rfd);
+#if EXECINFO
+          int i;
+
+          for (i=0;i<t->num_backtrace_strings;i++)
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Trace: %s\n",
+                        t->backtrace_strings[i]);
+#endif
+          GNUNET_assert(0);
+        }
+    }
+  if (-1 != wfd)
+    {
+      int flags = fcntl(wfd, F_GETFD);
+      if (flags == -1 && errno == EBADF)
+        {
+          GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", wfd);
+#if EXECINFO
+          int i;
+
+          for (i=0;i<t->num_backtrace_strings;i++)
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "Trace: %s\n",
+                        t->backtrace_strings[i]);
+#endif
+          GNUNET_assert(0);
+        }
+    }
 #endif
   t->read_fd = rfd;
+  GNUNET_assert(wfd >= -1);
   t->write_fd = wfd;
   t->id = ++last_id;
 #if PROFILE_DELAYS
@@ -1223,6 +1350,7 @@ add_without_sets (struct GNUNET_TIME_Relative delay,
   t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
   t->timeout = GNUNET_TIME_relative_to_absolute (delay);
   t->priority = check_priority (current_priority);
+  t->lifeness = current_lifeness;
   t->next = pending;
   pending = t;
   max_priority_added = GNUNET_MAX (max_priority_added,
@@ -1243,6 +1371,7 @@ add_without_sets (struct GNUNET_TIME_Relative delay,
 #endif
   return t->id;
 }
+#endif
 
 
 
@@ -1267,11 +1396,25 @@ GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
                                struct GNUNET_NETWORK_Handle * rfd,
                                GNUNET_SCHEDULER_Task task, void *task_cls)
 {
+#if MINGW
+  struct GNUNET_NETWORK_FDSet *rs;
+  GNUNET_SCHEDULER_TaskIdentifier ret;
+
+  GNUNET_assert (rfd != NULL);
+  rs = GNUNET_NETWORK_fdset_create ();
+  GNUNET_NETWORK_fdset_set (rs, rfd);
+  ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
+                                     GNUNET_SCHEDULER_NO_TASK, delay,
+                                     rs, NULL, task, task_cls);
+  GNUNET_NETWORK_fdset_destroy (rs);
+  return ret;
+#else
   return add_without_sets (delay,
                           GNUNET_NETWORK_get_fd (rfd),
                           -1,
                           task,
                           task_cls);
+#endif
 }
 
 
@@ -1296,11 +1439,26 @@ GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
                                 struct GNUNET_NETWORK_Handle * wfd,
                                 GNUNET_SCHEDULER_Task task, void *task_cls)
 {
+#if MINGW
+  struct GNUNET_NETWORK_FDSet *ws;
+  GNUNET_SCHEDULER_TaskIdentifier ret;
+
+  GNUNET_assert (wfd != NULL);
+  ws = GNUNET_NETWORK_fdset_create ();
+  GNUNET_NETWORK_fdset_set (ws, wfd);
+  ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
+                                     GNUNET_SCHEDULER_NO_TASK, delay,
+                                     NULL, ws, task, task_cls);
+  GNUNET_NETWORK_fdset_destroy (ws);
+  return ret;
+#else
+  GNUNET_assert(GNUNET_NETWORK_get_fd(wfd) >= 0);
   return add_without_sets (delay,
                           -1,
                           GNUNET_NETWORK_get_fd (wfd),
                           task,
                           task_cls);
+#endif
 }
 
 
@@ -1332,7 +1490,7 @@ GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
   GNUNET_assert (rfd != NULL);
   rs = GNUNET_NETWORK_fdset_create ();
   GNUNET_NETWORK_fdset_handle_set (rs, rfd);
-  ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
+  ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
                                      GNUNET_SCHEDULER_NO_TASK, delay,
                                      rs, NULL, task, task_cls);
   GNUNET_NETWORK_fdset_destroy (rs);
@@ -1379,7 +1537,7 @@ GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
   GNUNET_assert (wfd != NULL);
   ws = GNUNET_NETWORK_fdset_create ();
   GNUNET_NETWORK_fdset_handle_set (ws, wfd);
-  ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_KEEP,
+  ret = GNUNET_SCHEDULER_add_select (check_priority (current_priority),
                                      GNUNET_SCHEDULER_NO_TASK,
                                      delay, NULL, ws, task, task_cls);
   GNUNET_NETWORK_fdset_destroy (ws);
@@ -1388,6 +1546,7 @@ GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
   int fd;
 
   GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
+  GNUNET_assert(fd >= 0);
   return add_without_sets (delay,
                           -1,
                           fd,
@@ -1476,10 +1635,11 @@ GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
     check_priority ((prio ==
                      GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority
                     : prio);
+  t->lifeness = current_lifeness;
   t->next = pending;
   pending = t;
   max_priority_added = GNUNET_MAX (max_priority_added,
-                                         t->priority);
+                                  t->priority);
 #if DEBUG_TASKS
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Adding task: %llu / %p\n", t->id, t->callback_cls);