Skip to content

Workqueues

include/thread/workqueue.h View source View on GitHub
struct work_args (2 fields) source
§ void *arg1
§ void *arg2
struct work (5 fields) source
§ struct work_args args
§ struct list_node list_node
§ atomic_bool enqueued
§ atomic_bool active
struct delayed_work (3 fields) source
§ struct work work
§ struct timer timer
§ struct workqueue *wq
struct worker (12 fields) source
§ struct thread *thread

Assoc. thread.

§ struct workqueue *workqueue

Assoc. wq

§ time_ms_t last_active

Monotonic starting time of most recent work execution

§ time_ms_t inactivity_check_period

How much time in between timeout GC events

§ time_ms_t start_idle

Monotonic starting time of most recent idle

Internal flags

§ 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_node list_node
struct workqueue_stats (10 fields) source
TODO

Get in profiling.h and put these under there

§ uint64_t total_tasks_added

Total # of tasks submitted to the queue

§ uint64_t total_tasks_executed

Number of tasks successfully executed

§ uint64_t total_workers_spawned

Total worker threads spawned

§ uint64_t total_worker_exits

Total workers that exited

§ uint64_t max_queue_length

Max observed length of the task queue

§ uint64_t current_queue_length

Current length of the queue

§ uint64_t total_spawn_attempts

times spawn_worker was attempted

§ uint64_t total_spawn_failures

Number of times spawn_worker failed

§ uint64_t num_idle_workers

Snapshot of current idle workers

§ uint64_t num_active_workers

Snapshot of current active workers

struct workqueue_attributes (7 fields) source
§ size_t min_workers

If set to 0, this field will be treated as a “1”

§ size_t max_workers
§ time_ms_t spawn_delay
§ nice_t worker_niceness
§ struct idle_check
§ uint64_t min
§ uint64_t max
§ enum workqueue_flags flags
§ struct cpu_mask worker_cpu_mask
struct workqueue (23 fields) source
§ char *name
§ atomic_bool ignore_timeouts
§ struct spinlock work_lock

For works

§ struct spinlock worker_lock

For worker list

§ struct spinlock worker_array_lock

For worker array

§ struct spinlock lock

For condvar

§ struct condvar queue_cv
§ struct list_head workers
§ struct list_head works
§ struct worker *worker_array

if STATIC_WORKER is needed

§ atomic_bool spawn_pending

Some enqueue wants us to spawn a worker

§ atomic_uint32_t num_tasks

How many tasks do we have in the ringbuf

§ atomic_uint32_t num_workers

Current # workers

§ atomic_uint32_t idle_workers

idle

§ cpu_id_t core
§ time_ms_t last_spawn_attempt
§ atomic_flag spawner_flag_internal
§ struct workqueue_attributes attrs
§ atomic(enum workqueue_state) state

Atomic to avoid race where stale state is seen

§ struct thread_request *request
§ refcount_t refcount
enum worker_next_action (2 variants) source
§ WORKER_NEXT_ACTION_RUN
§ WORKER_NEXT_ACTION_EXIT
enum workqueue_flags : uint16_t (13 variants) source
§ WORKQUEUE_FLAG_PERMANENT = 1 << 1

Inverse: On-demand

Permanent workqueues are attached to each core and are always Active workqueues with on-demand worker spawning

§ WORKQUEUE_FLAG_AUTO_SPAWN = 1 << 2

Inverse: No auto spawn

This flag allows workqueues with multiple workers to spawn workers automatically if they detect that workers are busy.

Otherwise, that doesn’t happen, and workers are manually spawned

§ WORKQUEUE_FLAG_NAMED = 1 << 3

Has name - will honor (fmt, …)

§ WORKQUEUE_FLAG_STATIC_WORKERS = 1 << 4

struct worker will be statically allocated during workqueue creation.

This allows allocators to safely use workqueues that dynamically spawn threads, but shouldn’t be used everywhere because it can waste memory

§ WORKQUEUE_FLAG_NO_WORKER_GC = 1 << 5

Do not timeout workers

§ WORKQUEUE_FLAG_ISR_SAFE = 1 << 6
§ WORKQUEUE_FLAG_NO_AUTO_SPAWN = 0

Do not auto spawn workers

