/* M2 item 2: scalar-kernel capability demo. N threads, each owning * its own ailang_ctx_t, drive the headline scalar kernel; must be * -fsanitize=thread-clean with every thread's result == the serial * oracle. NO negative control here: a non-allocating scalar kernel * writes no shared mutable state even under a shared ctx, so a * shared-ctx variant cannot race (the de-globalisation negative * control lives in the rc_accounting harness — Task 5 Step 3). */ #include #include #include typedef struct ailang_ctx ailang_ctx_t; extern ailang_ctx_t *ailang_ctx_new(void); extern void ailang_ctx_free(ailang_ctx_t *); extern int64_t backtest_step(ailang_ctx_t *ctx, int64_t state, int64_t sample); #define NTHREADS 8 #define NITERS 200000 static int64_t serial_oracle(void) { /* mirrors examples/embed_backtest_step.ail: state += sample*sample, * sample = (i & 7) over NITERS iterations. */ int64_t s = 0; for (int i = 0; i < NITERS; i++) { int64_t x = i & 7; s = s + x * x; } return s; } static void *worker(void *out) { ailang_ctx_t *ctx = ailang_ctx_new(); int64_t s = 0; for (int i = 0; i < NITERS; i++) s = backtest_step(ctx, s, (i & 7)); ailang_ctx_free(ctx); *(int64_t *)out = s; return NULL; } int main(void) { pthread_t t[NTHREADS]; int64_t res[NTHREADS]; for (int i = 0; i < NTHREADS; i++) pthread_create(&t[i], NULL, worker, &res[i]); for (int i = 0; i < NTHREADS; i++) pthread_join(t[i], NULL); int64_t oracle = serial_oracle(); for (int i = 0; i < NTHREADS; i++) { if (res[i] != oracle) { fprintf(stderr, "thread %d: %lld != %lld\n", i, (long long)res[i], (long long)oracle); return 1; } } printf("swarm: ok (%lld)\n", (long long)oracle); return 0; }