1
0
mirror of https://github.com/Zygo/bees.git synced 2025-05-17 13:25:45 +02:00

task: replace waiting state with run/exec counter

Task::run() would schedule a new execution of Task, unless it was waiting
on a queue for execution.  This cannot be implemented with a bool,
since a Task might be included in multiple queues, and should still be
in waiting state even when executed in that case.

Replace the bool with a counter.  run() and append() (but not
append_nolock) increment the counter, exec() decrements the counter.
If the counter is non-zero when run() or append() is called, the Task
is not scheduled.

Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
Zygo Blaxell 2021-05-30 00:35:55 -04:00
parent d5ff35eacf
commit 0928362aab
2 changed files with 30 additions and 29 deletions

View File

@ -27,17 +27,20 @@ namespace crucible {
/// Create Task object containing closure and description. /// Create Task object containing closure and description.
Task(string title, function<void()> exec_fn); Task(string title, function<void()> exec_fn);
/// Schedule Task for at least one future execution. /// Schedule Task for at most one future execution.
/// May run Task in current thread or in other thread. /// May run Task in current thread or in other thread.
/// May run Task before or after returning. /// May run Task before or after returning.
/// Schedules Task at the end of the global execution queue.
/// ///
/// Only one instance of a Task may execute at a time. /// Only one instance of a Task may execute at a time.
/// If a Task is already scheduled, run() does nothing. /// If a Task is already scheduled, run() does nothing.
/// If a Task is already running, run() reschedules the /// If a Task is already running when a new instance reaches
/// task after the currently running instance returns. /// the front of the queue, the new instance will execute
/// after the current instance exits.
void run() const; void run() const;
/// Schedule Task to run after this task has run at least once. /// Schedule Task to run after this Task has run or
/// been destroyed.
void append(const Task &task) const; void append(const Task &task) const;
/// Describe Task as text. /// Describe Task as text.

View File

@ -54,9 +54,8 @@ namespace crucible {
/// Tasks to be executed after the current task is executed /// Tasks to be executed after the current task is executed
list<TaskStatePtr> m_post_exec_queue; list<TaskStatePtr> m_post_exec_queue;
/// Set when task is waiting to execute. /// Incremented by run() and append(). Decremented by exec().
/// Cleared when exec() begins. size_t m_run_count = 0;
bool m_is_waiting = false;
/// Set when task starts execution by exec(). /// Set when task starts execution by exec().
/// Cleared when exec() ends. /// Cleared when exec() ends.
@ -95,15 +94,16 @@ namespace crucible {
~TaskState(); ~TaskState();
TaskState(string title, function<void()> exec_fn); TaskState(string title, function<void()> exec_fn);
/// Run the task at least once. If task is already running, appends /// Run the task at most one more time. If task has
/// a self-reference to its after queue. If task is not running /// already started running, a new instance is scheduled.
/// and task is not waiting, adds task to a master queue and marks /// If an instance is already scheduled by run() or
/// the task waiting. /// append(), does nothing. Otherwise, schedules a new
/// instance at the end of TaskMaster's global queue.
void run(); void run();
/// Execute task immediately in current thread if it is not already /// Execute task immediately in current thread if it is not already
/// executing in another thread. If m_run_again is set while m_is_running /// executing in another thread; otherwise, append the current task
/// is true, the thread that set m_is_running will requeue the task. /// to itself to be executed immediately in the other thread.
void exec(); void exec();
/// Return title of task. /// Return title of task.
@ -112,9 +112,8 @@ namespace crucible {
/// Return ID of task. /// Return ID of task.
TaskId id() const; TaskId id() const;
/// Queue task to execute after current task finishes executing. /// Queue task to execute after current task finishes executing
/// If current task is neither running nor waiting, this /// or is destroyed.
/// places the argument task on a worker queue immediately.
void append(const TaskStatePtr &task); void append(const TaskStatePtr &task);
/// How masy Tasks are there? Good for catching leaks /// How masy Tasks are there? Good for catching leaks
@ -204,7 +203,6 @@ namespace crucible {
// then push it to the front of the global queue using normal locking methods. // then push it to the front of the global queue using normal locking methods.
TaskStatePtr rescue_task(make_shared<TaskState>("rescue_task", [](){})); TaskStatePtr rescue_task(make_shared<TaskState>("rescue_task", [](){}));
swap(rescue_task->m_post_exec_queue, queue); swap(rescue_task->m_post_exec_queue, queue);
rescue_task->m_is_waiting;
TaskQueue tq_one { rescue_task }; TaskQueue tq_one { rescue_task };
TaskMasterState::push_front(tq_one); TaskMasterState::push_front(tq_one);
} }
@ -259,19 +257,20 @@ namespace crucible {
void void
TaskState::append_nolock(const TaskStatePtr &task) TaskState::append_nolock(const TaskStatePtr &task)
{ {
task->m_is_waiting = true; THROW_CHECK0(invalid_argument, task);
m_post_exec_queue.push_back(task); m_post_exec_queue.push_back(task);
} }
void void
TaskState::append(const TaskStatePtr &task) TaskState::append(const TaskStatePtr &task)
{ {
if (!task) { THROW_CHECK0(invalid_argument, task);
return;
}
PairLock lock(m_mutex, task->m_mutex); PairLock lock(m_mutex, task->m_mutex);
if (!task->m_run_count) {
++task->m_run_count;
append_nolock(task); append_nolock(task);
} }
}
void void
TaskState::exec() TaskState::exec()
@ -280,11 +279,11 @@ namespace crucible {
THROW_CHECK0(invalid_argument, !m_title.empty()); THROW_CHECK0(invalid_argument, !m_title.empty());
unique_lock<mutex> lock(m_mutex); unique_lock<mutex> lock(m_mutex);
m_is_waiting = false;
if (m_is_running) { if (m_is_running) {
append_nolock(shared_from_this()); append_nolock(shared_from_this());
return; return;
} else { } else {
--m_run_count;
m_is_running = true; m_is_running = true;
} }
lock.unlock(); lock.unlock();
@ -327,10 +326,11 @@ namespace crucible {
TaskState::run() TaskState::run()
{ {
unique_lock<mutex> lock(m_mutex); unique_lock<mutex> lock(m_mutex);
if (!m_is_waiting) { if (m_run_count) {
TaskMasterState::push_back(shared_from_this()); return;
m_is_waiting = true;
} }
++m_run_count;
TaskMasterState::push_back(shared_from_this());
} }
TaskMasterState::TaskMasterState(size_t thread_max) : TaskMasterState::TaskMasterState(size_t thread_max) :
@ -636,9 +636,7 @@ namespace crucible {
Task::append(const Task &that) const Task::append(const Task &that) const
{ {
THROW_CHECK0(runtime_error, m_task_state); THROW_CHECK0(runtime_error, m_task_state);
if (!that) { THROW_CHECK0(runtime_error, that);
return;
}
m_task_state->append(that.m_task_state); m_task_state->append(that.m_task_state);
} }