§ WORKQUEUE_FLAG_ON_DEMAND = 0

Inverse of a permanent workqueue

§ 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 (3 variants) source
§ WORKQUEUE_STATE_DEAD

Gone, about to be freed

§ WORKQUEUE_STATE_DESTROYING

Destroying - Do not spawn threads

§ WORKQUEUE_STATE_ACTIVE

Active

enum workqueue_error : int32_t (7 variants) source

Positive values are success with a message, zero is success with nothing special.

Negative values are errors

§ WORKQUEUE_ERROR_NEED_NEW_WORKER = 4

For manual worker spawn

§ WORKQUEUE_ERROR_NEED_NEW_WQ = 3

All worker slots filled

§ WORKQUEUE_ERROR_OK = 0

No message

§ WORKQUEUE_ERROR_FULL = -1

Full ringbuffer

§ WORKQUEUE_ERROR_WLIST_EXECUTING = -2

Worklist executing

§ WORKQUEUE_ERROR_UNUSABLE = -3

Being destroyed, etc.

§ WORKQUEUE_ERROR_WORK_EXECUTING = -4
typedef void (*work_function)(void * arg, void * arg2)
source
void delayed_work_init(
    struct delayed_work *dwork,
    work_function fn,
    struct work_args args
)
source
bool delayed_work_schedule(
    struct delayed_work *dwork,
    time_ms_t delay_ms
)
source
bool delayed_work_schedule_on(
    struct workqueue *wq,
    struct delayed_work *dwork,
    time_ms_t delay_ms
)
source
bool delayed_work_cancel(struct delayed_work *dwork)
source
bool delayed_work_cancel_sync(struct delayed_work *dwork)
source
void workqueues_permanent_init(void)
source
struct workqueue * workqueue_create_internal(
    struct workqueue_attributes *attrs,
    const char *fmt
)
source
struct workqueue * workqueue_create_default(const char *fmt)
source
struct work * work_create(work_function func, struct work_args args)
source
struct work * work_init(
    struct work *work,
    work_function fn,
    struct work_args args
)
source
void workqueue_free(struct workqueue *queue)
source
enum workqueue_error workqueue_enqueue(
    struct workqueue *queue,
    struct work *work
)
source
void work_execute(struct work *cc_nonnull_ptr)
source
bool workqueue_should_spawn_worker(struct workqueue *queue)
source
void workqueue_kick(struct workqueue *queue)
source
void workqueue_destroy(struct workqueue *cc_nonnull_ptr)
source
void worker_main(void * *)
source
bool work_active(struct work *work)
source
#define WORK_ARGS(a, b)
source
Expansion
#define WORK_ARGS(a, b) ((struct work_args) {.arg1 = a, .arg2 = b})
#define WORKQUEUE_FLAG_SET(q, f)
source
Expansion
#define WORKQUEUE_FLAG_SET(q, f) (q->attrs.flags |= f)
#define WORKQUEUE_FLAG_UNSET(q, f)
source
Expansion
#define WORKQUEUE_FLAG_UNSET(q, f) (q->attrs.flags &= ~f)
#define WORKQUEUE_FLAG_TEST(q, f)
source
Expansion
#define WORKQUEUE_FLAG_TEST(q, f) (q->attrs.flags & f)
#define WORKQUEUE_STATE_SET(q, s)
source
Expansion
#define WORKQUEUE_STATE_SET(q, s) (atomic_store(&q->state, s))
#define WORKQUEUE_STATE_GET(q)
source
Expansion
#define WORKQUEUE_STATE_GET(q) (atomic_load(&q->state))
#define WORKQUEUE_DEFAULT_MAX_WORKERS 16
source
#define WORKQUEUE_DEFAULT_SPAWN_DELAY 150
source
#define WORKQUEUE_DEFAULT_MIN_IDLE_CHECK SECONDS_TO_MS(2)
source
#define WORKQUEUE_DEFAULT_MAX_IDLE_CHECK SECONDS_TO_MS(40)
source
#define WORKQUEUE_DEFAULT_IDLE_CHECK
source
#define workqueue_create(name, attrs)
source
Expansion
#define workqueue_create(name, attrs) \
    workqueue_create_internal((attrs), PP_UNPAREN(name))