/* race.c Started by Jeff Ondich on 4/11/96 Last modified on 4/8/02 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 kLockKey ((key_t)2357) #define kSharedIntKey ((key_t)1173) /* Prototypes */ char *create_shared_mem( key_t, int ); void interrupt_handler( int ); /* Global variables. */ /* These are global so interrupt_handler can detach them. */ int *gSharedInteger; int *gLock; int nRaces = 0, nNoRaces = 0; int main( void ) { pid_t pid; int temp; /* 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 */ gLock = (int *)create_shared_mem( kLockKey, sizeof(int) ); gSharedInteger = (int *)create_shared_mem( kSharedIntKey, sizeof(int) ); *gLock = 0; /* Fork a child process */ 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() ); while( 1 ) { if( *gLock == 0 ) { *gLock = 1; *gSharedInteger = 3; *gLock = 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() ); while( 1 ) { if( *gLock == 0 ) { *gLock = 1; *gSharedInteger = 2; temp = *gSharedInteger; *gLock = 0; if( temp != 2 ) { printf( "Race\n" ); nRaces++; } else nNoRaces++; } } } return( 0 ); } /*********************************************** * * create_shared_mem * * Create a "size"-byte * block of shared memory with the key. * create_shared_mem() also attaches the shared * memory to the current process' address space, * and returns the attached address. ***********************************************/ char *create_shared_mem( key_t key, int size ) { int id; char *sharedMemAddr; /* Create the shared memory. */ id = shmget( key, size, 0664 | IPC_CREAT | IPC_EXCL ); if( id == -1 ) { perror( "Shmget trouble" ); interrupt_handler( 0 ); } /* Attach the shared memory to the current process' address space. */ sharedMemAddr = (char *)shmat( id, (char *)0, 0 ); if( sharedMemAddr == (char *)-1 ) { perror( "Shmat trouble" ); interrupt_handler( 0 ); } return( sharedMemAddr ); } /*********************************************** * interrupt_handler * * Clean up IPC resources upon termination * by CTRL-C. ***********************************************/ void interrupt_handler( int sig ) { int id; fprintf( stderr, "%d quitting\n", getpid() ); id = shmget( kSharedIntKey, 0, 0 ); shmctl( id, IPC_RMID, NULL ); shmdt( gSharedInteger ); id = shmget( kLockKey, 0, 0 ); shmctl( id, IPC_RMID, NULL ); shmdt( gLock ); fprintf( stderr, "# races: %d, # no-races: %d\n", nRaces, nNoRaces ); exit( 1 ); }