// The ultimate concurrency proof: parent and forked child BOTH dispatch function // pointers WHILE preemptively interleaved. They ping-pong a shared turn word // (proving they time-slice), and on every one of its turns each thread makes an // indirect call through its own volatile function pointer -- the parent inc's an // accumulator, the child dec's one. This only produces the right accumulators // (parent 0->ROUNDS, child 1000->1000-ROUNDS) if the reentrant __jsl_indir reads // each thread's OWN per-thread DP target: with the old shared self-modified // operand the two threads' indirect dispatch aliased and wild-jumped. // // 0x025070 ROUNDS*2 (0x0014) final turn value (ping-pong completed) // 0x025072 5050 parent did ROUNDS fnptr inc's concurrently (acc==ROUNDS) // 0x025074 CCCC child did ROUNDS fnptr dec's concurrently (acc==1000-ROUNDS) #include extern int fork(void *subr); extern int wait(int *status); extern __attribute__((noreturn)) void _exit(int status); #define M(a) (*(volatile uint16_t *)(a)) #define TURN 0x025070UL #define RES_P 0x025072UL #define RES_C 0x025074UL #define ROUNDS 10 static uint16_t incf(uint16_t x) { return x + 1; } static uint16_t decf(uint16_t x) { return x - 1; } static uint16_t (*volatile fpP)(uint16_t) = incf; static uint16_t (*volatile fpC)(uint16_t) = decf; static void child(void) { uint16_t k; uint16_t acc; acc = 1000; for (k = 0; k < ROUNDS; k++) { while ((M(TURN) & 1) == 0) { } acc = fpC(acc); // indirect call, concurrent with parent M(TURN) = M(TURN) + 1; } if (acc == (uint16_t)(1000 - ROUNDS)) { M(RES_C) = 0xCCCC; } _exit(0); } int main(void) { uint16_t k; uint16_t acc; int pid; int st; M(TURN) = 0x0000; M(RES_P) = 0xDEAD; M(RES_C) = 0xDEAD; acc = 0; pid = fork((void *)child); if (pid > 0) { for (k = 0; k < ROUNDS; k++) { while ((M(TURN) & 1) == 1) { } acc = fpP(acc); // indirect call, concurrent with child M(TURN) = M(TURN) + 1; } if (acc == ROUNDS) { M(RES_P) = 0x5050; } wait(&st); } for (volatile unsigned long j = 0; j < 300000UL; j++) { } return 0; }