Race Conditions
of 2
Code Examples
The Problem: Who Runs First?
After fork(), both parent and child are placed in the kernel’s run queue. The scheduler decides which one runs first — and this decision is indeterminate. It can change between runs, between machines, under different load conditions. Any program that assumes a specific ordering between parent and child has a race condition.
A race condition occurs when the outcome of a program depends on the relative timing of two or more concurrent operations. After fork(), if parent and child both access shared resources (files, terminals, pipes) without synchronization, their output order becomes unpredictable.
All three outputs above come from the same program. If your program’s correctness depends on any specific ordering, it has a race condition.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int num_children = (argc > 1) ? atoi(argv[1]) : 3;
int i;
setbuf(stdout, NULL); /* disable buffering */
for (i = 0; i < num_children; i++) {
switch (fork()) {
case -1:
perror("fork"); exit(1);
case 0:
/* Child: print its number and exit */
printf("%d ", i);
_exit(EXIT_SUCCESS);
default:
break;
}
}
/* Parent: wait for all children */
for (i = 0; i < num_children; i++)
wait(NULL);
/* Parent also prints */
printf("\n");
return 0;
}
/* Run: ./race 5
Output changes every run!
e.g.: 2 0 1 3 4 (one run)
0 2 3 1 4 (next run)
1 0 4 2 3 (next run) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#define FILE_PATH "/tmp/race_test.txt"
void write_lines(const char *prefix, int count)
{
int fd = open(FILE_PATH, O_WRONLY | O_CREAT | O_APPEND, 0644);
if (fd == -1) { perror("open"); return; }
char buf[64];
for (int i = 0; i < count; i++) {
int len = snprintf(buf, sizeof(buf),
"%s line %d\n", prefix, i+1);
write(fd, buf, len);
/* Without usleep: scheduling decides interleaving.
With usleep: forces more interleaving for demo. */
usleep(100);
}
close(fd);
}
int main(void)
{
/* Clear the file */
int fd = open(FILE_PATH, O_WRONLY|O_CREAT|O_TRUNC, 0644);
close(fd);
pid_t pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
write_lines("CHILD ", 5);
_exit(0);
}
write_lines("PARENT", 5);
wait(NULL);
printf("File contents (order varies each run):\n");
system("cat " FILE_PATH);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
setbuf(stdout, NULL);
pid_t pid = fork();
if (pid == -1) { perror("fork"); exit(1); }
if (pid == 0) {
/* Anti-pattern: using sleep to "ensure" child runs after parent */
sleep(1); /* Does this guarantee parent ran first? NO! */
printf("[Child ] I ran after 1 second\n");
_exit(0);
}
/* Anti-pattern: parent prints without synchronization */
printf("[Parent] I ran immediately\n");
wait(NULL);
return 0;
}
/* This LOOKS ordered, but:
- What if the system is loaded and parent is preempted before printf?
- What if sleep() returns early due to a signal?
- sleep() is NOT a synchronization mechanism
The correct fix: use signals, pipes, semaphores, or mutexes. */
A race condition occurs when the program’s outcome depends on the scheduling order of parent and child processes after fork(). Since the kernel scheduler decides which process runs next (indeterminate), programs that assume a specific ordering between parent and child will produce unpredictable results.
No. sleep() is not a synchronization mechanism. It tells the scheduler to not schedule this process for N seconds, but there is no guarantee about other processes’ execution during that time. Under different load conditions, the sleep may not achieve the desired ordering. Use explicit synchronization: signals, pipes, semaphores, or mutexes.
(1) Signals: Parent sends SIGUSR1 to child when ready; child uses sigsuspend() to wait. (2) Pipes: Parent writes a byte when ready; child blocks on read() until byte arrives. (3) Semaphores: POSIX semaphores or System V semaphores. (4) Shared memory + mutex: Using pthread_mutex in shared memory region.
