Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
reactor_tests.h File Reference
#include <sched.h>
#include <stdint.h>
#include <serene/rt/engine.h>
#include <serene/rt/fiber.h>
#include <serene/rt/reactor.h>
#include "acutest.h"
#include "base.h"
#include "rt/reactor/backend.h"
#include "rt/reactor/internal.h"
#include "serene/rt/impl/spsc_ring.h"
Include dependency graph for reactor_tests.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define REACTOR_TESTS(X)

Functions

static void reactor_test_submit (srn_reactor_t *r, size_t channel, const srn_reactor_io_request_t *req)
static srn_reactor_io_completion_t reactor_test_await (srn_reactor_t *r, size_t channel)
static void reactor_test_start (srn_reactor_t *r, size_t nworkers)
static void test_reactor_lifecycle ()
static void test_reactor_activate_shutdown ()
static void test_reactor_fresh_state ()
static void test_reactor_nop_roundtrip ()
static void test_reactor_channel_routing ()
static void test_reactor_sleep ()
static void test_reactor_cq_spsc_stress ()

Macro Definition Documentation

◆ REACTOR_TESTS

#define REACTOR_TESTS ( X)
Value:
X("reactor::lifecycle", test_reactor_lifecycle), \
X("reactor::activate_shutdown", test_reactor_activate_shutdown), \
X("reactor::fresh_state", test_reactor_fresh_state), \
X("reactor::nop_roundtrip", test_reactor_nop_roundtrip), \
X("reactor::channel_routing", test_reactor_channel_routing), \
X("reactor::cq_spsc_stress", test_reactor_cq_spsc_stress), \
X("reactor::sleep", test_reactor_sleep)
static void test_reactor_lifecycle()
static void test_reactor_channel_routing()
static void test_reactor_sleep()
static void test_reactor_activate_shutdown()
static void test_reactor_fresh_state()
static void test_reactor_nop_roundtrip()
static void test_reactor_cq_spsc_stress()

Definition at line 38 of file reactor_tests.h.

38#define REACTOR_TESTS(X) \
39 X("reactor::lifecycle", test_reactor_lifecycle), \
40 X("reactor::activate_shutdown", test_reactor_activate_shutdown), \
41 X("reactor::fresh_state", test_reactor_fresh_state), \
42 X("reactor::nop_roundtrip", test_reactor_nop_roundtrip), \
43 X("reactor::channel_routing", test_reactor_channel_routing), \
44 X("reactor::cq_spsc_stress", test_reactor_cq_spsc_stress), \
45 X("reactor::sleep", test_reactor_sleep)

Function Documentation

◆ reactor_test_await()

srn_reactor_io_completion_t reactor_test_await ( srn_reactor_t * r,
size_t channel )
static

Definition at line 60 of file reactor_tests.h.

60 {
62 while (srn_reactor_reap(r, channel, &c, 1) == 0) {
63 // Wait for the reactor thread to post the completion. Yield so valgrind's
64 // serial scheduler runs the reactor thread instead of spinning here (with
65 // the reactor's indefinite wait, nothing else hands it the CPU).
66 sched_yield();
67 }
68 return c;
69}
size_t srn_reactor_reap(srn_reactor_t *reactor, size_t channel, srn_reactor_io_completion_t *out, size_t max)
Drain up to max completions from channel's completion queue into out, returning how many were taken.
Definition reactor.c:220
A completion, the result of one submission, echoed back on the channel.
Definition reactor.h:156
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactor_test_start()

void reactor_test_start ( srn_reactor_t * r,
size_t nworkers )
static

Definition at line 73 of file reactor_tests.h.

73 {
75}
void srn_reactor_activate(srn_reactor_t *reactor, size_t nchannels, srn_reactor_notify_fn notify)
Bring the reactor up, allocate nchannels channels (one per worker) and start the reactor thread.
Definition reactor.c:390
void srn_sched_wake_worker(srn_scheduler_t *sched, size_t channel)
Rouse parked workers so the owner of channel consumes its completions.
Definition scheduler.c:1116
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reactor_test_submit()

void reactor_test_submit ( srn_reactor_t * r,
size_t channel,
const srn_reactor_io_request_t * req )
static

