1#include <sch/sched.h>
2#include <sync/condvar.h>
3#include <sync/semaphore.h>
4#include <sync/spinlock.h>
5#include <thread/thread_types.h>
6
7LOCK_CHK_CLASS_DECLARE_LOCAL(semaphore_irq);
8LOCK_CHK_CLASS_DECLARE_LOCAL(semaphore_disp);
9
10void semaphore_init(struct semaphore *s, int value, bool irq_disable) {
11 s->count = value;
12 s->irq_disable = irq_disable;
13 if (irq_disable) {
14 spinlock_init_chk(&s->lock, LOCK_CHK_CLASS(semaphore_irq),
15 LOCK_CHKD_FULL);
16 } else {
17 spinlock_init_chk(&s->lock, LOCK_CHK_CLASS(semaphore_disp),
18 LOCK_CHKD_FULL);
19 }
20 condvar_init(cv: &s->cv, irq_disable);
21}
22
23static enum irql semaphore_lock_internal(struct semaphore *sem) {
24 if (sem->irq_disable)
25 return spin_lock_irq_disable(&sem->lock);
26
27 return spin_lock(&sem->lock);
28}
29
30void semaphore_wait(struct semaphore *s) {
31 enum irql irql = semaphore_lock_internal(sem: s);
32
33 while (atomic_load(&s->count) == 0)
34 condvar_wait(cv: &s->cv, lock: &s->lock, irql, out: &irql);
35
36 atomic_fetch_sub(&s->count, 1);
37 spin_unlock(&s->lock, irql);
38}
39
40bool semaphore_timedwait(struct semaphore *s, time_ms_t timeout_ms) {
41 enum irql irql = semaphore_lock_internal(sem: s);
42
43 while (atomic_load(&s->count) == 0) {
44 enum wake_reason wr =
45 condvar_wait_timeout(cv: &s->cv, lock: &s->lock, timeout_ms, irql, out: &irql);
46 if (wr == WAKE_REASON_TIMEOUT && atomic_load(&s->count) == 0) {
47 spin_unlock(&s->lock, irql);
48 return false;
49 }
50 }
51
52 atomic_fetch_sub(&s->count, 1);
53 spin_unlock(&s->lock, irql);
54
55 return true;
56}
57
58void semaphore_post(struct semaphore *s) {
59 enum irql irql = semaphore_lock_internal(sem: s);
60
61 atomic_fetch_add(&s->count, 1);
62
63 condvar_signal(cv: &s->cv);
64
65 spin_unlock(&s->lock, irql);
66}
67
68void semaphore_postn(struct semaphore *s, int n) {
69 enum irql irql = semaphore_lock_internal(sem: s);
70
71 atomic_fetch_add(&s->count, n);
72 for (int i = 0; i < n; i++)
73 condvar_signal(cv: &s->cv);
74
75 spin_unlock(&s->lock, irql);
76}
77