mirror of
https://github.com/Zygo/bees.git
synced 2025-05-18 05:45:45 +02:00
It turns out I've been using pthread_setname_np wrong the whole time: * on Linux, the thread name length is 15 characters. TASK_COMM_LEN is 16 bytes, and the last one is always 0. This is now hardcoded in many places and cannot be changed. * pthread_setname_np doesn't return -errno, so DIE_IF_MINUS_ERRNO was the wrong macro. On the other hand, we never want to do anything differently when pthread_setname_np fails, so we never needed to check the return value. Also, libc silently ignores attempts to set the thread name when it is too long. That's almost certainly a libc bug, but libc probably suppresses the error result for the same reasons I ignore the error result. Wrap the pthread_setname function with a C++ std::string overload that truncates the argument at 15 characters, so we at least get the first part of the task name in the thread name field. Later commits can deal with making the bees thread names shorter. Also wrap pthread_getname for symmetry. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
185 lines
5.2 KiB
C++
185 lines
5.2 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();
|
|
|
|
/// Gets the current load tracking statistics
|
|
struct LoadStats {
|
|
/// Current load extracted from last two 5-second load average samples
|
|
double current_load;
|
|
/// Target thread count computed from previous thread count and current load
|
|
double thread_target;
|
|
/// Load average for last 60 seconds
|
|
double loadavg;
|
|
};
|
|
static LoadStats get_current_load();
|
|
|
|
/// 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();
|
|
|
|
/// Stop running any new Tasks. All existing
|
|
/// Consumer threads will exit. Does not affect queue.
|
|
/// Does not wait for threads to exit. Reversible.
|
|
static void pause(bool paused = true);
|
|
};
|
|
|
|
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);
|
|
};
|
|
|
|
/// Wrapper around pthread_setname_np which handles length limits
|
|
void pthread_setname(const string &name);
|
|
|
|
/// Wrapper around pthread_getname_np for symmetry
|
|
string pthread_getname();
|
|
}
|
|
|
|
#endif // CRUCIBLE_TASK_H
|