| 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 | |
| 6 | static 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 | |
| 17 | static inline uint64_t workqueue_current_worker_count(struct workqueue *q) { |
| 18 | return atomic_load(&q->num_workers); |
| 19 | } |
| 20 | |
| 21 | static 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 | |
| 28 | static inline bool workqueue_empty(struct workqueue *queue) { |
| 29 | return atomic_load(&queue->num_tasks) == 0; |
| 30 | } |
| 31 | |
| 32 | static inline void workqueue_set_needs_spawn(struct workqueue *queue, |
| 33 | bool needs) { |
| 34 | atomic_store(&queue->spawn_pending, needs); |
| 35 | } |
| 36 | |
| 37 | static inline bool workqueue_needs_spawn(struct workqueue *queue) { |
| 38 | return atomic_load(&queue->spawn_pending); |
| 39 | } |
| 40 | |
| 41 | static inline bool workqueue_getref(struct workqueue *queue) { |
| 42 | return refcount_inc(rc: &queue->refcount); |
| 43 | } |
| 44 | |
| 45 | static 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 */ |
| 52 | static 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 | |
| 59 | static 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 | |
| 66 | static inline bool ignore_timeouts(struct workqueue *q) { |
| 67 | return atomic_load(&q->ignore_timeouts); |
| 68 | } |
| 69 | |
| 70 | static inline bool workqueue_usable(struct workqueue *q) { |
| 71 | return atomic_load(&q->state) == WORKQUEUE_STATE_ACTIVE; |
| 72 | } |
| 73 | |
| 74 | static inline size_t workqueue_workers(struct workqueue *wq) { |
| 75 | return atomic_load(&wq->num_workers); |
| 76 | } |
| 77 | |
| 78 | static inline size_t workqueue_idlers(struct workqueue *wq) { |
| 79 | return atomic_load(&wq->idle_workers); |
| 80 | } |
| 81 | |
| 82 | bool workqueue_try_spawn_worker(struct workqueue *queue); |
| 83 | bool workqueue_dequeue_task(struct workqueue *queue, struct work **out); |
| 84 | void workqueue_link_thread_and_worker(struct worker *worker, |
| 85 | struct thread *thread); |
| 86 | bool workqueue_spawn_worker_internal(struct workqueue *queue); |
| 87 | struct workqueue *workqueue_least_loaded_queue_except(int64_t except_core_num); |
| 88 | struct workqueue *workqueue_get_least_loaded(void); |
| 89 | struct workqueue *workqueue_get_least_loaded_remote(void); |
| 90 | struct worker *workqueue_spawn_permanent_worker(struct workqueue *queue); |
| 91 | struct workqueue *workqueue_create_full(struct workqueue_attributes *attrs, |
| 92 | const char *fmt, va_list args); |
| 93 | enum thread_request_decision workqueue_request_callback(struct thread *t, |
| 94 | void *data); |
| 95 | struct thread *worker_create(struct cpu_mask mask, nice_t niceness); |
| 96 | |