65816-llvm-mos/demos/gnoHeapConcur.c
2026-07-07 21:11:28 -05:00

79 lines
2.2 KiB
C

// Stress concurrent malloc/free (fix: the __heapLock semaphore mutex). The
// parent and a forked child both hammer the SHARED heap -- each repeatedly
// malloc()s a block, fills it with a thread-unique pattern, holds it briefly (to
// invite preemption), verifies the pattern survived, and free()s it. If the
// free-list critical section were not serialized, the two threads' interleaved
// malloc/free could hand the same block to both (or corrupt the list), so a
// pattern check would fail or the walk would crash. Both loops completing with
// intact patterns shows the heap is concurrency-safe.
//
// 0x025082 AAAA parent's alloc loop finished with every pattern intact
// 0x025084 CCCC child's alloc loop finished with every pattern intact
// 0x025086 E0DD parent reaped the child
#include <stdint.h>
extern int fork(void *subr);
extern int wait(int *status);
extern __attribute__((noreturn)) void _exit(int status);
extern void *malloc(unsigned long n);
extern void free(void *p);
#define M(a) (*(volatile uint16_t *)(a))
#define ITERS 120
static uint16_t allocLoop(uint16_t tag) {
volatile uint16_t *p;
uint16_t ok;
int i;
int j;
ok = 1;
for (i = 0; i < ITERS; i++) {
p = (volatile uint16_t *)malloc(32);
if (p == 0) {
ok = 0;
break;
}
for (j = 0; j < 16; j++) {
p[j] = (uint16_t)(tag + j);
}
for (volatile int s = 0; s < 40; s++) { // hold the block; invite a task swap
}
for (j = 0; j < 16; j++) {
if (p[j] != (uint16_t)(tag + j)) {
ok = 0;
}
}
free((void *)p);
}
return ok;
}
static void child(void) {
uint16_t ok;
ok = allocLoop(0xC000);
M(0x025084UL) = ok ? 0xCCCC : 0xDEAD;
_exit(0);
}
int main(void) {
uint16_t ok;
int st;
M(0x025082UL) = 0xDEAD;
M(0x025084UL) = 0xDEAD;
M(0x025086UL) = 0xDEAD;
fork((void *)child);
ok = allocLoop(0xA000); // parent hammers the heap concurrently
M(0x025082UL) = ok ? 0xAAAA : 0xDEAD;
wait(&st);
M(0x025086UL) = 0xE0DD;
for (volatile unsigned long j = 0; j < 300000UL; j++) {
}
return 0;
}