/*
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).
* future), but only if the return value is "GNUNET_NO" (and
* the "lowest_pending_id" check failed).
*
- * @param sched the scheduler
* @param id which task are we checking for
* @return GNUNET_YES if so, GNUNET_NO if not
*/
/**
* Update all sets and timeout for select.
*
- * @param sched the scheduler
* @param rs read-set, set to all FDs we would like to read (updated)
* @param ws write-set, set to all FDs we would like to write (updated)
* @param timeout next timeout (updated)
* Check if the given task is eligible to run now.
* Also set the reason why it is eligible.
*
- * @param sched the scheduler
* @param task task to check if it is ready
* @param now the current time
* @param rs set of FDs ready for reading
/**
* Put a task that is ready for execution into the ready queue.
*
- * @param handle the scheduler
* @param task task ready for execution
*/
static void
* 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
*/
* 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 ()
* may become ready, hence we should select on it). Naturally, if
* there are no more ready tasks, we also return.
*
- * @param sched the scheduler
* @param rs FDs ready for reading
* @param ws FDs ready for writing
*/
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);
- system (lsof);
+ (void) close (1);
+ (void) dup2 (2, 1);
+ if (0 != system (lsof))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "system");
#endif
#endif
abort ();
* 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
* dropped (if the decision should be made based on the number of
* tasks ready to run).
*
- * @param sched scheduler to query
* @param p priority level to look at
* @return number of tasks pending right now
*/
* Cancel the task with the specified identifier.
* The task must not yet have run.
*
- * @param sched scheduler to use
* @param task id of the task to cancel
* @return original closure of the 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)
* similar to the other "add" functions except that there is no delay
* and the reason code can be specified.
*
- * @param sched scheduler to use
* @param task main function of the task
* @param task_cls closure for 'main'
* @param reason reason for task invocation
#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",
* has completed. It will be run with the priority of the calling
* task.
*
- * @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-readiness
/**
* 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
* 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 delay when should this operation time out? Use
* GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
* @param task main function of the task
#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;
* 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
*/
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;
+}
+
+
/**
* || shutdown-active )
* </code>
*
- * @param sched scheduler to use
* @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 rfd file descriptor we want to read (can be -1)
* @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,
#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,
#endif
return t->id;
}
+#endif
* socket operation is ready. It will be run with the priority of
* the calling task.
*
- * @param sched scheduler to use
* @param delay when should this operation time out? Use
* GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
* @param rfd read file-descriptor
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
}
* socket operation is ready. It will be run with the priority of
* the calling task.
*
- * @param sched scheduler to use
* @param delay when should this operation time out? Use
* GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
* @param wfd write file-descriptor
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
}
* socket operation is ready. It will be run with the priority of
* the calling task.
*
- * @param sched scheduler to use
* @param delay when should this operation time out? Use
* GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
* @param rfd read file-descriptor
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);
* socket operation is ready. It will be run with the priority of
* the calling task.
*
- * @param sched scheduler to use
* @param delay when should this operation time out? Use
* GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
* @param wfd write file-descriptor
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);
int fd;
GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
+ GNUNET_assert(fd >= 0);
return add_without_sets (delay,
-1,
fd,
* || (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
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);