Definition at line 50 of file reactor_tests.h.

50 {
51 while (!srn_spsc_ring_push(r->channels[channel].sq, req)) {
52 // SQ full, the reactor thread will drain it; retry. Yield so valgrind's
53 // serial scheduler can run the reactor thread rather than spin here.
54 sched_yield();
55 }
56 r->backend->wake(r); // the reactor waits indefinitely; nudge it to drain
57}
bool srn_spsc_ring_push(srn_spsc_ring_t *r, const void *elem)
Producer side.
Definition spsc_ring.c:61
void(* wake)(srn_reactor_t *reactor)
Definition backend.h:77
srn_spsc_ring_t * sq
Definition internal.h:32
srn_reactor_io_channel_t * channels
One channel per worker, there is a 1to1 mapping between worker ids and channel ids.
Definition internal.h:56
const srn_reactor_backend_t * backend
The chosen kernel mechanism and its private state, set at activation.
Definition internal.h:47
Here is the call graph for this function:
Here is the caller graph for this function:

◆ test_reactor_activate_shutdown()

void test_reactor_activate_shutdown ( )
static

Definition at line 91 of file reactor_tests.h.

91 {
92 MAKE_ENGINE(mm, engine);
93 reactor_test_start(engine->reactor, 4);
94 TEST_CHECK(engine->reactor->nchannels == 4);
95 SHUTDOWN_ENGINE(mm, engine); // joins the reactor thread
96}
#define TEST_CHECK(cond)
Definition acutest.h:95
#define SHUTDOWN_ENGINE(mm, engine)
Definition base.h:38
#define MAKE_ENGINE(mm, engine)
Definition base.h:32
static void reactor_test_start(srn_reactor_t *r, size_t nworkers)
Here is the call graph for this function:

◆ test_reactor_channel_routing()

void test_reactor_channel_routing ( )
static

Definition at line 137 of file reactor_tests.h.

137 {
138 MAKE_ENGINE(mm, engine);
139 srn_reactor_t *r = engine->reactor;
140 reactor_test_start(r, 2);
141
142 int marker = 0;
143 srn_reactor_io_request_t req = {.op = SRN_REACTOR_IO_NOP, .fiber_data = &marker};
144 reactor_test_submit(r, 1, &req);
145
147 TEST_CHECK(c.fiber_data == &marker);
148 TEST_CHECK(!srn_reactor_channel_has_completions(r, 0)); // channel 0 untouched
149
150 SHUTDOWN_ENGINE(mm, engine);
151}
bool srn_reactor_channel_has_completions(srn_reactor_t *reactor, size_t channel)
Whether channel's completion queue has unconsumed completions.
Definition reactor.c:237
@ SRN_REACTOR_IO_NOP
Completes immediately with a zero result.
Definition reactor.h:64
static srn_reactor_io_completion_t reactor_test_await(srn_reactor_t *r, size_t channel)
static void reactor_test_submit(srn_reactor_t *r, size_t channel, const srn_reactor_io_request_t *req)
void * fiber_data
The fiber_data of the submission this completes.
Definition reactor.h:159
A submission, one request a fiber places on its channel.
Definition reactor.h:113
Here is the call graph for this function:

◆ test_reactor_cq_spsc_stress()

void test_reactor_cq_spsc_stress ( )
static

Definition at line 182 of file reactor_tests.h.

