//------------------------------------------------------------------- // racecure.cpp // // This demo revises our 'racedemo.cpp' program; it eliminates // the 'race condition' by using signals to synchronize access // to the stdout stream (in the manner suggested by W. Richard // Stevens in "Advanced Programming in the UNIX Environment"). // The 'sigsuspend()' function is used here to enable tasks to // avoid 'busy-waiting' in an especially efficient fashion (by // not being sheduled for CPU time until conditions exist that // allow them to make forward progress). // // programmer: ALLAN CRUSE // written on: 10 OCT 2004 //------------------------------------------------------------------- #include // for perror(), setbuf(), putc() #include // for fork() #include // for exit() #include // for signal(), sigsuspend(), kill(), etc #define DELAY_COUNT 20000000 sigset_t newmask, oldmask, zeromask; // signal-sets volatile sig_atomic_t sigflag = 0; // modified by signal-handler void upon_signal( int signum ) { sigflag = 1; } void INITIALIZE_TELL_WAIT( void ) { sigemptyset( &zeromask ); sigemptyset( &newmask ); sigaddset( &newmask, SIGUSR1 ); sigaddset( &newmask, SIGUSR2 ); sigprocmask( SIG_BLOCK, &newmask, &oldmask ); signal( SIGUSR1, upon_signal ); signal( SIGUSR2, upon_signal ); } void WAIT_PARENT( void ) { while ( sigflag == 0 ) sigsuspend( &zeromask ); sigflag = 0; sigprocmask( SIG_SETMASK, &oldmask, NULL ); } void TELL_PARENT( int pid ) { kill( pid, SIGUSR2 ); // tells parent we're done } void WAIT_CHILD( void ) { while ( sigflag == 0 ) sigsuspend( &zeromask ); sigflag = 0; sigprocmask( SIG_SETMASK, &oldmask, NULL ); } void TELL_CHILD( int pid ) { kill( pid, SIGUSR1 ); // tells child we're done } //--------------------------------------------------------------------- // Below is the original 'racedemo' code -- with synchronization added //--------------------------------------------------------------------- void one_character_at_a_time( char *msg ) { setbuf( stdout, NULL ); // set to 'unbuffered' while ( int c = *msg++ ) { putc( c, stdout ); int i = DELAY_COUNT; while( --i ); } } int main( int argc, char **argv ) { INITIALIZE_TELL_WAIT(); int pid = fork(); if ( pid < 0 ) { perror( "fork" ); exit(1); } else if ( pid == 0 ) { WAIT_PARENT(); // lets parent go first one_character_at_a_time( "output from child process \n" ); TELL_PARENT( getppid() ); // ok parent can exit } else { one_character_at_a_time( "output from parent process \n" ); TELL_CHILD( pid ); // tells child to go now WAIT_CHILD(); // but lets child finish } exit(0); }