1#include <sync/event.h>
2#include <thread/thread.h>
3
4struct event_set_ctx {
5 struct event *e;
6 bool prev;
7};
8
9static bool event_try_acquire(void *object) {
10 struct event *e = object;
11
12 if (e->type == EVENT_NOTIFICATION)
13 return atomic_load_rlx(&e->signaled);
14
15 return atomic_xchg_rlx(&e->signaled, false);
16}
17
18static void event_latch_locked(void *arg) {
19 struct event_set_ctx *c = arg;
20 c->prev = atomic_xchg_release(&c->e->signaled, true);
21}
22
23bool event_set_full(struct event *e, enum thread_resume_reason reason,
24 void (*callback)(struct thread *)) {
25 struct event_set_ctx c = {.e = e, .prev = false};
26
27 if (e->type == EVENT_NOTIFICATION) {
28 /* Everyone that is linked now was
29 * snapshotted by whatever set signaled it */
30 if (atomic_load_acq(&e->signaled))
31 return true;
32
33 thread_wait_header_satisfy_all(hdr: &e->header, reason, callback,
34 before_locked: event_latch_locked, arg: &c);
35 return c.prev;
36 }
37
38 thread_wait_header_satisfy_one(hdr: &e->header, reason, cb: callback,
39 empty_locked: event_latch_locked, arg: &c);
40 return c.prev;
41}
42
43bool event_set(struct event *e) {
44 return event_set_full(e, reason: THREAD_WAKE_REASON_BLOCKING_MANUAL, NULL);
45}
46
47bool event_reset(struct event *e) {
48 return atomic_xchg_rlx(&e->signaled, false);
49}
50
51void event_clear(struct event *e) {
52 atomic_store_rlx(&e->signaled, false);
53}
54
55bool event_read_state(struct event *e) {
56 return atomic_load_acq(&e->signaled);
57}
58
59struct thread_wait_object event_wait_object(struct event *e) {
60 return (struct thread_wait_object){
61 .header = &e->header, .object = e, .try_acquire = event_try_acquire};
62}
63
64/* We take the lock here so that setters that have signaled us
65 * are known to have released it, so we can't race an unlock */
66bool event_try_wait(struct event *e) {
67 enum irql irql = spin_lock_high(&e->header.lock);
68
69 bool acquired = event_try_acquire(object: e);
70
71 spin_unlock(&e->header.lock, irql);
72 return acquired;
73}
74
75static struct thread_wait_result event_fast_result(void) {
76 return (struct thread_wait_result){.status = THREAD_WAIT_SATISFIED,
77 .key = 0,
78 .reason =
79 THREAD_WAKE_REASON_BLOCKING_MANUAL};
80}
81
82static void event_prepare(struct event *e, enum thread_wait_type type,
83 enum thread_block_reason reason) {
84 const struct thread_wait_object obj = event_wait_object(e);
85 thread_wait_prepare(objects: &obj, count: 1, NULL, type, reason);
86}
87
88struct thread_wait_result event_wait(struct event *e,
89 enum thread_wait_type type,
90 enum thread_block_reason reason) {
91 if (event_try_wait(e))
92 return event_fast_result();
93
94 event_prepare(e, type, reason);
95 return thread_wait_complete();
96}
97
98struct thread_wait_result event_wait_timeout(struct event *e,
99 enum thread_wait_type type,
100 enum thread_block_reason reason,
101 time_ms_t timeout_ms) {
102 if (event_try_wait(e))
103 return event_fast_result();
104
105 event_prepare(e, type, reason);
106 return thread_wait_complete_timeout(timeout_ms);
107}
108
109void event_init(struct event *e, enum event_type type, bool signaled) {
110 thread_wait_header_init(header: &e->header);
111 e->type = type;
112 atomic_store_rlx(&e->signaled, signaled);
113}
114