1#include <stdarg.h>
2#include <thread/workqueue.h>
3
4#define work_from_worklist_node(node) container_of(node, struct work, list_node)
5
6static inline enum irql workqueue_lock(struct workqueue *workqueue)
7 TSA_ACQUIRES_SPIN(&workqueue->lock) {
8 if (workqueue->attrs.flags & WORKQUEUE_FLAG_ISR_SAFE) {
9 return spin_lock_high(&workqueue->lock);
10 } else {
11 return spin_lock(&workqueue->lock);
12 }
13}
14
15#define WORKQUEUE_NUM_WORKS(wq) (atomic_load(&wq->num_tasks))
16
17static inline uint64_t workqueue_current_worker_count(struct workqueue *q) {
18 return atomic_load(&q->num_workers);
19}
20
21static inline bool workqueue_works_empty(struct workqueue *queue) {
22 enum irql irql = spin_lock_high(&queue->work_lock);
23 bool empty = list_head_empty(h: &queue->works);
24 spin_unlock(&queue->work_lock, irql);
25 return empty;
26}
27
28static inline bool workqueue_empty(struct workqueue *queue) {
29 return atomic_load(&queue->num_tasks) == 0;
30}
31
32static inline void workqueue_set_needs_spawn(struct workqueue *queue,
33 bool needs) {
34 atomic_store(&queue->spawn_pending, needs);
35}
36
37static inline bool workqueue_needs_spawn(struct workqueue *queue) {
38 return atomic_load(&queue->spawn_pending);
39}
40
41static inline bool workqueue_getref(struct workqueue *queue) {
42 return refcount_inc(rc: &queue->refcount);
43}
44
45static inline void workqueue_putref(struct workqueue *queue) {
46 if (refcount_dec_and_test(rc: &queue->refcount))
47 return workqueue_free(queue);
48}
49
50/* Regardless of if we use static workers or not this will
51 * always be the list of workers we have */
52static inline void workqueue_add_worker(struct workqueue *wq,
53 struct worker *wker) {
54 enum irql irql = spin_lock_high(&wq->worker_lock);
55 list_head_push_front(h: &wq->workers, n: &wker->list_node);
56 spin_unlock(&wq->worker_lock, irql);
57}
58
59static inline void workqueue_remove_worker(struct workqueue *wq,
60 struct worker *worker) {
61 enum irql irql = spin_lock_high(&wq->worker_lock);
62 list_node_delete_init(n: &worker->list_node);
63 spin_unlock(&wq->worker_lock, irql);
64}
65
66static inline bool ignore_timeouts(struct workqueue *q) {
67 return atomic_load(&q->ignore_timeouts);
68}
69
70static inline bool workqueue_usable(struct workqueue *q) {
71 return atomic_load(&q->state) == WORKQUEUE_STATE_ACTIVE;
72}
73
74static inline size_t workqueue_workers(struct workqueue *wq) {
75 return atomic_load(&wq->num_workers);
76}
77
78static inline size_t workqueue_idlers(struct workqueue *wq) {
79 return atomic_load(&wq->idle_workers);
80}
81
82bool workqueue_try_spawn_worker(struct workqueue *queue);
83bool workqueue_dequeue_task(struct workqueue *queue, struct work **out);
84void workqueue_link_thread_and_worker(struct worker *worker,
85 struct thread *thread);
86bool workqueue_spawn_worker_internal(struct workqueue *queue);
87struct workqueue *workqueue_least_loaded_queue_except(int64_t except_core_num);
88struct workqueue *workqueue_get_least_loaded(void);
89struct workqueue *workqueue_get_least_loaded_remote(void);
90struct worker *workqueue_spawn_permanent_worker(struct workqueue *queue);
91struct workqueue *workqueue_create_full(struct workqueue_attributes *attrs,
92 const char *fmt, va_list args);
93enum thread_request_decision workqueue_request_callback(struct thread *t,
94 void *data);
95struct thread *worker_create(struct cpu_mask mask, nice_t niceness);
96