Files
scriptc/packages/runtime/src/scr_worker.test.c
T

143 lines
5.1 KiB
C

#undef NDEBUG
#include "scr_worker.h"
#include <assert.h>
#include <sched.h>
#include <stdatomic.h>
#include <stdio.h>
enum { WORKERS = 4, MESSAGES = 16 };
static atomic_bool start;
static SCR_TL unsigned timer_calls;
static SCR_TL unsigned cleanup_calls;
static void check_cleanup(void) { cleanup_calls++; }
static void timer(ScrClosure *closure) {
(void)closure;
assert(!scr_context_is_main());
timer_calls++;
}
static int entry(uint32_t root_id) {
scr_init();
scr_lib_init(0, NULL);
assert(!scr_context_is_main());
assert(timer_calls == 0 && cleanup_calls == 0);
assert(scr_context_atexit(check_cleanup) == 0);
while (!atomic_load_explicit(&start, memory_order_acquire)) sched_yield();
ScrDyn *input = scr_worker_data();
assert(input && !scr_exc_pending());
assert(scr_dyn_obj_get(input, "root", 4)->v.num == root_id);
assert(scr_dyn_obj_get(input, "self", 4) == input);
ScrDyn *again = scr_worker_data();
assert(input == again);
scr_dyn_release(again);
scr_dyn_release(input);
for (unsigned i = 0; i < MESSAGES; i++) {
ScrDyn *value = scr_dyn_new_num(root_id * 100 + i);
ScrMessage *wire = scr_message_encode(value);
scr_dyn_release(value);
assert(wire && scr_worker_post_parent(wire));
ScrMailEvent *reply = NULL;
while (!(reply = scr_mailbox_take(scr_worker_inbox())))
scr_mailbox_wait(scr_worker_inbox(), 1000);
assert(reply->kind == SCR_WORKER_MESSAGE && reply->source == 0);
value = scr_message_decode(reply->message);
assert(value && value->kind == SCR_DYN_NUM && value->v.num == root_id * 100 + i);
scr_dyn_release(value);
scr_mail_event_free(reply);
}
scr_set_timeout(scr_closure_new((void *)timer, 0), 1);
assert(!scr_loop_run(NULL) && !scr_exc_pending());
assert(timer_calls == 1 && cleanup_calls == 0);
return (int)root_id;
}
#if defined(__linux__)
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
/* Worker builds grow the descriptor table at load, while the process has one
* thread: growing it later, with threads sharing it, waits a full RCU grace
* period on the thread that opens the next worker's mailbox. */
static void check_fd_table_reserved(void) {
struct rlimit limit;
if (getrlimit(RLIMIT_NOFILE, &limit) != 0 || (limit.rlim_cur != RLIM_INFINITY && limit.rlim_cur <= 255)) return;
FILE *status = fopen("/proc/self/status", "r");
assert(status);
char line[256];
long size = -1;
while (fgets(line, sizeof line, status))
if (strncmp(line, "FDSize:", 7) == 0) size = strtol(line + 7, NULL, 10);
fclose(status);
assert(size >= 256);
}
#endif
int main(void) {
#if defined(__linux__)
check_fd_table_reserved();
#endif
scr_init();
scr_lib_init(0, NULL);
ScrMailbox *mailbox = scr_mailbox_new();
assert(mailbox);
ScrWorkerThread *workers[WORKERS];
unsigned messages[WORKERS] = {0};
bool online[WORKERS] = {false};
bool exited[WORKERS] = {false};
for (unsigned i = 0; i < WORKERS; i++) {
ScrDyn *input = scr_dyn_new_obj();
scr_dyn_obj_set(input, "root", 4, scr_dyn_new_num(i));
scr_dyn_obj_set(input, "self", 4, scr_dyn_retain(input));
ScrMessage *wire = scr_message_encode(input);
scr_dyn_release(input);
assert(wire && !scr_exc_pending());
ScrStr *name = scr_str_new("WorkerThread", 12);
workers[i] = scr_worker_thread_start(entry, i, wire, false, mailbox, NULL, 0, name, 0);
scr_str_release(name);
assert(workers[i] && !scr_exc_pending());
assert(scr_worker_thread_id(workers[i]) != 0);
}
/* No sender heap survives into the workerData decode. */
scr_collect_cycles();
atomic_store_explicit(&start, true, memory_order_release);
unsigned exit_count = 0;
while (exit_count != WORKERS) {
ScrMailEvent *event = scr_mailbox_take(mailbox);
if (!event) { scr_mailbox_wait(mailbox, 1000); continue; }
unsigned i = 0;
while (i < WORKERS && scr_worker_thread_id(workers[i]) != event->source) i++;
assert(i < WORKERS && !exited[i]);
if (event->kind == SCR_WORKER_ONLINE) {
assert(!online[i]);
online[i] = true;
} else if (event->kind == SCR_WORKER_MESSAGE) {
assert(online[i] && messages[i] < MESSAGES);
ScrDyn *value = scr_message_decode(event->message);
assert(value && value->kind == SCR_DYN_NUM && value->v.num == i * 100 + messages[i]);
messages[i]++;
ScrMessage *reply = scr_message_encode(value);
scr_dyn_release(value);
assert(reply && scr_worker_thread_post(workers[i], reply));
} else {
assert(event->kind == SCR_WORKER_EXIT && event->code == (int)i);
assert(online[i] && messages[i] == MESSAGES);
exited[i] = true;
exit_count++;
scr_worker_thread_join(workers[i]);
scr_worker_thread_join(workers[i]);
ScrMessage *late = scr_message_encode(scr_dyn_undefined());
assert(late && !scr_worker_thread_post(workers[i], late));
}
scr_mail_event_free(event);
}
for (unsigned i = 0; i < WORKERS; i++) scr_worker_thread_release(workers[i]);
scr_mailbox_release(mailbox);
assert(timer_calls == 0 && cleanup_calls == 0 && scr_context_is_main());
scr_context_cleanup();
puts("worker threads isolate heaps, exchange messages and publish ordered exits");
}