/*
This file is part of GNUnet
- (C) 2009, 2011 Christian Grothoff (and other contributing authors)
+ (C) 2009-2013 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
- by the Free Software Foundation; either version 2, or (at your
+ by the Free Software Foundation; either version 3, or (at your
option) any later version.
GNUnet is distributed in the hope that it will be useful, but
* @author Christian Grothoff
*/
#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"
+#include "gnunet_util_lib.h"
#include "disk.h"
#define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
-#ifdef LINUX
+#if HAVE_EXECINFO_H
#include "execinfo.h"
/**
/**
* Function to use as a select() in the scheduler.
- * If NULL, we use GNUNET_NETWORK_socket_select ().
+ * If NULL, we use GNUNET_NETWORK_socket_select().
*/
static GNUNET_SCHEDULER_select scheduler_select;
* Sets the select function to use in the scheduler (scheduler_select).
*
* @param new_select new select function to use
- * @param new_select_cls closure for 'new_select'
+ * @param new_select_cls closure for @a new_select
* @return previously used select function, NULL for default
*/
void
if (pos != NULL)
{
to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
- if (timeout->rel_value > to.rel_value)
+ if (timeout->rel_value_us > to.rel_value_us)
*timeout = to;
if (pos->reason != 0)
*timeout = GNUNET_TIME_UNIT_ZERO;
pos = pending;
while (pos != NULL)
{
- if (pos->timeout.abs_value != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
+ if (pos->timeout.abs_value_us != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
{
to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
- if (timeout->rel_value > to.rel_value)
+ if (timeout->rel_value_us > to.rel_value_us)
*timeout = to;
}
if (pos->read_fd != -1)
* @param now the current time
* @param rs set of FDs ready for reading
* @param ws set of FDs ready for writing
- * @return GNUNET_YES if we can run it, GNUNET_NO if not.
+ * @return #GNUNET_YES if we can run it, #GNUNET_NO if not.
*/
static int
is_ready (struct Task *task, struct GNUNET_TIME_Absolute now,
enum GNUNET_SCHEDULER_Reason reason;
reason = task->reason;
- if (now.abs_value >= task->timeout.abs_value)
+ if (now.abs_value_us >= task->timeout.abs_value_us)
reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
(((task->read_fd != -1) &&
reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
if (reason == 0)
return GNUNET_NO; /* not ready */
- reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
+ reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
task->reason = reason;
return GNUNET_YES;
}
while (pos != NULL)
{
next = pos->next;
- if (now.abs_value >= pos->timeout.abs_value)
+ if (now.abs_value_us >= pos->timeout.abs_value_us)
pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
if (0 == pos->reason)
break;
pos = pending;
while (pos != NULL)
{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
"Checking readiness of task: %llu / %p\n",
pos->id, pos->callback_cls);
next = pos->next;
GNUNET_assert (pos != NULL); /* ready_count wrong? */
ready[p] = pos->next;
ready_count--;
- if (current_priority != pos->priority)
- {
- current_priority = pos->priority;
- (void) GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
- pos->priority);
- }
+ current_priority = pos->priority;
current_lifeness = pos->lifeness;
active_task = pos;
#if PROFILE_DELAYS
- if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value >
- DELAY_THRESHOLD.rel_value)
+ if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value_us >
+ DELAY_THRESHOLD.rel_value_us)
{
- LOG (GNUNET_ERROR_TYPE_ERROR, "Task %llu took %llums to be scheduled\n",
- pos->id,
- (unsigned long long)
- GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Task %llu took %s to be scheduled\n",
+ (unsigned long long) pos->id,
+ GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (pos->start_time), GNUNET_YES));
}
#endif
tc.reason = pos->reason;
(pos->write_fd != -1) &&
(!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
GNUNET_abort (); // added to ready in previous select loop!
- LOG (GNUNET_ERROR_TYPE_DEBUG,
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
"Running task: %llu / %p\n", pos->id,
pos->callback_cls);
pos->callback (pos->callback_cls, &tc);
return;
}
#endif
+
+
+/**
+ * Wait for a short time.
+ * Sleeps for @a ms ms (as that should be long enough for virtually all
+ * modern systems to context switch and allow another process to do
+ * some 'real' work).
+ *
+ * @param ms how many ms to wait
+ */
+static void
+short_wait (unsigned int ms)
+{
+ struct GNUNET_TIME_Relative timeout;
+
+ timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, ms);
+ (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout);
+}
+
+
/**
* Signal handler called for signals that should cause us to 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
+ * @return #GNUNET_OK to continue the main loop,
+ * #GNUNET_NO to exit
*/
static int
check_lifeness ()
GNUNET_DISK_PIPE_END_READ);
GNUNET_assert (pr != NULL);
my_pid = getpid ();
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering signal handlers\n");
shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
#ifndef MINGW
GNUNET_abort ();
break;
}
- if ((ret == 0) && (timeout.rel_value == 0) && (busy_wait_warning > 16))
+ if ((0 == ret) && (0 == timeout.rel_value_us) && (busy_wait_warning > 16))
{
LOG (GNUNET_ERROR_TYPE_WARNING, _("Looks like we're busy waiting...\n"));
- sleep (1); /* mitigate */
+ short_wait (100); /* mitigate */
}
check_ready (rs, ws);
run_ready (rs, ws);
}
if (last_tr == tasks_run)
{
+ short_wait (1);
busy_wait_warning++;
}
else
GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
GNUNET_SCHEDULER_Task task, void *task_cls)
{
- return GNUNET_SCHEDULER_add_select (prio,
+ return GNUNET_SCHEDULER_add_select (prio,
GNUNET_TIME_UNIT_ZERO, NULL, NULL, task,
task_cls);
}
prev = pending_timeout_last;
if (prev != NULL)
{
- if (prev->timeout.abs_value > t->timeout.abs_value)
+ if (prev->timeout.abs_value_us > t->timeout.abs_value_us)
prev = NULL;
else
pos = prev->next; /* heuristic success! */
pos = pending_timeout;
}
while ((pos != NULL) &&
- ((pos->timeout.abs_value <= t->timeout.abs_value) ||
- (pos->reason != 0)))
+ ((pos->timeout.abs_value_us <= t->timeout.abs_value_us) ||
+ (0 != pos->reason)))
{
prev = pos;
pos = pos->next;
/**
- * Schedule a new task to be run as soon as possible. The task
- * will be run with the DEFAULT priority.
+ * Schedule a new task to be run as soon as possible. Note that this
+ * does not guarantee that this will be the next task that is being
+ * run, as other tasks with higher priority (or that are already ready
+ * to run) might get to run first. Just as with delays, clients must
+ * not rely on any particular order of execution between tasks
+ * scheduled concurrently.
+ *
+ * The task will be run with the DEFAULT priority.
*
* @param task main function of the task
- * @param task_cls closure of task
+ * @param task_cls closure of @a task
* @return unique task identifier for the job
* only valid until "task" is started!
*/
*/
#ifndef MINGW
static GNUNET_SCHEDULER_TaskIdentifier
-add_without_sets (struct GNUNET_TIME_Relative delay,
+add_without_sets (struct GNUNET_TIME_Relative delay,
enum GNUNET_SCHEDULER_Priority priority,
int rfd, int wfd,
GNUNET_SCHEDULER_Task task, void *task_cls)
* either the delay has expired or the socket operation is ready. It
* will be run with the DEFAULT priority.
*
- * * @param delay when should this operation time out? Use
+ * @param delay when should this operation time out? Use
* GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param priority priority to use for the task
* @param rfd read file-descriptor
* @param task main function of the task
* @param task_cls closure of task
GNUNET_NETWORK_fdset_destroy (rs);
return ret;
#else
- return add_without_sets (delay,
+ return add_without_sets (delay,
priority,
GNUNET_NETWORK_get_fd (rfd), -1, task,
task_cls);
return ret;
#else
GNUNET_assert (GNUNET_NETWORK_get_fd (wfd) >= 0);
- return add_without_sets (delay,
+ return add_without_sets (delay,
GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-1, GNUNET_NETWORK_get_fd (wfd), task,
task_cls);
int fd;
GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
- return add_without_sets (delay,
+ return add_without_sets (delay,
GNUNET_SCHEDULER_PRIORITY_DEFAULT,
fd, -1, task, task_cls);
GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
GNUNET_assert (fd >= 0);
- return add_without_sets (delay,
+ return add_without_sets (delay,
GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-1, fd, task, task_cls);
void *backtrace_array[MAX_TRACE_DEPTH];
#endif
- GNUNET_assert (active_task != NULL);
+ GNUNET_assert (NULL != active_task);
GNUNET_assert (NULL != task);
t = GNUNET_malloc (sizeof (struct Task));
t->callback = task;