/* * Pthread rwlock * ============== * * This program is a simple test case for the implementation * of the Readers-Writer lock in the Pthread library provided by DiVinE. * * *tags*: test, C99 * * Description * ----------- * * Reading and writing is interleaved in all (visible) possibilities * (currently also those unfair) and at some points safety is verified. * * Read lock ensures for reader that reading consisting of possibly * more than one load will not be interleaved with writing. But when compiled * with the macro `BUG` defined, read lock is disabled and hence safety is violated * (even thought writer still uses write lock). * * Parameters * ---------- * * - `BUG`: if defined than the algorithm is incorrect and violates the safety property * - `NUM_OF_READERS`: the number of readers * - `NUM_OF_WRITERS`: the number of writers * - `INITIAL`, `EOF`: the stream of exchanged data = `INITIAL, INITIAL + 1, ... , EOF - 1, EOF` * * Verification * ------------ * * - all available properties with the default values of parameters: * * $ divine compile --llvm pthread_rwlock.c * $ divine verify -p assert pthread_rwlock.bc -d * $ divine verify -p safety pthread_rwlock.bc -d * * - customizing the number of threads: * * $ divine compile --llvm --cflags="-DNUM_OF_READERS=3 -DNUM_OF_WRITERS=4" pthread_rwlock.c * $ divine verify -p assert pthread_rwlock.bc -d * $ divine verify -p safety pthread_rwlock.bc -d * * - configuring the stream of exchanged data between writers and readers: * * $ divine compile --llvm --cflags="-DINITIAL=0 -DEOF=20" pthread_rwlock.c * $ divine verify -p assert pthread_rwlock.bc -d * $ divine verify -p safety pthread_rwlock.bc -d * * Execution * --------- * * $ clang -lpthread -o pthread_rwlock.exe pthread_rwlock.cpp * $ ./pthread_rwlock.exe */ #include #include #include #ifndef NUM_OF_READERS #define NUM_OF_READERS 2 #endif #ifndef NUM_OF_WRITERS #define NUM_OF_WRITERS 2 #endif // stream of exchanged data = INITIAL, INITIAL + 1, ... , EOF - 1, EOF #ifndef INITIAL #define INITIAL 1 #endif #ifndef EOF #define EOF 3 #endif #define ERRNO( fun ) assert( !fun ); #ifndef BUG #define VERIFY_RWLOCK #endif volatile int global = INITIAL; volatile int writers = 0; volatile int readers = 0; volatile int ready_readers = 0; pthread_mutex_t mutex; // mutex for readers pthread_rwlock_t rwlock; #ifdef VERIFY_RWLOCK void check() { // ( writers != 0 ) => ( writers = 1 and readers = 0 ) assert( !writers || ( writers == 1 && !readers ) ); } #endif void global_write() { #ifdef VERIFY_RWLOCK ++writers; check(); ++global; --writers; #else ++global; #endif } int global_read() { #ifdef VERIFY_RWLOCK pthread_mutex_lock( &mutex ); ++readers; pthread_mutex_unlock( &mutex ); check(); int local = global; pthread_mutex_lock( &mutex ); --readers; pthread_mutex_unlock( &mutex ); return local; #else return global; #endif } void *reader( void *arg ) { (void) arg; // read initial value ERRNO( pthread_rwlock_tryrdlock( &rwlock ) ) // shouldn't fail int local = global_read(); assert( local == INITIAL ); ERRNO( pthread_rwlock_unlock( &rwlock ) ) // here the thread would process initial value pthread_mutex_lock( &mutex ); ++ready_readers; pthread_mutex_unlock( &mutex ); while ( local != EOF ) { // Avoids infinite state space. When fairness will be // implemented, this become unecessary. while ( local == global ); #ifndef BUG ERRNO( pthread_rwlock_rdlock( &rwlock ) ) #endif local = global_read(); // Here the thread would continue with reading and processing of loaded data // (which won't change meanwhile, if the read lock is obtained). assert( local == global_read() ); // fails with macro BUG defined #ifndef BUG ERRNO( pthread_rwlock_unlock( &rwlock ) ) #endif } return NULL; } void *writer( void *arg ) { (void) arg; int local; // wait for all readers to read initial value while ( ready_readers < NUM_OF_READERS ); do { ERRNO( pthread_rwlock_wrlock( &rwlock ) ) local = global_read(); if ( local != EOF ) { // here the thread would produce next value global_write(); } ERRNO( pthread_rwlock_unlock( &rwlock ) ) } while ( local != EOF ); return NULL; } int main() { int i; pthread_t readers[NUM_OF_READERS]; pthread_t writers[NUM_OF_WRITERS]; pthread_mutex_init( &mutex, NULL ); pthread_rwlock_init( &rwlock, NULL ); // create threads for ( i = 0; i < NUM_OF_READERS; i++ ) pthread_create( &readers[i], NULL, reader, NULL ); for ( i = 0; i < NUM_OF_WRITERS; i++ ) pthread_create( &writers[i], NULL, writer, NULL ); // join threads for ( i = 0; i < NUM_OF_READERS; i++ ) pthread_join( readers[i], NULL ); for ( i = 0; i < NUM_OF_WRITERS; i++ ) pthread_join( writers[i], NULL ); assert( global == EOF ); pthread_mutex_destroy( &mutex ); pthread_rwlock_destroy( &rwlock ); return 0; }