mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 21:35:45 +02:00
Exclusion was generating a new Task every time a lock was contended. That results in thousands of empty Task objects which contain a single Task item. Get rid of ExclusionState. Exclusion is now a simple weak_ptr to a Task. If the weak_ptr is expired, the Exclusion is unlocked. If the weak_ptr is not expired, it points to the Task which owns the Exclusion. try_lock now appends the Task attempting to lock the Exclusion directly to the owning Task, eliminating the need for Exclusion to have one. This also removes the need to call insert_task separately, though insert_task remains for other use cases. With no ExclusionState there is no need for a string argument to Exclusion's constructor, so get rid of that too. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
165 lines
4.5 KiB
C++
165 lines
4.5 KiB
C++
#ifndef CRUCIBLE_TASK_H
|
|
#define CRUCIBLE_TASK_H
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <ostream>
|
|
#include <string>
|
|
|
|
namespace crucible {
|
|
using namespace std;
|
|
|
|
class TaskState;
|
|
|
|
using TaskId = uint64_t;
|
|
|
|
/// A unit of work to be scheduled by TaskMaster.
|
|
class Task {
|
|
shared_ptr<TaskState> m_task_state;
|
|
|
|
Task(shared_ptr<TaskState> pts);
|
|
|
|
public:
|
|
|
|
/// Create empty Task object.
|
|
Task() = default;
|
|
|
|
/// Create Task object containing closure and description.
|
|
Task(string title, function<void()> exec_fn);
|
|
|
|
/// Schedule Task for at most one future execution.
|
|
/// May run Task in current thread or in other thread.
|
|
/// 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.
|
|
/// If a Task is already scheduled, run() does nothing.
|
|
/// If a Task is already running when a new instance reaches
|
|
/// the front of the queue, the new instance will execute
|
|
/// after the current instance exits.
|
|
void run() const;
|
|
|
|
/// Schedule Task to run after this Task has run or
|
|
/// been destroyed.
|
|
void append(const Task &task) const;
|
|
|
|
/// Describe Task as text.
|
|
string title() const;
|
|
|
|
/// Returns currently executing task if called from exec_fn.
|
|
/// Usually used to reschedule the currently executing Task.
|
|
static Task current_task();
|
|
|
|
/// Returns number of currently existing Task objects.
|
|
/// Good for spotting leaks.
|
|
static size_t instance_count();
|
|
|
|
/// Ordering operator for containers
|
|
bool operator<(const Task &that) const;
|
|
|
|
/// Null test
|
|
operator bool() const;
|
|
|
|
/// Unique non-repeating(ish) ID for task
|
|
TaskId id() const;
|
|
};
|
|
|
|
ostream &operator<<(ostream &os, const Task &task);
|
|
|
|
class TaskMaster {
|
|
public:
|
|
/// Blocks until the running thread count reaches this number
|
|
static void set_thread_count(size_t threads);
|
|
|
|
/// Sets minimum thread count when load average tracking enabled
|
|
static void set_thread_min_count(size_t min_threads);
|
|
|
|
/// Calls set_thread_count with default
|
|
static void set_thread_count();
|
|
|
|
/// Creates thread to track load average and adjust thread count dynamically
|
|
static void set_loadavg_target(double target);
|
|
|
|
/// Writes the current non-executing Task queue
|
|
static ostream & print_queue(ostream &);
|
|
|
|
/// Writes the current executing Task for each worker
|
|
static ostream & print_workers(ostream &);
|
|
|
|
/// Gets the current number of queued Tasks
|
|
static size_t get_queue_count();
|
|
|
|
/// Gets the current number of active workers
|
|
static size_t get_thread_count();
|
|
|
|
/// Drop the current queue and discard new Tasks without
|
|
/// running them. Currently executing tasks are not
|
|
/// affected (use set_thread_count(0) to wait for those
|
|
/// to complete).
|
|
static void cancel();
|
|
};
|
|
|
|
class BarrierState;
|
|
|
|
/// Barrier delays the execution of one or more Tasks.
|
|
/// The Tasks are executed when the last shared reference to the
|
|
/// BarrierState is released. Copies of Barrier objects refer
|
|
/// to the same Barrier state.
|
|
class Barrier {
|
|
shared_ptr<BarrierState> m_barrier_state;
|
|
|
|
public:
|
|
Barrier();
|
|
|
|
/// Schedule a task for execution when last Barrier is released.
|
|
void insert_task(Task t);
|
|
|
|
/// Release this reference to the barrier state.
|
|
/// Last released reference executes the task.
|
|
/// Barrier can only be released once, after which the
|
|
/// object can no longer be used.
|
|
void release();
|
|
};
|
|
|
|
class ExclusionLock {
|
|
shared_ptr<Task> m_owner;
|
|
ExclusionLock(shared_ptr<Task> owner);
|
|
friend class Exclusion;
|
|
public:
|
|
/// Explicit default constructor because we have other kinds
|
|
ExclusionLock() = default;
|
|
|
|
/// Release this Lock immediately and permanently
|
|
void release();
|
|
|
|
/// Test for locked state
|
|
operator bool() const;
|
|
};
|
|
|
|
class Exclusion {
|
|
mutex m_mutex;
|
|
weak_ptr<Task> m_owner;
|
|
|
|
public:
|
|
/// Attempt to obtain a Lock. If successful, current Task
|
|
/// owns the Lock until the ExclusionLock is released
|
|
/// (it is the ExclusionLock that owns the lock, so it can
|
|
/// be passed to other Tasks or threads, but this is not
|
|
/// recommended practice).
|
|
/// If not successful, current Task is appended to the
|
|
/// task that currently holds the lock. Current task is
|
|
/// expected to release any other ExclusionLock
|
|
/// objects it holds, and exit its Task function.
|
|
ExclusionLock try_lock(const Task &task);
|
|
|
|
/// Execute Task when Exclusion is unlocked (possibly
|
|
/// immediately).
|
|
void insert_task(const Task &t);
|
|
};
|
|
|
|
|
|
}
|
|
|
|
#endif // CRUCIBLE_TASK_H
|