1#pragma once
2#include <structures/list.h>
3#include <sync/spinlock.h>
4#include <thread/thread_types.h>
5#include <time/time.h>
6
7#define THREAD_WAIT_BLOCK_SYNC 0
8#define THREAD_NUM_WAIT_BLOCKS 4
9#define THREAD_WAIT_KEY_NONE UINT16_MAX
10
11struct thread_wait_header {
12 struct spinlock lock;
13 struct list_head waiters;
14};
15
16#define THREAD_WAIT_HEADER_INIT(name_) \
17 {.lock = SPINLOCK_INIT, .waiters = LIST_HEAD_INIT((name_).waiters)}
18
19enum thread_wait_block_state {
20 THREAD_WAIT_BLOCK_INACTIVE,
21 THREAD_WAIT_BLOCK_ACTIVE,
22 THREAD_WAIT_BLOCK_SATISFIED,
23};
24
25struct thread_wait_block {
26 /* TODO: we're using a doubly linked list here because the block list under
27 * a thread can grow arbitrarily long. Perhaps someday we can consider
28 * something like Windows' MAXIMUM_WAIT_OBJECTS, so we can go for
29 * a singularly linked list, save 8 bytes, at the cost of an
30 * absolute maximum + maybe minor slowdowns (?) */
31 struct list_node obj_node;
32 struct thread *thread;
33 void *object;
34 uint16_t key;
35 enum thread_wait_block_state state;
36 struct thread_wait_header *header;
37};
38ct_assert_struct_size_eq(thread_wait_block, 48);
39
40struct thread_wait_object {
41 struct thread_wait_header *header;
42 void *object;
43
44 /* Optional function. This runs under header->lock right before
45 * we link the wait block, and if it returns true,
46 * the wait is satisfied at registration time and any
47 * remaining objects never get linked */
48 bool (*try_acquire)(void *object);
49};
50
51struct thread_wait_result {
52 enum thread_wait_status status;
53 uint16_t key;
54 enum thread_resume_reason reason;
55};
56
57void thread_wait_header_init(struct thread_wait_header *header);
58void thread_wait_init(struct thread *thread);
59
60/* If storage == NULL, we use the thread's embedded blocks. No allocation
61 * ever happens in here, however, the caller remains runnable */
62void thread_wait_prepare(const struct thread_wait_object *objects, size_t count,
63 struct thread_wait_block *storage,
64 enum thread_wait_type type,
65 enum thread_block_reason reason);
66
67void thread_wait_prepare_one(struct thread_wait_header *header, void *object,
68 enum thread_wait_type type,
69 enum thread_block_reason reason);
70
71void thread_wait_prepare_to_sleep(struct thread_wait_header *header,
72 void *object, enum thread_wait_type type);
73
74/* Satisfy unlinks ALL blocks before the waiter becomes runnable.
75 * The callback runs just for the winner */
76bool thread_wait_satisfy(struct thread_wait_block *block,
77 enum thread_resume_reason reason,
78 void (*callback)(struct thread *));
79
80/* For callbacks that took a snapshot of thread->wait_epoch */
81bool thread_wait_satisfy_epoch(struct thread_wait_block *block, uint64_t epoch,
82 enum thread_resume_reason reason,
83 void (*cb)(struct thread *));
84
85struct thread *thread_wait_header_satisfy(struct thread_wait_header *hdr,
86 enum thread_resume_reason reason,
87 void (*cb)(struct thread *));
88
89/* Like thread_wait_header_satisfy, but if nothing remains
90 * to satisfy, empty_locked runs under hdr->lock */
91struct thread *thread_wait_header_satisfy_one(struct thread_wait_header *hdr,
92 enum thread_resume_reason reason,
93 void (*cb)(struct thread *),
94 void (*empty_locked)(void *),
95 void *arg);
96
97/* Satisfies the waiters linked when before_locked runs, under hdr->lock */
98size_t thread_wait_header_satisfy_all(struct thread_wait_header *hdr,
99 enum thread_resume_reason reason,
100 void (*callback)(struct thread *),
101 void (*before_locked)(void *), void *arg);
102
103/* Alert is a "level-triggered" singular event. We only end interruptible
104 * waits with alerts, but otherwise, the alert will merely pend until
105 * the next interruptible wait/park happens */
106void thread_alert(struct thread *cc_nonnull_ptr thread);
107void thread_park(void);
108
109/* The actual "go wait" action, yield and whatnot */
110struct thread_wait_result thread_wait_complete(void);
111
112struct thread_wait_result thread_wait_complete_timeout(time_ms_t timeout_ms);
113void thread_wait_cancel(void);
114