114 lines
3.6 KiB
C
114 lines
3.6 KiB
C
// testEngineMyBasic.c -- my-basic on context threads under the actor model. Verifies a
|
|
// host native is serviced on the host thread, and that several my-basic contexts run
|
|
// concurrently without corruption -- my-basic keeps process-global state, so the engine
|
|
// serializes my-basic work with a lock; this is the test that would race without it.
|
|
// Built under ASan+UBSan (make test) and ThreadSanitizer (make tsanmb).
|
|
|
|
#define _POSIX_C_SOURCE 200809L
|
|
|
|
#include "calog.h"
|
|
|
|
#include <stdatomic.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#define CHECK(cond, msg) checkImpl((cond), (msg), __FILE__, __LINE__)
|
|
|
|
#define PUMP_LIMIT 4000
|
|
#define CONTEXT_COUNT 3
|
|
|
|
static CalogT *calog = NULL;
|
|
static _Atomic int32_t bumpCount = 0;
|
|
static _Atomic uint64_t bumpCtxId = 0xFFFFu;
|
|
static int32_t testsRun = 0;
|
|
static int32_t testsFailed = 0;
|
|
|
|
static int32_t bump(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData);
|
|
static void checkImpl(bool condition, const char *message, const char *file, int32_t line);
|
|
static void testConcurrentContexts(void);
|
|
static void testHostNative(void);
|
|
|
|
|
|
static int32_t bump(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) {
|
|
(void)args;
|
|
(void)argCount;
|
|
(void)userData;
|
|
atomic_store(&bumpCtxId, calogCurrentId());
|
|
atomic_fetch_add(&bumpCount, 1);
|
|
calogValueNil(result);
|
|
return calogOkE;
|
|
}
|
|
|
|
|
|
static void checkImpl(bool condition, const char *message, const char *file, int32_t line) {
|
|
testsRun++;
|
|
if (!condition) {
|
|
testsFailed++;
|
|
printf("FAIL %s:%d %s\n", file, line, message);
|
|
}
|
|
}
|
|
|
|
|
|
static void testConcurrentContexts(void) {
|
|
CalogContextT *ctxs[CONTEXT_COUNT];
|
|
struct timespec ts = { 0, 500000 };
|
|
int32_t i;
|
|
|
|
// The scripts allocate (list creation) as they run so the parallel execution paths
|
|
// churn my-basic's now-atomic allocation counter concurrently -- the case that
|
|
// raced before the vendored patch (see make tsanmb).
|
|
atomic_store(&bumpCount, 0);
|
|
for (i = 0; i < CONTEXT_COUNT; i++) {
|
|
ctxs[i] = calogContextOpen(calog, &calogMyBasicEngine);
|
|
calogContextEval(ctxs[i], "a = list(1, 2, 3)\nb = list(4, 5, 6)\nbump()");
|
|
}
|
|
for (i = 0; i < PUMP_LIMIT && atomic_load(&bumpCount) < CONTEXT_COUNT; i++) {
|
|
calogPump(calog);
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
CHECK(atomic_load(&bumpCount) == CONTEXT_COUNT, "several concurrent my-basic contexts all reached the host native");
|
|
for (i = 0; i < CONTEXT_COUNT; i++) {
|
|
calogContextClose(ctxs[i]);
|
|
}
|
|
}
|
|
|
|
|
|
static void testHostNative(void) {
|
|
CalogContextT *ctx;
|
|
struct timespec ts = { 0, 500000 };
|
|
int32_t i;
|
|
|
|
ctx = calogContextOpen(calog, &calogMyBasicEngine);
|
|
CHECK(ctx != NULL, "opened a my-basic context");
|
|
atomic_store(&bumpCount, 0);
|
|
calogContextEval(ctx, "bump()");
|
|
for (i = 0; i < PUMP_LIMIT && atomic_load(&bumpCount) < 1; i++) {
|
|
calogPump(calog);
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
CHECK(atomic_load(&bumpCount) == 1, "the my-basic script's native call ran");
|
|
CHECK(atomic_load(&bumpCtxId) == 0, "the native ran on the host thread (id 0)");
|
|
calogContextClose(ctx);
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
calog = calogCreate();
|
|
if (calog == NULL) {
|
|
printf("calog create failed\n");
|
|
return 1;
|
|
}
|
|
calogRegister(calog, "bump", bump, NULL);
|
|
|
|
testHostNative();
|
|
testConcurrentContexts();
|
|
|
|
calogDestroy(calog);
|
|
|
|
printf("\n%d checks, %d failed\n", testsRun, testsFailed);
|
|
fflush(stdout);
|
|
if (testsFailed != 0) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|