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

70 lines
1.7 KiB
C

// Stress the fork stash race (fix for the rapid-double-fork window). The parent
// forks FOUR children back-to-back, each a DIFFERENT function that writes its own
// signature to its own slot. Without the fork mutex + consumption handshake, a
// second fork() would overwrite __gnoPendingChildFn before the first child read
// it, so a child would run the WRONG entry -- a slot would be missing or double-
// written. With the handshake, fork() blocks until each child has consumed the
// stash, so every child runs its intended function.
//
// 0x025090 AAAA childA ran (else DEAD -> A got the wrong entry)
// 0x025092 BBBB childB ran
// 0x025094 CCCC childC ran
// 0x025096 DDDD childD ran
// 0x025098 E0DD parent reaped all four
#include <stdint.h>
extern int fork(void *subr);
extern int wait(int *status);
extern __attribute__((noreturn)) void _exit(int status);
#define M(a) (*(volatile uint16_t *)(a))
static void childA(void) {
M(0x025090UL) = 0xAAAA;
_exit(0);
}
static void childB(void) {
M(0x025092UL) = 0xBBBB;
_exit(0);
}
static void childC(void) {
M(0x025094UL) = 0xCCCC;
_exit(0);
}
static void childD(void) {
M(0x025096UL) = 0xDDDD;
_exit(0);
}
int main(void) {
int st;
M(0x025090UL) = 0xDEAD;
M(0x025092UL) = 0xDEAD;
M(0x025094UL) = 0xDEAD;
M(0x025096UL) = 0xDEAD;
M(0x025098UL) = 0xDEAD;
fork((void *)childA); // back-to-back: stresses the stash window
fork((void *)childB);
fork((void *)childC);
fork((void *)childD);
wait(&st);
wait(&st);
wait(&st);
wait(&st);
M(0x025098UL) = 0xE0DD;
for (volatile unsigned long j = 0; j < 300000UL; j++) {
}
return 0;
}