Skip to content

Workqueues

include/thread/workqueue.h View source View on GitHub
struct work_args {
    void  *arg1;
    void  *arg2;
};
struct deferred_event {
    size_t                 timer;
    timestamp_t            timestamp_ms;
    work_function          callback;
    struct work_args       args;
    struct deferred_event  *next;
};
struct work {
    work_function     func;
    struct work_args  args;
    struct list_head  list_node;
    atomic_bool       enqueued;
    atomic_bool       active;
    _Atomic uint64_t  seq;
};
struct worker {
    struct thread            *thread;
    struct workqueue         *workqueue;
    time_t                   last_active;
    time_t                   inactivity_check_period;
    time_t                   start_idle;
    bool                     timeout_ran : 1;
    bool                     should_exit : 1;
    bool                     is_permanent : 1;
    bool                     present : 1;
    bool                     idle : 1;
    enum worker_next_action  next_action;
    struct list_head         list_node;
};
struct workqueue_stats {
    uint64_t  total_tasks_added;
    uint64_t  total_tasks_executed;
    uint64_t  total_workers_spawned;
    uint64_t  total_worker_exits;
    uint64_t  max_queue_length;
    uint64_t  current_queue_length;
    uint64_t  total_spawn_attempts;
    uint64_t  total_spawn_failures;
    uint64_t  num_idle_workers;
    uint64_t  num_active_workers;
};
struct workqueue_attributes {
    size_t                min_workers;
    size_t                max_workers;
    size_t                capacity;
    time_t                spawn_delay;
    nice_t                worker_niceness;
    struct {
        uint64_t  min;
        uint64_t  max;
    } idle_check;
    enum workqueue_flags  flags;
    struct cpu_mask       worker_cpu_mask;
};
struct workqueue {
    char                          *name;
    atomic_bool                   ignore_timeouts;
    struct spinlock               work_lock;
    struct spinlock               worker_lock;
    struct spinlock               worker_array_lock;
    struct spinlock               lock;
    struct condvar                queue_cv;
    struct work                   *oneshot_works;
    struct list_head              workers;
    struct list_head              works;
    struct worker                 *worker_array;
    _Atomic uint64_t              head;
    _Atomic uint64_t              tail;
    atomic_bool                   spawn_pending;
    _Atomic uint32_t              num_tasks;
    _Atomic uint32_t              num_workers;
    _Atomic uint32_t              idle_workers;
    cpu_id_t                      core;
    time_t                        last_spawn_attempt;
    atomic_flag                   spawner_flag_internal;
    struct workqueue_attributes   attrs;
    _Atomic enum workqueue_state  state;
    struct thread_request         *request;
    refcount_t                    refcount;
};
enum worker_next_action {
    WORKER_NEXT_ACTION_RUN,
    WORKER_NEXT_ACTION_EXIT,
};
enum workqueue_flags : uint16_t {
    WORKQUEUE_FLAG_PERMANENT = 1 << 1,
    WORKQUEUE_FLAG_AUTO_SPAWN = 1 << 2,
    WORKQUEUE_FLAG_NAMED = 1 << 3,
    WORKQUEUE_FLAG_STATIC_WORKERS = 1 << 4,
    WORKQUEUE_FLAG_NO_WORKER_GC = 1 << 5,
    WORKQUEUE_FLAG_ISR_SAFE = 1 << 6,
    WORKQUEUE_FLAG_NO_AUTO_SPAWN = 0,
    WORKQUEUE_FLAG_ON_DEMAND = 0,
    WORKQUEUE_FLAG_NAMELESS = 0,
    WORKQUEUE_FLAG_NON_STATIC_WORKERS = 0,
    WORKQUEUE_FLAG_WORKER_GC = 0,
    WORKQUEUE_FLAG_NON_ISR_SAFE = 0,
    WORKQUEUE_FLAG_DEFAULTS = WORKQUEUE_FLAG_AUTO_SPAWN | WORKQUEUE_FLAG_NAMED,
};
enum workqueue_state : uint16_t {
    WORKQUEUE_STATE_DEAD,
    WORKQUEUE_STATE_DESTROYING,
    WORKQUEUE_STATE_ACTIVE,
};
enum workqueue_error : int32_t {
    WORKQUEUE_ERROR_NEED_NEW_WORKER = 4,
    WORKQUEUE_ERROR_NEED_NEW_WQ = 3,
    WORKQUEUE_ERROR_OK = 0,
    WORKQUEUE_ERROR_FULL = -1,
    WORKQUEUE_ERROR_WLIST_EXECUTING = -2,
    WORKQUEUE_ERROR_UNUSABLE = -3,
    WORKQUEUE_ERROR_WORK_EXECUTING = -4,
};
typedef void (*work_function)(void * arg, void * arg2);
void defer_init(void);
bool defer_enqueue(work_function func, struct work_args args, uint64_t delay_ms);
void workqueues_permanent_init(void);
struct workqueue * workqueue_create(const char *fmt, struct workqueue_attributes *attrs);
struct workqueue * workqueue_create_default(const char *fmt);
struct work * work_create(work_function func, struct work_args args);
struct work * work_init(struct work *work, work_function fn, struct work_args args);
void workqueue_free(struct workqueue *queue);
enum workqueue_error workqueue_enqueue_oneshot(struct workqueue *queue, work_function func, struct work_args args);
enum workqueue_error workqueue_enqueue(struct workqueue *queue, struct work *work);
enum workqueue_error __warn_unused_result workqueue_add_oneshot(work_function func, struct work_args args);
enum workqueue_error __warn_unused_result workqueue_add_remote_oneshot(work_function func, struct work_args args);
enum workqueue_error __warn_unused_result workqueue_add_local_oneshot(work_function func, struct work_args args);
enum workqueue_error __warn_unused_result workqueue_add_fast_oneshot(work_function func, struct work_args args);
enum workqueue_error __warn_unused_result workqueue_add(struct work *work);
enum workqueue_error __warn_unused_result workqueue_add_remote(struct work *work);
enum workqueue_error __warn_unused_result workqueue_add_local(struct work *work);
enum workqueue_error __warn_unused_result workqueue_add_fast(struct work *work);
void work_execute(struct work *task);
bool workqueue_should_spawn_worker(struct workqueue *queue);
void workqueue_kick(struct workqueue *queue);
void workqueue_destroy(struct workqueue *queue);
void worker_main(void * *);
bool work_active(struct work *work);
#define WORK_ARGS(a, b) ((struct work_args) {.arg1 = a, .arg2 = b})
#define WORKQUEUE_FLAG_SET(q, f) (q->attrs.flags |= f)
#define WORKQUEUE_FLAG_UNSET(q, f) (q->attrs.flags &= ~f)
#define WORKQUEUE_FLAG_TEST(q, f) (q->attrs.flags & f)
#define WORKQUEUE_STATE_SET(q, s) (atomic_store(&q->state, s))
#define WORKQUEUE_STATE_GET(q) (atomic_load(&q->state))
#define WORKQUEUE_DEFAULT_CAPACITY 512
#define WORKQUEUE_DEFAULT_MAX_WORKERS 16
#define WORKQUEUE_DEFAULT_SPAWN_DELAY 150
#define WORKQUEUE_DEFAULT_MIN_IDLE_CHECK SECONDS_TO_MS(2)
#define WORKQUEUE_DEFAULT_MAX_IDLE_CHECK SECONDS_TO_MS(40)
#define WORKQUEUE_DEFAULT_IDLE_CHECK \
    {.max = WORKQUEUE_DEFAULT_MAX_IDLE_CHECK, \
     .min = WORKQUEUE_DEFAULT_MIN_IDLE_CHECK}