| 1 | #include <sch/sched.h> |
| 2 | #include <sync/condvar.h> |
| 3 | #include <thread/thread.h> |
| 4 | #include <thread/workqueue.h> |
| 5 | |
| 6 | static void condvar_prepare_wait(struct condvar *cv) { |
| 7 | thread_wait_prepare_one(header: &cv->waiters, object: cv, type: THREAD_WAIT_UNINTERRUPTIBLE, |
| 8 | reason: THREAD_BLOCK_REASON_MANUAL); |
| 9 | } |
| 10 | |
| 11 | static enum wake_reason |
| 12 | condvar_finish_wait(struct spinlock *lock, enum irql irql, enum irql *out, |
| 13 | bool timed, time_ms_t timeout_ms) TSA_NO_ANALYSIS { |
| 14 | enum irql sirql = irql_get(); |
| 15 | |
| 16 | spin_unlock(lock, irql); |
| 17 | struct thread_wait_result result = |
| 18 | timed ? thread_wait_complete_timeout(timeout_ms) |
| 19 | : thread_wait_complete(); |
| 20 | enum wake_reason reason = result.reason == THREAD_WAKE_REASON_SLEEP_TIMEOUT |
| 21 | ? WAKE_REASON_TIMEOUT |
| 22 | : WAKE_REASON_SIGNAL; |
| 23 | |
| 24 | *out = sirql == IRQL_HIGH_LEVEL ? spin_lock_high(lock) : spin_lock(lock); |
| 25 | return reason; |
| 26 | } |
| 27 | |
| 28 | enum wake_reason condvar_wait(struct condvar *cv, struct spinlock *lock, |
| 29 | enum irql irql, enum irql *out) TSA_NO_ANALYSIS { |
| 30 | condvar_prepare_wait(cv); |
| 31 | return condvar_finish_wait(lock, irql, out, false, timeout_ms: 0); |
| 32 | } |
| 33 | |
| 34 | void condvar_init(struct condvar *cv) { |
| 35 | thread_wait_header_init(header: &cv->waiters); |
| 36 | } |
| 37 | |
| 38 | static void nop_callback(struct thread *unused) { |
| 39 | cc_ignore(unused); |
| 40 | } |
| 41 | |
| 42 | struct thread *condvar_signal_callback(struct condvar *cv, |
| 43 | thread_action_callback tac) { |
| 44 | struct thread *t = thread_wait_header_satisfy( |
| 45 | hdr: &cv->waiters, reason: THREAD_WAKE_REASON_SLEEP_MANUAL, cb: tac); |
| 46 | |
| 47 | if (!t) |
| 48 | tac(NULL); |
| 49 | return t; |
| 50 | } |
| 51 | |
| 52 | struct thread *condvar_signal(struct condvar *cv) { |
| 53 | return condvar_signal_callback(cv, tac: nop_callback); |
| 54 | } |
| 55 | |
| 56 | void condvar_broadcast_callback(struct condvar *cv, |
| 57 | thread_action_callback tac) { |
| 58 | thread_wait_header_satisfy_all( |
| 59 | hdr: &cv->waiters, reason: THREAD_WAKE_REASON_SLEEP_MANUAL, callback: tac, NULL, NULL); |
| 60 | } |
| 61 | |
| 62 | void condvar_broadcast(struct condvar *cv) { |
| 63 | condvar_broadcast_callback(cv, tac: nop_callback); |
| 64 | } |
| 65 | |
| 66 | enum wake_reason condvar_wait_timeout(struct condvar *cv, struct spinlock *lock, |
| 67 | time_ms_t timeout_ms, enum irql irql, |
| 68 | enum irql *out) TSA_NO_ANALYSIS { |
| 69 | condvar_prepare_wait(cv); |
| 70 | return condvar_finish_wait(lock, irql, out, true, timeout_ms); |
| 71 | } |
| 72 | |