182 {
183 MAKE_ENGINE(mm, engine);
184 srn_reactor_t *r = engine->reactor;
185 reactor_test_start(r, 1);
186
187 enum { N = 100000 };
188 size_t sent = 0, got = 0;
189
190 // Submit and reap are interleaved through the public API: the channel's
191 // admission cap fills, so the producer must consume completions (and drop
192 // them via srn_reactor_op_done) to reopen the window and keep making
193 // progress. The test thread is both sides of channel 0.
194 while (got < N) {
195 bool progress = false;
196
197 while (sent < N) {
199 .op = SRN_REACTOR_IO_NOP, .fiber_data = (void *)(uintptr_t)sent
200 };
201 if (!srn_reactor_submit(r, 0, &req)) {
202 break; // channel at its in-flight cap; go drain the CQ
203 }
204 sent++;
205 progress = true;
206 }
207
209 while (srn_reactor_reap(r, 0, &c, 1) == 1) {
210 if ((uintptr_t)c.fiber_data != got || c.result != 0) {
211 TEST_CHECK(false); // out of order or bad result
212 goto done;
213 }
215 got++;
216 progress = true;
217 }
218
219 if (!progress) {
220 // The reactor thread holds the outstanding ops; yield so valgrind's
221 // serial scheduler can run it rather than spin here.
222 sched_yield();
223 }
224 }
225done:
226 TEST_CHECK(got == N);
228 SHUTDOWN_ENGINE(mm, engine);
229}
void srn_reactor_op_done(srn_reactor_t *reactor, size_t channel)
Drop channel's and the reactor's in-flight counts by one.
Definition reactor.c:162
bool srn_reactor_idle(srn_reactor_t *reactor)
Whether the reactor has no operations in flight.
Definition reactor.c:169
bool srn_reactor_submit(srn_reactor_t *reactor, size_t channel, const srn_reactor_io_request_t *request)
Place a request on channel's submission queue and rouse the reactor.
Definition reactor.c:174
size_t result
The result of the operation on success, bytes transferred for the transfers, the accepted descriptor ...
Definition reactor.h:163
Here is the call graph for this function:

◆ test_reactor_fresh_state()

void test_reactor_fresh_state ( )
static

Definition at line 99 of file reactor_tests.h.

99 {
100 MAKE_ENGINE(mm, engine);
101 srn_reactor_t *r = engine->reactor;
102 reactor_test_start(r, 2);
103
107
109 TEST_CHECK(srn_reactor_reap(r, 0, &c, 1) == 0);
110
111 SHUTDOWN_ENGINE(mm, engine);
112}
Here is the call graph for this function:

◆ test_reactor_lifecycle()

void test_reactor_lifecycle ( )
static

Definition at line 83 of file reactor_tests.h.

83 {
84 MAKE_ENGINE(mm, engine);
85 TEST_ASSERT(engine->reactor != nullptr);
86 SHUTDOWN_ENGINE(mm, engine);
87}
#define TEST_ASSERT(cond)
Definition acutest.h:117

◆ test_reactor_nop_roundtrip()

void test_reactor_nop_roundtrip ( )
static

Definition at line 120 of file reactor_tests.h.

120 {
121 MAKE_ENGINE(mm, engine);
122 srn_reactor_t *r = engine->reactor;
123 reactor_test_start(r, 1);
124
125 int marker = 0;
126 srn_reactor_io_request_t req = {.op = SRN_REACTOR_IO_NOP, .fiber_data = &marker};
127 reactor_test_submit(r, 0, &req);
128
130 TEST_CHECK(c.fiber_data == &marker);
131 TEST_CHECK(c.result == 0);
132
133 SHUTDOWN_ENGINE(mm, engine);
134}
Here is the call graph for this function:

◆ test_reactor_sleep()

void test_reactor_sleep ( )
static

Definition at line 155 of file reactor_tests.h.

155 {
156 MAKE_ENGINE(mm, engine);
157 srn_reactor_t *r = engine->reactor;
158 reactor_test_start(r, 1);
159
160 uint64_t before = srn_now_ns();
161 uint64_t delay = 50ULL * SRN_NS_PER_MS; // 50ms
162
163 int marker = 0;
165 .op = SRN_REACTOR_IO_SLEEP, .deadline_ns = before + delay, .fiber_data = &marker
166 };
167 reactor_test_submit(r, 0, &req);
168
170 TEST_CHECK(c.fiber_data == &marker);
171 TEST_CHECK(c.result == 0);
172
173 TEST_CHECK(srn_now_ns() - before >= delay); // fired no earlier than deadline
174
175 SHUTDOWN_ENGINE(mm, engine);
176}
@ SRN_REACTOR_IO_SLEEP
Wait out a deadline.
Definition reactor.h:66
#define SRN_NS_PER_MS
nanoseconds in one millisecond
Definition utils.h:308
uint64_t srn_now_ns(void)
The current time on the monotonic clock, in nanoseconds.
Definition utils.c:46
Here is the call graph for this function: