/* * 989_pkgcache_concurrent_run — concurrent shared-cache build-correctness * smoke (#104). N distinct root programs that all `import shared` are built * CONCURRENTLY into ONE shared WW_PKGCACHE; every resulting binary must be * byte-IDENTICAL to a reference built in ISOLATION (private cache) and run to * its expected exit. This guards that concurrent `ww build --sep` sharing one * out/.pkgcache produces correct, deterministic binaries — a regression guard * for the cache subsystem under contention (store crash, lock bug, wrong-key * copy, etc.). * * It does NOT prove the temp+rename store is atomic against torn reads: with * content-keying every concurrent cold build MISSES at lookup and STORES (it * never HIT-reads a mid-store entry), so the torn-read window is not forced * here. That race is closed by construction at the cache_store fix site; the * deferred white-box guard is TASK #105. * * Both driver stages (rule 10): the cs and ww references are byte-identical. * Light wwstage-driver test (CLAUDE.md rule 14): all outputs + caches live * under a private /tmp tree, so it is parallel-safe and off every byte-id / * bootstrap gate. Models 989_pkgcache_run conventions. */ #include #include #include #include #include #include #define NPROG 4 #define BASE_EXIT 7 /* shared.v() == 7; rootI returns 7 + I */ static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return 1; } static const char * absbin(void) { const char *b = getenv("BIN"); if (!b) b = "out/bin"; if (b[0] == '/') return b; static char buf[2048]; char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return NULL; snprintf(buf, sizeof buf, "%s/%s", cwd, b); return buf; } static int write_file(const char *path, const char *body) { FILE *f = fopen(path, "wb"); if (!f) return -1; fputs(body, f); fclose(f); return 0; } static int slurp(const char *path, char **outbuf, size_t *outlen) { FILE *f = fopen(path, "rb"); if (!f) return -1; fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); if (n < 0) { fclose(f); return -1; } char *b = malloc((size_t)n + 1); if (!b) { fclose(f); return -1; } if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; } b[n] = '\0'; fclose(f); *outbuf = b; *outlen = (size_t)n; return 0; } static int files_eq(const char *a, const char *b) { char *ba = NULL, *bb = NULL; size_t na = 0, nb = 0; if (slurp(a, &ba, &na) < 0 || slurp(b, &bb, &nb) < 0) { free(ba); free(bb); return -1; } int eq = (na == nb && memcmp(ba, bb, na) == 0); free(ba); free(bb); return eq ? 0 : 1; } static const char *shared_src = "package shared;\n" "export fn v() i32 = { return 7; };\n"; int main(void) { const char *bin = absbin(); if (!bin) return 1; int fail = 0; char td[64], cmd[8192], batch[32768]; char sharedww[1024]; snprintf(td, sizeof td, "/tmp/wwconc_%d", getpid()); snprintf(cmd, sizeof cmd, "rm -rf %s", td); runwait(cmd); snprintf(cmd, sizeof cmd, "mkdir -p %s/shared", td); runwait(cmd); snprintf(sharedww, sizeof sharedww, "%s/shared/shared.ww", td); if (write_file(sharedww, shared_src)) { fail++; goto out; } /* N distinct roots, each importing the one shared pkg. Distinct return * (7 + I) so each program — and so each clean binary — is distinguishable, * while the contended cache entry (shared) is common to all. */ for (int i = 0; i < NPROG; i++) { char rootww[1024], src[256]; snprintf(rootww, sizeof rootww, "%s/root%d.ww", td, i); snprintf(src, sizeof src, "package main;\nimport shared;\n" "fn main() i32 = { return shared.v() + %d; };\n", i); if (write_file(rootww, src)) { fail++; goto out; } } struct { const char *drv, *tag; } stg[] = { { "ww", "cs" }, { "ww_ww", "ww" }, }; for (int s = 0; s < 2; s++) { const char *drv = stg[s].drv, *tag = stg[s].tag; char shcache[1024]; snprintf(shcache, sizeof shcache, "%s/cache.%s", td, tag); /* References: each rootI built ISOLATED (private cache, no contention) * = the clean baseline bytes a concurrent build must reproduce. */ for (int i = 0; i < NPROG; i++) { char refcache[1024], refprog[1024], refscr[1024], rootww[1024]; snprintf(refcache, sizeof refcache, "%s/refc.%s.%d", td, tag, i); snprintf(refprog, sizeof refprog, "%s/ref.%s.%d", td, tag, i); snprintf(refscr, sizeof refscr, "%s/ref.%s.%d.sepwork", td, tag, i); snprintf(rootww, sizeof rootww, "%s/root%d.ww", td, i); snprintf(cmd, sizeof cmd, "rm -rf %s %s; WW_PKGCACHE='%s' timeout 240 %s/%s build --sep " "-o %s %s >/dev/null 2>&1", refscr, refcache, refcache, bin, drv, refprog, rootww); if (runwait(cmd) != 0) { fprintf(stderr, "concur FAIL[%s]: reference build %d failed\n", drv, i); fail++; } if (runwait(refprog) != BASE_EXIT + i) { fprintf(stderr, "concur FAIL[%s]: reference %d wrong exit\n", drv, i); fail++; } } /* Fresh shared cache so every build in the batch MISS-stores `shared` * concurrently → write contention on shared/P.{wwi,o,key}. Distinct * per-prog output so every binary can be verified, not just one. */ snprintf(cmd, sizeof cmd, "rm -rf %s", shcache); runwait(cmd); size_t off = 0; off += (size_t)snprintf(batch + off, sizeof batch - off, "export WW_PKGCACHE='%s'; ", shcache); for (int i = 0; i < NPROG; i++) { off += (size_t)snprintf(batch + off, sizeof batch - off, "( rm -rf %s/c.%s.%d.sepwork; timeout 240 %s/%s build " "--sep -o %s/c.%s.%d %s/root%d.ww >/dev/null 2>&1 ) & ", td, tag, i, bin, drv, td, tag, i, td, i); } off += (size_t)snprintf(batch + off, sizeof batch - off, "wait"); if (off >= sizeof batch) { fprintf(stderr, "concur FAIL: batch cmd truncated\n"); fail++; goto out; } runwait(batch); for (int i = 0; i < NPROG; i++) { char prog[1024], refprog[1024]; snprintf(prog, sizeof prog, "%s/c.%s.%d", td, tag, i); snprintf(refprog, sizeof refprog, "%s/ref.%s.%d", td, tag, i); if (runwait(prog) != BASE_EXIT + i) { fprintf(stderr, "concur FAIL[%s]: concurrent prog i=%d " "wrong/failed exit\n", drv, i); fail++; } if (files_eq(prog, refprog) != 0) { fprintf(stderr, "concur FAIL[%s]: concurrent binary i=%d " "!= isolated reference\n", drv, i); fail++; } } } /* rule 10: the cs and ww isolated references are byte-identical. */ for (int i = 0; i < NPROG; i++) { char a[1024], b[1024]; snprintf(a, sizeof a, "%s/ref.cs.%d", td, i); snprintf(b, sizeof b, "%s/ref.ww.%d", td, i); if (files_eq(a, b) != 0) { fprintf(stderr, "concur FAIL: cs != ww reference %d (rule 10)\n", i); fail++; } } out: snprintf(cmd, sizeof cmd, "rm -rf %s", td); runwait(cmd); if (fail) { fprintf(stderr, "concur: %d check(s) failed\n", fail); return 1; } printf("concur: %d concurrent --sep builds sharing one cache, both stages " "— every binary byte-identical to its isolated reference + correct run; " "cs==ww references (rule 10)\n", NPROG); return 0; }