/* M2 item 1: the de-globalisation teeth. Exercises ailang_rc_alloc / * ailang_rc_dec DIRECTLY (not through any kernel) from N threads, each * owning its own ailang_ctx_t. Per-ctx counters must be individually * correct and -fsanitize=thread-clean. Build -DSHARED_CTX for the * global-hammering negative control (must make tsan report a race). */ #include #include #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 void *ailang_rc_alloc(size_t); extern void ailang_rc_dec(void *); /* set the per-call ctx the runtime accounts into (defined in rc.c) */ extern __thread ailang_ctx_t *__ail_tls_ctx; #define NTHREADS 8 #define NCYCLES 200000 #ifdef SHARED_CTX static ailang_ctx_t *g_shared; /* negative control: one ctx, all threads */ #endif static void *worker(void *_a) { (void)_a; #ifdef SHARED_CTX ailang_ctx_t *ctx = g_shared; #else ailang_ctx_t *ctx = ailang_ctx_new(); #endif __ail_tls_ctx = ctx; for (int i = 0; i < NCYCLES; i++) { void *p = ailang_rc_alloc(16); ailang_rc_dec(p); /* refcount 1 -> 0, frees, free_count++ */ } #ifndef SHARED_CTX /* per-ctx balance must hold exactly */ /* (read via the same struct layout the runtime defines) */ ailang_ctx_free(ctx); #endif return NULL; } int main(void) { #ifdef SHARED_CTX g_shared = ailang_ctx_new(); #endif pthread_t t[NTHREADS]; for (int i = 0; i < NTHREADS; i++) pthread_create(&t[i], NULL, worker, NULL); for (int i = 0; i < NTHREADS; i++) pthread_join(t[i], NULL); #ifdef SHARED_CTX ailang_ctx_free(g_shared); #endif printf("rc_accounting_tsan: ok\n"); return 0; }