1#include <sch/sched.h>
2#include <sync/completion.h>
3#include <sync/condvar.h>
4#include <sync/spinlock.h>
5#include <thread/thread_types.h>
6
7#define COMPLETION_ALL (UINT32_MAX / 2)
8
9LOCK_CHK_CLASS_DECLARE_LOCAL(completion_irq);
10LOCK_CHK_CLASS_DECLARE_LOCAL(completion_disp);
11
12void completion_init(struct completion *c, bool irq_disable) {
13 c->done = 0;
14 c->irq_disable = irq_disable;
15 if (irq_disable) {
16 spinlock_init_chk(&c->lock, LOCK_CHK_CLASS(completion_irq),
17 LOCK_CHKD_FULL);
18 } else {
19 spinlock_init_chk(&c->lock, LOCK_CHK_CLASS(completion_disp),
20 LOCK_CHKD_FULL);
21 }
22 condvar_init(cv: &c->cv, irq_disable);
23}
24
25static enum irql completion_lock_internal(struct completion *c) {
26 if (c->irq_disable)
27 return spin_lock_irq_disable(&c->lock);
28
29 return spin_lock(&c->lock);
30}
31
32void completion_reinit(struct completion *c) {
33 enum irql irql = completion_lock_internal(c);
34 c->done = 0;
35 spin_unlock(&c->lock, irql);
36}
37
38void complete(struct completion *c) {
39 enum irql irql = completion_lock_internal(c);
40
41 if (c->done < UINT32_MAX)
42 c->done++;
43
44 condvar_signal(cv: &c->cv);
45
46 spin_unlock(&c->lock, irql);
47}
48
49void complete_all(struct completion *c) {
50 enum irql irql = completion_lock_internal(c);
51
52 c->done = COMPLETION_ALL;
53
54 condvar_broadcast(cv: &c->cv);
55
56 spin_unlock(&c->lock, irql);
57}
58
59void completion_wait(struct completion *c) {
60 enum irql irql = completion_lock_internal(c);
61
62 while (c->done == 0)
63 condvar_wait(cv: &c->cv, lock: &c->lock, irql, out: &irql);
64
65 if (c->done != COMPLETION_ALL)
66 c->done--;
67
68 spin_unlock(&c->lock, irql);
69}
70
71bool completion_wait_timeout(struct completion *c, time_ms_t timeout_ms) {
72 enum irql irql = completion_lock_internal(c);
73
74 while (c->done == 0) {
75 enum wake_reason wr =
76 condvar_wait_timeout(cv: &c->cv, lock: &c->lock, timeout_ms, irql, out: &irql);
77 if (wr == WAKE_REASON_TIMEOUT && c->done == 0) {
78 spin_unlock(&c->lock, irql);
79 return false;
80 }
81 }
82
83 if (c->done != COMPLETION_ALL)
84 c->done--;
85
86 spin_unlock(&c->lock, irql);
87 return true;
88}
89
90bool completion_try_wait(struct completion *c) {
91 enum irql irql = completion_lock_internal(c);
92
93 if (c->done == 0) {
94 spin_unlock(&c->lock, irql);
95 return false;
96 }
97
98 if (c->done != COMPLETION_ALL)
99 c->done--;
100
101 spin_unlock(&c->lock, irql);
102 return true;
103}
104
105bool completion_done(struct completion *c) {
106 return atomic_load_explicit(&c->done, memory_order_relaxed) > 0;
107}
108