/* example of race condition between processing updating shared files The program terminates via exit (), a killing signal from the user, or a timeout. It uses the read() and write() system calls, which are guaranteed to have atomic effects at the level of a single physical disk block of data. See program shared_memory for a similar example, using threads and shared memory. */ #define _XOPEN_SOURCE 500 #define _REENTRANT #include #include #include #include #include #include #include #include #include #include #include #include #define TOTAL 1000000000 long A = 500000000; long B = 500000000; pid_t other_process = 0; int fildes_A = 0; int fildes_B = 0; long count_1 = 0; long count_2 = 0; void handler (int sig) { fprintf (stderr, "exiting due to signal %d\n", sig); fprintf (stderr, "count_1 = %ld10 count_2 = %ld10\n", count_1, count_2); exit (-1); } void killfile (char * filename) { if (unlink (filename) == -1) { perror ("lseek"); } } void shutdown () { if (other_process) kill (other_process, SIGKILL); fprintf (stderr, "count_1 = %ld10 count_2 = %ld10\n", count_1, count_2); killfile ("A"); killfile ("B"); exit (-1); } void fail (char * msg) { perror (msg); shutdown (); } void initfile (char * filename, long *value) { int fildes; if ( (fildes = open (filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1) { fail ("create"); } if (lseek (fildes, 0, SEEK_SET) == -1) { fail ("lseek"); } write (fildes, (char *) value, sizeof (value)); close (fildes); } int openfile (char * filename) { int fildes; if ((fildes = open (filename, O_RDWR) == -1)) { fail ("open"); } return fildes; } void position_to_front (int fildes) { if (lseek (fildes, 0, SEEK_SET) == -1) { fail ("lseek"); } } void read_from_file (int fildes, long *value) { int nread; position_to_front (fildes); if (( nread = read (fildes, value, sizeof (value)) != sizeof (value))) { fprintf (stderr, "nread = %d\n", nread); fail ("read"); } } void write_to_file (int fildes, long *value) { int nwritten; position_to_front (fildes); if ((nwritten = write (fildes, value, sizeof (value)) != sizeof (value))) { fprintf (stderr, "nwritten = %d\n", nwritten); fail ("write"); } } void transfer_files (long amount, int from_account, int to_account) { long from_value, to_value; read_from_file (from_account, &from_value); read_from_file (to_account, &to_value); to_value = to_value + amount; from_value = from_value - amount; write_to_file (from_account, &from_value); write_to_file (to_account, &to_value); } void check_consistency_files () { long total, A_value, B_value; read_from_file (fildes_A, &A_value); read_from_file (fildes_B, &B_value); total = A_value + B_value; if (total != TOTAL) { fprintf (stderr, "*** LOSS OF CONSISTENCY ***\n"); fprintf (stderr, "main: A = %ld10 B = %ld10 TOTAL-(A+B) = %ld10\n", A_value, B_value, TOTAL - total); shutdown (); } } int main (int argc, char **argv) { struct sigaction act; /* install handler for alarm signal */ sigaction (SIGALRM, NULL, &act); act.sa_handler = handler; sigaction (SIGALRM, &act, NULL); alarm (10); /* will kill entire process in 10 seconds */ initfile ("A", &A); initfile ("B", &B); if ( (other_process = fork ())) { /* we are the parent */ fildes_A = openfile ("A"); fildes_B = openfile ("B"); while (1) { count_1++; transfer_files (10, fildes_A, fildes_B); check_consistency_files (); } } else { /* we are the child */ other_process = getppid (); openfile ("A"); openfile ("B"); while (1) { count_2++; transfer_files (10, fildes_B, fildes_A); check_consistency_files (); } } exit (0); }