// 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;
}
