1/* @title: Test coordination primitives */
2#pragma once
3#include <atomic.h>
4#include <compiler/core.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <sync/condvar.h>
8#include <sync/event_latch.h>
9#include <sync/spinlock.h>
10#include <time/time.h>
11#include <tsa.h>
12
13/* Just a wrapper around event_latch. Someday we could
14 *
15 * TODO: add metadata and tracking around this.
16 *
17 * But we only really need an event_latch for "correctness" */
18struct test_latch {
19 struct event_latch el;
20};
21
22/* This effectively serves as the
23 *
24 * while (atomic_load(&v->phase) != n)
25 * scheduler_yield();
26 *
27 * pattern that tests like, with a poison bool to tell it to end */
28struct test_phase {
29 struct spinlock lock;
30 struct condvar cv;
31 atomic_uint32_t phase;
32 atomic_bool poisoned;
33};
34
35bool test_phase_wait(struct test_phase *p, uint32_t expected,
36 time_ms_t timeout_ms) TSA_MAY_SLEEP;
37
38static inline void test_latch_init(struct test_latch *l) {
39 event_latch_init(el: &l->el, count: 1);
40}
41
42static inline void test_latch_set(struct test_latch *l) {
43 event_latch_count_down(el: &l->el);
44}
45
46static inline bool test_latch_test(const struct test_latch *l) {
47 return event_latch_is_ready(el: &l->el);
48}
49
50static inline bool test_latch_wait_timeout(struct test_latch *l,
51 time_ms_t timeout_ms) TSA_MAY_SLEEP {
52 return event_latch_wait_timeout(el: &l->el, timeout_ms);
53}
54
55static inline bool test_latch_spin_timeout(struct test_latch *l,
56 time_ms_t timeout_ms) {
57 return once_latch_spin_timeout(latch: &l->el.latch, timeout_ms);
58}
59
60static inline void test_phase_init(struct test_phase *p) {
61 spinlock_init(&p->lock);
62 condvar_init(cv: &p->cv);
63 atomic_store_rlx(&p->phase, 0);
64 atomic_store_release(&p->poisoned, false);
65}
66
67static inline uint32_t test_phase_get(const struct test_phase *p) {
68 return atomic_load_acq(&p->phase);
69}
70
71static inline void test_phase_publish(struct test_phase *p, uint32_t phase) {
72 enum irql irql = spin_lock(&p->lock);
73 atomic_store_release(&p->phase, phase);
74 condvar_broadcast(cv: &p->cv);
75 spin_unlock(&p->lock, irql);
76}
77
78static inline void test_phase_set(struct test_phase *p, uint32_t phase) {
79 test_phase_publish(p, phase);
80}
81
82static inline void test_phase_advance(struct test_phase *p) {
83 enum irql irql = spin_lock(&p->lock);
84 atomic_inc_release(&p->phase);
85 condvar_broadcast(cv: &p->cv);
86 spin_unlock(&p->lock, irql);
87}
88
89static inline bool test_phase_is_poisoned(const struct test_phase *p) {
90 return atomic_load_acq(&p->poisoned);
91}
92
93static inline void test_phase_poison(struct test_phase *p) {
94 enum irql irql = spin_lock(&p->lock);
95 atomic_store_release(&p->poisoned, true);
96 condvar_broadcast(cv: &p->cv);
97 spin_unlock(&p->lock, irql);
98}
99