/*
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.
*/
* 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
/**
*/
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).
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 >
}
+/**
+ * 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
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);
#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 ();
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)
#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",
#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;
*/
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,
}
+/**
+ * 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;
+}
+
+
/**
#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
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,
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),
int fd;
GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
+ GNUNET_assert(fd >= 0);
return add_without_sets (delay,
-1,
fd,
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);