Files

87 lines
3.1 KiB
C
Raw Permalink Normal View History

/* test_concurrency.c — exercises Section 5.3's core promise: readers
* never observe a torn snapshot while writers race structural changes,
* and Section 5.7's buffer-growth guard doesn't crash or corrupt under
* concurrent grow-and-read. Not a formal proof, but real thread
* interleavings under a sanitizer-friendly build catch real races. */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "packfs.h"
#include "test_harness.h"
#define WRITER_THREADS 4
#define READER_THREADS 4
#define ITERATIONS 500
static Vfs *g_v;
static void *writer_thread(void *arg) {
long id = (long)arg;
char path[64];
for (int i = 0; i < ITERATIONS; i++) {
snprintf(path, sizeof(path), "/t%ld/f%d.txt", id, i % 8);
int err = 0;
VfsFile *f = vfs_open(g_v, path, VFS_O_WRONLY | VFS_O_CREAT | VFS_O_TRUNC, &err);
if (!f) continue;
char payload[128];
int n = snprintf(payload, sizeof(payload), "thread=%ld iter=%d %.*s", id, i, (i % 50), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
vfs_write(f, payload, (pfs_usize)n);
vfs_close(f);
if (i % 37 == 0) vfs_unlink(g_v, path);
}
return NULL;
}
static void *reader_thread(void *arg) {
(void)arg;
char path[64];
for (int i = 0; i < ITERATIONS; i++) {
for (long id = 0; id < WRITER_THREADS; id++) {
snprintf(path, sizeof(path), "/t%ld/f%d.txt", id, i % 8);
VfsStat st;
if (vfs_stat(g_v, path, &st) != VFS_OK) continue;
int err = 0;
VfsFile *f = vfs_open(g_v, path, VFS_O_RDONLY, &err);
if (!f) continue;
char buf[256];
pfs_isize r = vfs_read(f, buf, sizeof(buf) - 1);
/* No CHECK on content here: a concurrent writer may legitimately
* replace the file between stat and open/read (Section 5.3
* guarantees a consistent snapshot per call, not across calls).
* What we're really testing is the absence of a crash, a
* negative-but-uncaught read length, or a hang. */
CHECK(r >= 0);
vfs_close(f);
}
VfsDir dir;
if (vfs_readdir(g_v, "/", &dir) == VFS_OK) vfs_dir_free(&dir);
}
return NULL;
}
int main(void) {
g_v = vfs_new();
Backend *mem = backend_mem_new();
CHECK_EQ_INT(vfs_mount(g_v, "/", mem), VFS_OK);
pthread_t writers[WRITER_THREADS], readers[READER_THREADS];
for (long i = 0; i < WRITER_THREADS; i++) pthread_create(&writers[i], NULL, writer_thread, (void *)i);
for (long i = 0; i < READER_THREADS; i++) pthread_create(&readers[i], NULL, reader_thread, (void *)i);
for (int i = 0; i < WRITER_THREADS; i++) pthread_join(writers[i], NULL);
for (int i = 0; i < READER_THREADS; i++) pthread_join(readers[i], NULL);
/* sanity: the store is still fully consistent after the race */
VfsDir dir;
CHECK_EQ_INT(vfs_readdir(g_v, "/", &dir), VFS_OK);
CHECK(dir.count > 0);
vfs_dir_free(&dir);
vfs_unmount(g_v, "/");
backend_free(mem);
vfs_free(g_v);
TEST_MAIN_END();
}