/* bugfix-rc-global-stats-race: RED host.
 *
 * Drives the GLOBAL RC-stats fallback path (`g_rc_alloc_count` /
 * `g_rc_free_count`, runtime/rc.c:90-91) under high thread contention.
 *
 * Crucially: NO `ailang_ctx_new`, so `__ail_tls_ctx` stays NULL in every
 * worker and both `ailang_rc_alloc` (rc.c:161) and the to-zero branch of
 * `ailang_rc_dec` (rc.c:212) take the `else g_rc_*count++;` arm. Those
 * two counters are plain `static uint64_t` with a non-atomic `++`, so
 * concurrent increments lose updates (classic read-modify-write race).
 *
 * Each worker does NCYCLES of alloc-immediately-dec on a 16-byte box.
 * No box ever crosses a thread (alloc + dec in the same loop body), so
 * the per-object refcount header op is correct and the program is
 * bit-exact every run. The ONLY observable defect is the under-counted
 * global Σ printed by the atexit handler.
 *
 * The integration test (crates/ail/tests/embed_rc_global_stats_race.rs)
 * parses the atexit `ailang_rc_stats:` line and asserts
 *   allocs == NTHREADS*NCYCLES  AND  frees == NTHREADS*NCYCLES.
 * Pre-fix: reliably fails (allocs/frees short by lost increments).
 * Post-fix (atomic global counters): deterministic exact equality.
 *
 * This host links libailang_rt.a directly; it does not call any kernel.
 */
#include <stddef.h>
#include <stdio.h>
#include <pthread.h>

extern void *ailang_rc_alloc(size_t);
extern void  ailang_rc_dec(void *);

/* High contention: 8 threads * 2_000_000 cycles = 16_000_000 expected
 * global allocs and 16_000_000 global frees. At this contention the
 * non-atomic `++` reliably loses updates on every observed run, so the
 * RED is deterministic (it fails pre-fix, not merely flaky). */
#define NTHREADS 8
#define NCYCLES  2000000

static void *worker(void *_a) {
    (void)_a;
    /* __ail_tls_ctx left NULL on purpose -> global fallback path. */
    for (int i = 0; i < NCYCLES; i++) {
        void *p = ailang_rc_alloc(16);
        ailang_rc_dec(p); /* refcount 1 -> 0, frees, g_rc_free_count++ */
    }
    return NULL;
}

int main(void) {
    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);
    /* All threads joined before main returns -> the atexit reader sees
     * a fully quiesced counter (no reader/writer ordering concern; the
     * defect is purely the lost writer-vs-writer increments). */
    printf("rc_global_stats_race: ran %d threads x %d cycles\n",
           NTHREADS, NCYCLES);
    return 0;
}
