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