#include #include "high_resolution_timer.hpp" #include "pthread.h" #include #define likely(x) x #define unlikely(x) x long dummy; template struct test { test(long count, unsigned num_threads, unsigned write_ratio): count(count),num_threads(num_threads), write_ratio(write_ratio), value(0) {} double run() { boost::thread_group g; boost::high_resolution_timer t; for (int i = 1; i != num_threads; ++i) g.add_thread(new boost::thread(thf_fn, this)); thf(); g.join_all(); return t.elapsed(); } private: static void thf_fn(test* this_){this_->thf();} void thf(void) { int rand; for (long i = 0; i != count; ++i) { rand = rand * 435898247 + 382842987; int num = rand % 8; if (num < write_ratio) { boost::lock_guard lock(mutex); ++value; } else { boost::shared_lock lock(mutex); dummy = value; } } }; long value; long count; long num_threads; long write_ratio; mutex_type mutex; }; class nonrecursive_rw_mutex { public: nonrecursive_rw_mutex(void) { int status = pthread_rwlock_init(&rwlock, NULL); assert(status == 0); } ~nonrecursive_rw_mutex(void) { int status = pthread_rwlock_destroy(&rwlock); assert(status == 0); } void lock(void) { int status = pthread_rwlock_wrlock(&rwlock); assert(status == 0); } bool try_lock(void) { int status = pthread_rwlock_trywrlock(&rwlock); switch (status) { case 0: return true; case EBUSY: return false; case EDEADLK: default: assert(false); } } /* glibc seems to be buggy ... don't unlock more often than it has been locked * * http://sourceware.org/bugzilla/show_bug.cgi?id=4825 */ void unlock(void) { int status = pthread_rwlock_unlock(&rwlock); assert(status == 0); } void lock_shared(void) { int status = pthread_rwlock_rdlock(&rwlock); assert(status == 0); } bool try_lock_shared(void) { int status = pthread_rwlock_tryrdlock(&rwlock); if (status == 0) return true; if (status == EBUSY) return false; assert(false); } void unlock_shared(void) { unlock(); } protected: pthread_rwlock_t rwlock; public: typedef boost::unique_lock unique_lock; typedef boost::shared_lock shared_lock; }; class rw_mutex: public nonrecursive_rw_mutex { public: typedef boost::unique_lock unique_lock; typedef boost::shared_lock shared_lock; rw_mutex(void): writelock_count(0), writer_id(0) {} ~rw_mutex(void) { assert(writer_id == 0); } void lock(void) { int status = pthread_rwlock_wrlock(&rwlock); switch (status) { case 0: writer_id = pthread_self(); assert(writelock_count == 0); case EDEADLK: assert(writer_id == pthread_self()); ++writelock_count; return; default: assert(false); } } bool try_lock(void) { int status = pthread_rwlock_trywrlock(&rwlock); switch (status) { case 0: assert(writer_id == 0); assert(writelock_count == 0); writer_id = pthread_self(); ++writelock_count; return true; case EDEADLK: assert(writer_id == pthread_self()); case EBUSY: if (writer_id == pthread_self()) { assert(writelock_count > 0); ++writelock_count; return true; } else { assert(writer_id != pthread_self()); return false; } default: assert(false); } } void unlock(void) { assert(writelock_count > 0); assert(writer_id); if (--writelock_count == 0) { writer_id = 0; nonrecursive_rw_mutex::unlock(); } } void lock_shared(void) { if (unlikely(writer_id == pthread_self())) /* we're already owning it as writer */ return; nonrecursive_rw_mutex::lock_shared(); } bool try_lock_shared(void) { if (unlikely(writer_id == pthread_self())) /* we're already owning it as writer */ return true; return nonrecursive_rw_mutex::try_lock_shared(); } void unlock_shared(void) { if (unlikely(writer_id == pthread_self())) /* we're owning it as writer */ return; int status = pthread_rwlock_unlock(&rwlock); assert(status == 0); } private: unsigned int writelock_count; pthread_t writer_id; /* id of the writer thread * set during the write lock */ }; using namespace std; int main(void) { long count = 5e6; long num_threads = 2; for (long write_ratio = 0; write_ratio != 9; ++write_ratio) { cout << "writes: " << write_ratio/8.f*100 << "%" << endl; { test tester(count, num_threads, write_ratio); cout << "boost::shared_mutex " << tester.run() << endl; } { test tester(count, num_threads, write_ratio); cout << "nonrecursive_rw_mutex " << tester.run() << endl; } { test tester(count, num_threads, write_ratio); cout << "rw_mutex " << tester.run() << endl; } cout << endl; } }