/* race_with_lock.c Started by Jeff Ondich on 4/11/96 Last modified on 16 February 2022 This program demonstrates how race conditions can occur when you use a "lock variable" strategy to try to avoid them. It also demonstrates simple creation and attachment of shared memory in linux. */ #include #include #include #include #include #include #include #define LOCK_KEY ((key_t)2357) #define SHARED_INT_KEY ((key_t)1113) #define REPORT_FREQUENCY 1000000000 // Prototypes char *create_shared_mem(key_t, int); void interrupt_handler(int); void clean_up_shared_memory(); // These are global so interrupt_handler can detach them. int *g_shared_integer; int *g_lock; int main() { // This forces the parent and child to clean up their shared memory segments on Ctrl-C signal( SIGINT, interrupt_handler ); // Get and attach shared memory blocks g_lock = (int *)create_shared_mem(LOCK_KEY, sizeof(int)); if (g_lock == NULL) { clean_up_shared_memory(); fprintf(stderr, "Trouble allocating shared lock variable\n"); exit(1); } *g_lock = 0; g_shared_integer = (int *)create_shared_mem(SHARED_INT_KEY, sizeof(int)); if (g_shared_integer == NULL) { clean_up_shared_memory(); fprintf(stderr, "Trouble allocating shared memory\n"); exit(1); } // Fork a child process int pid = fork(); if (pid == -1) { perror("fork() failure"); exit(1); } if (pid != 0) { // Parent loops forever, putting 3's into the shared // integer using the (flawed) lock variable technique. fprintf(stderr, "Parent [%d] starting\n", getpid()); long counter = 0; while (1) { counter++; if (counter % REPORT_FREQUENCY == 0) { printf("Parent %ld\n", counter); } if (*g_lock == 0) { *g_lock = 1; *g_shared_integer = 3; *g_lock = 0; } } } else { // Child loops forever, putting 2's into the shared // integer using the lock variable technique. // If a race condition occurs, child reports it. fprintf(stderr, "Child [%d] starting\n", getpid()); long counter = 0; while (1) { counter++; if (counter % REPORT_FREQUENCY == 0) { printf("Child %ld\n", counter); } if (*g_lock == 0) { *g_lock = 1; *g_shared_integer = 2; int saved_integer = *g_shared_integer; *g_lock = 0; if (saved_integer != 2) { printf("Whoops %ld\n", counter); fflush(stdout); } } } } return 0; } // Create a block of shared memory with the specified key. // create_shared_mem() also attaches the shared memory to // the current process' address space, and returns the // attached address. Returns NULL if errors occur. char *create_shared_mem(key_t key, int size) { int id; char *shared_memory_address; // Allocate the shared memory id = shmget(key, size, 0664 | IPC_CREAT | IPC_EXCL); if (id == -1) { perror("shmget trouble"); return NULL; } // Attach the shared memory to the address space of the current process shared_memory_address = (char *)shmat( id, (char *)0, 0 ); if (shared_memory_address == (char *)-1) { perror("shmat trouble"); return NULL; } return shared_memory_address; } void clean_up_shared_memory() { int id = shmget(SHARED_INT_KEY, 0, 0); shmctl(id, IPC_RMID, NULL); shmdt(g_shared_integer); id = shmget(LOCK_KEY, 0, 0); shmctl(id, IPC_RMID, NULL); shmdt(g_lock); } void interrupt_handler(int sig) { fprintf(stderr, "%d quitting\n", getpid()); clean_up_shared_memory(); exit( 1 ); }