A hands on demonstration of a race condition and its fix, written in C with POSIX threads and semaphores. Individual assignment for the Operating Systems course (SOF201).
Picture a Touch n Go style e-wallet where 5 shared wallets are hit by a thousand user threads at once, each doing a random mix of four operations: add funds, transfer between wallets, make a payment, and earn rewards. If the wallet updates are not protected, threads step on each other and money quietly goes missing.
To prove it, the program keeps its own running totals of everything added, deducted, and rewarded, then checks those totals against the actual final balances. If the two do not match, a race happened.
ewallet_race.cleaves the wallet updates unguarded. Run it and the ledger drifts. In the report run the wallets summed to 812 when the validated figure should have been 878, and one wallet even went negative despite a balance check, which is a classic lost update.ewallet_semaphore.cwraps every wallet update in a critical section using a global lock plus a per wallet semaphore. Now the validated sum matches the real balances on every run.
- Creating and joining a large pool of POSIX threads with
pthread_createandpthread_join - Where a race condition actually comes from, shown with real numbers rather than described in the abstract
- Fixing it with counting semaphores (
sem_init,sem_wait,sem_post,sem_destroy) - A self checking validation sum, so the difference between the broken and the correct version shows up right in the output
gcc ewallet_race.c -o ewallet_race -lpthread
./ewallet_race
gcc ewallet_semaphore.c -o ewallet_semaphore -lpthread
./ewallet_semaphore
Run the race version a few times and watch the validated sum fail to tally. Run the semaphore version and watch it line up every time.
ewallet_race.cthe version with the race conditionewallet_semaphore.cthe version fixed with semaphoresdocs/report.pdfthe full write up