summaryrefslogtreecommitdiff
path: root/src/mongo/dbtests/threadedtests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/dbtests/threadedtests.cpp')
-rw-r--r--src/mongo/dbtests/threadedtests.cpp1276
1 files changed, 625 insertions, 651 deletions
diff --git a/src/mongo/dbtests/threadedtests.cpp b/src/mongo/dbtests/threadedtests.cpp
index e8af4c8fdd5..f87f8245aec 100644
--- a/src/mongo/dbtests/threadedtests.cpp
+++ b/src/mongo/dbtests/threadedtests.cpp
@@ -57,790 +57,764 @@
namespace ThreadedTests {
- using std::unique_ptr;
- using std::cout;
- using std::endl;
- using std::string;
-
- template <int nthreads_param=10>
- class ThreadedTest {
- public:
- virtual void setup() {} //optional
- virtual void subthread(int remaining) = 0; // each thread whatever test work you want done
- virtual void validate() = 0; // after work is done
-
- static const int nthreads = nthreads_param;
-
- void run() {
- setup();
- launch_subthreads(nthreads);
- validate();
- }
+using std::unique_ptr;
+using std::cout;
+using std::endl;
+using std::string;
+
+template <int nthreads_param = 10>
+class ThreadedTest {
+public:
+ virtual void setup() {} // optional
+ virtual void subthread(int remaining) = 0; // each thread whatever test work you want done
+ virtual void validate() = 0; // after work is done
+
+ static const int nthreads = nthreads_param;
+
+ void run() {
+ setup();
+ launch_subthreads(nthreads);
+ validate();
+ }
- virtual ~ThreadedTest() {}; // not necessary, but makes compilers happy
+ virtual ~ThreadedTest(){}; // not necessary, but makes compilers happy
- private:
- void launch_subthreads(int remaining) {
- if (!remaining)
- return;
+private:
+ void launch_subthreads(int remaining) {
+ if (!remaining)
+ return;
- stdx::thread athread(stdx::bind(&ThreadedTest::subthread, this, remaining));
- launch_subthreads(remaining - 1);
- athread.join();
- }
- };
+ stdx::thread athread(stdx::bind(&ThreadedTest::subthread, this, remaining));
+ launch_subthreads(remaining - 1);
+ athread.join();
+ }
+};
#ifdef MONGO_PLATFORM_32
- // Avoid OOM on Linux-32 by using fewer threads
- const int nthr=45;
+// Avoid OOM on Linux-32 by using fewer threads
+const int nthr = 45;
#else
- const int nthr=135;
+const int nthr = 135;
#endif
- class MongoMutexTest : public ThreadedTest<nthr> {
+class MongoMutexTest : public ThreadedTest<nthr> {
#if defined(MONGO_CONFIG_DEBUG_BUILD)
- enum { N = 2000 };
+ enum { N = 2000 };
#else
- enum { N = 4000/*0*/ };
+ enum { N = 4000 /*0*/ };
#endif
- ProgressMeter pm;
+ ProgressMeter pm;
- public:
- MongoMutexTest() : pm(N * nthreads) {
+public:
+ MongoMutexTest() : pm(N * nthreads) {}
- }
-
- void run() {
- Timer t;
- cout << "MongoMutexTest N:" << N << endl;
- ThreadedTest<nthr>::run();
- cout << "MongoMutexTest " << t.millis() << "ms" << endl;
- }
-
- private:
-
- virtual void subthread(int tnumber) {
- Client::initThread("mongomutextest");
-
- OperationContextImpl txn;
+ void run() {
+ Timer t;
+ cout << "MongoMutexTest N:" << N << endl;
+ ThreadedTest<nthr>::run();
+ cout << "MongoMutexTest " << t.millis() << "ms" << endl;
+ }
- sleepmillis(0);
- for( int i = 0; i < N; i++ ) {
- int x = std::rand();
- bool sometimes = (x % 15 == 0);
- if( i % 7 == 0 ) {
- Lock::GlobalRead r(txn.lockState()); // nested test
- Lock::GlobalRead r2(txn.lockState());
+private:
+ virtual void subthread(int tnumber) {
+ Client::initThread("mongomutextest");
+
+ OperationContextImpl txn;
+
+ sleepmillis(0);
+ for (int i = 0; i < N; i++) {
+ int x = std::rand();
+ bool sometimes = (x % 15 == 0);
+ if (i % 7 == 0) {
+ Lock::GlobalRead r(txn.lockState()); // nested test
+ Lock::GlobalRead r2(txn.lockState());
+ } else if (i % 7 == 1) {
+ Lock::GlobalRead r(txn.lockState());
+ ASSERT(txn.lockState()->isReadLocked());
+ } else if (i % 7 == 4 && tnumber == 1 /*only one upgrader legal*/) {
+ Lock::GlobalWrite w(txn.lockState());
+ ASSERT(txn.lockState()->isW());
+ if (i % 7 == 2) {
+ Lock::TempRelease t(txn.lockState());
}
- else if( i % 7 == 1 ) {
- Lock::GlobalRead r(txn.lockState());
- ASSERT(txn.lockState()->isReadLocked());
+ } else if (i % 7 == 2) {
+ Lock::GlobalWrite w(txn.lockState());
+ ASSERT(txn.lockState()->isW());
+ if (sometimes) {
+ Lock::TempRelease t(txn.lockState());
}
- else if( i % 7 == 4 &&
- tnumber == 1 /*only one upgrader legal*/ ) {
- Lock::GlobalWrite w(txn.lockState());
- ASSERT( txn.lockState()->isW() );
- if( i % 7 == 2 ) {
- Lock::TempRelease t(txn.lockState());
- }
+ } else if (i % 7 == 3) {
+ Lock::GlobalWrite w(txn.lockState());
+ { Lock::TempRelease t(txn.lockState()); }
+ Lock::GlobalRead r(txn.lockState());
+ ASSERT(txn.lockState()->isW());
+ if (sometimes) {
+ Lock::TempRelease t(txn.lockState());
}
- else if( i % 7 == 2 ) {
- Lock::GlobalWrite w(txn.lockState());
- ASSERT( txn.lockState()->isW() );
- if( sometimes ) {
- Lock::TempRelease t(txn.lockState());
- }
+ } else if (i % 7 == 5) {
+ {
+ ScopedTransaction scopedXact(&txn, MODE_IS);
+ Lock::DBLock r(txn.lockState(), "foo", MODE_S);
}
- else if( i % 7 == 3 ) {
- Lock::GlobalWrite w(txn.lockState());
- {
- Lock::TempRelease t(txn.lockState());
- }
- Lock::GlobalRead r(txn.lockState());
- ASSERT( txn.lockState()->isW() );
- if( sometimes ) {
- Lock::TempRelease t(txn.lockState());
- }
+ {
+ ScopedTransaction scopedXact(&txn, MODE_IS);
+ Lock::DBLock r(txn.lockState(), "bar", MODE_S);
}
- else if( i % 7 == 5 ) {
- {
- ScopedTransaction scopedXact(&txn, MODE_IS);
- Lock::DBLock r(txn.lockState(), "foo", MODE_S);
- }
- {
+ } else if (i % 7 == 6) {
+ if (i > N / 2) {
+ int q = i % 11;
+ if (q == 0) {
ScopedTransaction scopedXact(&txn, MODE_IS);
- Lock::DBLock r(txn.lockState(), "bar", MODE_S);
- }
- }
- else if( i % 7 == 6 ) {
- if( i > N/2 ) {
- int q = i % 11;
- if( q == 0 ) {
- ScopedTransaction scopedXact(&txn, MODE_IS);
-
- Lock::DBLock r(txn.lockState(), "foo", MODE_S);
- ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S));
- Lock::DBLock r2(txn.lockState(), "foo", MODE_S);
- ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S));
+ Lock::DBLock r(txn.lockState(), "foo", MODE_S);
+ ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S));
- Lock::DBLock r3(txn.lockState(), "local", MODE_S);
- ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S));
- ASSERT(txn.lockState()->isDbLockedForMode("local", MODE_S));
- }
- else if( q == 1 ) {
- // test locking local only -- with no preceding lock
- {
- ScopedTransaction scopedXact(&txn, MODE_IS);
- Lock::DBLock x(txn.lockState(), "local", MODE_S);
- }
- {
- ScopedTransaction scopedXact(&txn, MODE_IX);
- Lock::DBLock x(txn.lockState(), "local", MODE_X);
-
- // No actual writing here, so no WriteUnitOfWork
- if( sometimes ) {
- Lock::TempRelease t(txn.lockState());
- }
- }
- } else if( q == 1 ) {
- {
- ScopedTransaction scopedXact(&txn, MODE_IS);
- Lock::DBLock x(txn.lockState(), "admin", MODE_S);
- }
+ Lock::DBLock r2(txn.lockState(), "foo", MODE_S);
+ ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S));
- {
- ScopedTransaction scopedXact(&txn, MODE_IX);
- Lock::DBLock x(txn.lockState(), "admin", MODE_X);
- }
+ Lock::DBLock r3(txn.lockState(), "local", MODE_S);
+ ASSERT(txn.lockState()->isDbLockedForMode("foo", MODE_S));
+ ASSERT(txn.lockState()->isDbLockedForMode("local", MODE_S));
+ } else if (q == 1) {
+ // test locking local only -- with no preceding lock
+ {
+ ScopedTransaction scopedXact(&txn, MODE_IS);
+ Lock::DBLock x(txn.lockState(), "local", MODE_S);
}
- else if( q == 3 ) {
+ {
ScopedTransaction scopedXact(&txn, MODE_IX);
+ Lock::DBLock x(txn.lockState(), "local", MODE_X);
- Lock::DBLock x(txn.lockState(), "foo", MODE_X);
- Lock::DBLock y(txn.lockState(), "admin", MODE_S);
+ // No actual writing here, so no WriteUnitOfWork
+ if (sometimes) {
+ Lock::TempRelease t(txn.lockState());
+ }
}
- else if( q == 4 ) {
+ } else if (q == 1) {
+ {
ScopedTransaction scopedXact(&txn, MODE_IS);
-
- Lock::DBLock x(txn.lockState(), "foo2", MODE_S);
- Lock::DBLock y(txn.lockState(), "admin", MODE_S);
+ Lock::DBLock x(txn.lockState(), "admin", MODE_S);
}
- else {
+
+ {
ScopedTransaction scopedXact(&txn, MODE_IX);
+ Lock::DBLock x(txn.lockState(), "admin", MODE_X);
+ }
+ } else if (q == 3) {
+ ScopedTransaction scopedXact(&txn, MODE_IX);
- Lock::DBLock w(txn.lockState(), "foo", MODE_X);
+ Lock::DBLock x(txn.lockState(), "foo", MODE_X);
+ Lock::DBLock y(txn.lockState(), "admin", MODE_S);
+ } else if (q == 4) {
+ ScopedTransaction scopedXact(&txn, MODE_IS);
- {
- Lock::TempRelease t(txn.lockState());
- }
+ Lock::DBLock x(txn.lockState(), "foo2", MODE_S);
+ Lock::DBLock y(txn.lockState(), "admin", MODE_S);
+ } else {
+ ScopedTransaction scopedXact(&txn, MODE_IX);
- Lock::DBLock r2(txn.lockState(), "foo", MODE_S);
- Lock::DBLock r3(txn.lockState(), "local", MODE_S);
- }
- }
- else {
- ScopedTransaction scopedXact(&txn, MODE_IS);
+ Lock::DBLock w(txn.lockState(), "foo", MODE_X);
+
+ { Lock::TempRelease t(txn.lockState()); }
- Lock::DBLock r(txn.lockState(), "foo", MODE_S);
Lock::DBLock r2(txn.lockState(), "foo", MODE_S);
Lock::DBLock r3(txn.lockState(), "local", MODE_S);
}
+ } else {
+ ScopedTransaction scopedXact(&txn, MODE_IS);
+
+ Lock::DBLock r(txn.lockState(), "foo", MODE_S);
+ Lock::DBLock r2(txn.lockState(), "foo", MODE_S);
+ Lock::DBLock r3(txn.lockState(), "local", MODE_S);
}
- pm.hit();
}
+ pm.hit();
}
+ }
- virtual void validate() {
- {
- MMAPV1LockerImpl ls;
- Lock::GlobalWrite w(&ls);
- }
- {
- MMAPV1LockerImpl ls;
- Lock::GlobalRead r(&ls);
- }
+ virtual void validate() {
+ {
+ MMAPV1LockerImpl ls;
+ Lock::GlobalWrite w(&ls);
}
- };
+ {
+ MMAPV1LockerImpl ls;
+ Lock::GlobalRead r(&ls);
+ }
+ }
+};
- template <typename _AtomicUInt>
- class IsAtomicWordAtomic : public ThreadedTest<> {
- static const int iterations = 1000000;
- typedef typename _AtomicUInt::WordType WordType;
- _AtomicUInt target;
+template <typename _AtomicUInt>
+class IsAtomicWordAtomic : public ThreadedTest<> {
+ static const int iterations = 1000000;
+ typedef typename _AtomicUInt::WordType WordType;
+ _AtomicUInt target;
- void subthread(int) {
- for(int i=0; i < iterations; i++) {
- target.fetchAndAdd(WordType(1));
- }
- }
- void validate() {
- ASSERT_EQUALS(target.load() , unsigned(nthreads * iterations));
-
- _AtomicUInt u;
- ASSERT_EQUALS(0u, u.load());
- ASSERT_EQUALS(0u, u.fetchAndAdd(WordType(1)));
- ASSERT_EQUALS(2u, u.addAndFetch(WordType(1)));
- ASSERT_EQUALS(2u, u.fetchAndSubtract(WordType(1)));
- ASSERT_EQUALS(0u, u.subtractAndFetch(WordType(1)));
- ASSERT_EQUALS(0u, u.load());
-
- u.fetchAndAdd(WordType(1));
- ASSERT_GREATER_THAN(u.load(), WordType(0));
-
- u.fetchAndSubtract(WordType(1));
- ASSERT_NOT_GREATER_THAN(u.load(), WordType(0));
+ void subthread(int) {
+ for (int i = 0; i < iterations; i++) {
+ target.fetchAndAdd(WordType(1));
}
- };
+ }
+ void validate() {
+ ASSERT_EQUALS(target.load(), unsigned(nthreads * iterations));
+
+ _AtomicUInt u;
+ ASSERT_EQUALS(0u, u.load());
+ ASSERT_EQUALS(0u, u.fetchAndAdd(WordType(1)));
+ ASSERT_EQUALS(2u, u.addAndFetch(WordType(1)));
+ ASSERT_EQUALS(2u, u.fetchAndSubtract(WordType(1)));
+ ASSERT_EQUALS(0u, u.subtractAndFetch(WordType(1)));
+ ASSERT_EQUALS(0u, u.load());
+
+ u.fetchAndAdd(WordType(1));
+ ASSERT_GREATER_THAN(u.load(), WordType(0));
+
+ u.fetchAndSubtract(WordType(1));
+ ASSERT_NOT_GREATER_THAN(u.load(), WordType(0));
+ }
+};
- class MVarTest : public ThreadedTest<> {
- static const int iterations = 10000;
- MVar<int> target;
+class MVarTest : public ThreadedTest<> {
+ static const int iterations = 10000;
+ MVar<int> target;
- public:
- MVarTest() : target(0) {}
- void subthread(int) {
- for(int i=0; i < iterations; i++) {
- int val = target.take();
+public:
+ MVarTest() : target(0) {}
+ void subthread(int) {
+ for (int i = 0; i < iterations; i++) {
+ int val = target.take();
#if BOOST_VERSION >= 103500
- //increase chances of catching failure
- stdx::this_thread::yield();
+ // increase chances of catching failure
+ stdx::this_thread::yield();
#endif
- target.put(val+1);
- }
- }
- void validate() {
- ASSERT_EQUALS(target.take() , nthreads * iterations);
+ target.put(val + 1);
}
- };
+ }
+ void validate() {
+ ASSERT_EQUALS(target.take(), nthreads * iterations);
+ }
+};
- class ThreadPoolTest {
- static const unsigned iterations = 10000;
- static const unsigned nThreads = 8;
+class ThreadPoolTest {
+ static const unsigned iterations = 10000;
+ static const unsigned nThreads = 8;
- AtomicUInt32 counter;
- void increment(unsigned n) {
- for (unsigned i=0; i<n; i++) {
- counter.fetchAndAdd(1);
- }
+ AtomicUInt32 counter;
+ void increment(unsigned n) {
+ for (unsigned i = 0; i < n; i++) {
+ counter.fetchAndAdd(1);
}
+ }
- public:
- void run() {
- OldThreadPool tp(nThreads);
-
- for (unsigned i=0; i < iterations; i++) {
- tp.schedule(&ThreadPoolTest::increment, this, 2);
- }
-
- tp.join();
+public:
+ void run() {
+ OldThreadPool tp(nThreads);
- ASSERT_EQUALS(counter.load(), iterations * 2);
+ for (unsigned i = 0; i < iterations; i++) {
+ tp.schedule(&ThreadPoolTest::increment, this, 2);
}
- };
- class RWLockTest1 {
- public:
- void run() {
- RWLock lk( "eliot" );
- {
- rwlock r( lk , true , 1000 );
- }
- }
- };
+ tp.join();
- class RWLockTest2 {
- public:
- static void worker1( RWLockRecursiveNongreedy * lk , AtomicUInt32 * x ) {
- x->fetchAndAdd(1); // 1
- RWLockRecursiveNongreedy::Exclusive b(*lk);
- x->fetchAndAdd(1); // 2
- }
- static void worker2( RWLockRecursiveNongreedy * lk , AtomicUInt32 * x ) {
- RWLockRecursiveNongreedy::Shared c(*lk);
- x->fetchAndAdd(1);
- }
- void run() {
- /**
- * note: this test will deadlock if the code breaks
- */
- RWLockRecursiveNongreedy lk( "eliot2" , 120 * 1000 );
- cout << "RWLock impl: " << lk.implType() << endl;
- unique_ptr<RWLockRecursiveNongreedy::Shared> a( new RWLockRecursiveNongreedy::Shared(lk) );
- AtomicUInt32 x1(0);
- cout << "A : " << &x1 << endl;
- stdx::thread t1( stdx::bind( worker1 , &lk , &x1 ) );
- while ( ! x1.load() );
- verify( x1.load() == 1 );
- sleepmillis( 500 );
- verify( x1.load() == 1 );
- AtomicUInt32 x2(0);
- stdx::thread t2( stdx::bind( worker2, &lk , &x2 ) );
- t2.join();
- verify( x2.load() == 1 );
- a.reset();
- for ( int i=0; i<2000; i++ ) {
- if ( x1.load() == 2 )
- break;
- sleepmillis(1);
- }
- verify( x1.load() == 2 );
- t1.join();
- }
- };
+ ASSERT_EQUALS(counter.load(), iterations * 2);
+ }
+};
- class RWLockTest3 {
- public:
- static void worker2( RWLockRecursiveNongreedy * lk , AtomicUInt32 * x ) {
- verify( ! lk->__lock_try(0) );
- RWLockRecursiveNongreedy::Shared c( *lk );
- x->fetchAndAdd(1);
+class RWLockTest1 {
+public:
+ void run() {
+ RWLock lk("eliot");
+ { rwlock r(lk, true, 1000); }
+ }
+};
+
+class RWLockTest2 {
+public:
+ static void worker1(RWLockRecursiveNongreedy* lk, AtomicUInt32* x) {
+ x->fetchAndAdd(1); // 1
+ RWLockRecursiveNongreedy::Exclusive b(*lk);
+ x->fetchAndAdd(1); // 2
+ }
+ static void worker2(RWLockRecursiveNongreedy* lk, AtomicUInt32* x) {
+ RWLockRecursiveNongreedy::Shared c(*lk);
+ x->fetchAndAdd(1);
+ }
+ void run() {
+ /**
+ * note: this test will deadlock if the code breaks
+ */
+ RWLockRecursiveNongreedy lk("eliot2", 120 * 1000);
+ cout << "RWLock impl: " << lk.implType() << endl;
+ unique_ptr<RWLockRecursiveNongreedy::Shared> a(new RWLockRecursiveNongreedy::Shared(lk));
+ AtomicUInt32 x1(0);
+ cout << "A : " << &x1 << endl;
+ stdx::thread t1(stdx::bind(worker1, &lk, &x1));
+ while (!x1.load())
+ ;
+ verify(x1.load() == 1);
+ sleepmillis(500);
+ verify(x1.load() == 1);
+ AtomicUInt32 x2(0);
+ stdx::thread t2(stdx::bind(worker2, &lk, &x2));
+ t2.join();
+ verify(x2.load() == 1);
+ a.reset();
+ for (int i = 0; i < 2000; i++) {
+ if (x1.load() == 2)
+ break;
+ sleepmillis(1);
}
+ verify(x1.load() == 2);
+ t1.join();
+ }
+};
+
+class RWLockTest3 {
+public:
+ static void worker2(RWLockRecursiveNongreedy* lk, AtomicUInt32* x) {
+ verify(!lk->__lock_try(0));
+ RWLockRecursiveNongreedy::Shared c(*lk);
+ x->fetchAndAdd(1);
+ }
- void run() {
- /**
- * note: this test will deadlock if the code breaks
- */
+ void run() {
+ /**
+ * note: this test will deadlock if the code breaks
+ */
- RWLockRecursiveNongreedy lk( "eliot2" , 120 * 1000 );
+ RWLockRecursiveNongreedy lk("eliot2", 120 * 1000);
- unique_ptr<RWLockRecursiveNongreedy::Shared> a( new RWLockRecursiveNongreedy::Shared( lk ) );
+ unique_ptr<RWLockRecursiveNongreedy::Shared> a(new RWLockRecursiveNongreedy::Shared(lk));
- AtomicUInt32 x2(0);
+ AtomicUInt32 x2(0);
- stdx::thread t2( stdx::bind( worker2, &lk , &x2 ) );
- t2.join();
- verify( x2.load() == 1 );
+ stdx::thread t2(stdx::bind(worker2, &lk, &x2));
+ t2.join();
+ verify(x2.load() == 1);
- a.reset();
- }
- };
-
- class RWLockTest4 {
- public:
+ a.reset();
+ }
+};
+class RWLockTest4 {
+public:
#if defined(__linux__) || defined(__APPLE__)
- static void worker1( pthread_rwlock_t * lk , AtomicUInt32 * x ) {
- x->fetchAndAdd(1); // 1
- cout << "lock b try" << endl;
- while ( 1 ) {
- if ( pthread_rwlock_trywrlock( lk ) == 0 )
- break;
- sleepmillis(10);
- }
- cout << "lock b got" << endl;
- x->fetchAndAdd(1); // 2
- pthread_rwlock_unlock( lk );
+ static void worker1(pthread_rwlock_t* lk, AtomicUInt32* x) {
+ x->fetchAndAdd(1); // 1
+ cout << "lock b try" << endl;
+ while (1) {
+ if (pthread_rwlock_trywrlock(lk) == 0)
+ break;
+ sleepmillis(10);
}
+ cout << "lock b got" << endl;
+ x->fetchAndAdd(1); // 2
+ pthread_rwlock_unlock(lk);
+ }
- static void worker2( pthread_rwlock_t * lk , AtomicUInt32 * x ) {
- cout << "lock c try" << endl;
- pthread_rwlock_rdlock( lk );
- x->fetchAndAdd(1);
- cout << "lock c got" << endl;
- pthread_rwlock_unlock( lk );
- }
+ static void worker2(pthread_rwlock_t* lk, AtomicUInt32* x) {
+ cout << "lock c try" << endl;
+ pthread_rwlock_rdlock(lk);
+ x->fetchAndAdd(1);
+ cout << "lock c got" << endl;
+ pthread_rwlock_unlock(lk);
+ }
#endif
- void run() {
- /**
- * note: this test will deadlock if the code breaks
- */
+ void run() {
+/**
+ * note: this test will deadlock if the code breaks
+ */
#if defined(__linux__) || defined(__APPLE__)
- // create
- pthread_rwlock_t lk;
- verify( pthread_rwlock_init( &lk , 0 ) == 0 );
+ // create
+ pthread_rwlock_t lk;
+ verify(pthread_rwlock_init(&lk, 0) == 0);
- // read lock
- verify( pthread_rwlock_rdlock( &lk ) == 0 );
+ // read lock
+ verify(pthread_rwlock_rdlock(&lk) == 0);
- AtomicUInt32 x1(0);
- stdx::thread t1( stdx::bind( worker1 , &lk , &x1 ) );
- while ( ! x1.load() );
- verify( x1.load() == 1 );
- sleepmillis( 500 );
- verify( x1.load() == 1 );
+ AtomicUInt32 x1(0);
+ stdx::thread t1(stdx::bind(worker1, &lk, &x1));
+ while (!x1.load())
+ ;
+ verify(x1.load() == 1);
+ sleepmillis(500);
+ verify(x1.load() == 1);
- AtomicUInt32 x2(0);
+ AtomicUInt32 x2(0);
- stdx::thread t2( stdx::bind( worker2, &lk , &x2 ) );
- t2.join();
- verify( x2.load() == 1 );
+ stdx::thread t2(stdx::bind(worker2, &lk, &x2));
+ t2.join();
+ verify(x2.load() == 1);
- pthread_rwlock_unlock( &lk );
+ pthread_rwlock_unlock(&lk);
- for ( int i=0; i<2000; i++ ) {
- if ( x1.load() == 2 )
- break;
- sleepmillis(1);
- }
-
- verify( x1.load() == 2 );
- t1.join();
-#endif
+ for (int i = 0; i < 2000; i++) {
+ if (x1.load() == 2)
+ break;
+ sleepmillis(1);
}
- };
- // we don't use upgrade so that part is not important currently but the other aspects of this test are
- // interesting; it would be nice to do analogous tests for SimpleRWLock and QLock
- class UpgradableTest : public ThreadedTest<7> {
- RWLock m;
- public:
- UpgradableTest() : m("utest") {}
- private:
- virtual void validate() { }
- virtual void subthread(int x) {
- Client::initThread("utest");
-
- /* r = get a read lock
- R = get a read lock and we expect it to be fast
- u = get upgradable
- U = get upgradable and we expect it to be fast
- w = get a write lock
- */
- // /-- verify upgrade can be done instantly while in a read lock already
- // | /-- verify upgrade acquisition isn't greedy
- // | | /-- verify writes aren't greedy while in upgradable (or are they?)
- // v v v
- const char *what = " RURuRwR";
-
- sleepmillis(100*x);
-
- int Z = 1;
- LOG(Z) << x << ' ' << what[x] << " request" << endl;
- char ch = what[x];
- switch( ch ) {
- case 'w':
- {
- m.lock();
- LOG(Z) << x << " w got" << endl;
- sleepmillis(100);
- LOG(Z) << x << " w unlock" << endl;
- m.unlock();
- }
- break;
+ verify(x1.load() == 2);
+ t1.join();
+#endif
+ }
+};
+
+// we don't use upgrade so that part is not important currently but the other aspects of this test are
+// interesting; it would be nice to do analogous tests for SimpleRWLock and QLock
+class UpgradableTest : public ThreadedTest<7> {
+ RWLock m;
+
+public:
+ UpgradableTest() : m("utest") {}
+
+private:
+ virtual void validate() {}
+ virtual void subthread(int x) {
+ Client::initThread("utest");
+
+ /* r = get a read lock
+ R = get a read lock and we expect it to be fast
+ u = get upgradable
+ U = get upgradable and we expect it to be fast
+ w = get a write lock
+ */
+ // /-- verify upgrade can be done instantly while in a read lock already
+ // | /-- verify upgrade acquisition isn't greedy
+ // | | /-- verify writes aren't greedy while in upgradable (or are they?)
+ // v v v
+ const char* what = " RURuRwR";
+
+ sleepmillis(100 * x);
+
+ int Z = 1;
+ LOG(Z) << x << ' ' << what[x] << " request" << endl;
+ char ch = what[x];
+ switch (ch) {
+ case 'w': {
+ m.lock();
+ LOG(Z) << x << " w got" << endl;
+ sleepmillis(100);
+ LOG(Z) << x << " w unlock" << endl;
+ m.unlock();
+ } break;
case 'u':
- case 'U':
- {
- Timer t;
- RWLock::Upgradable u(m);
- LOG(Z) << x << ' ' << ch << " got" << endl;
- if( ch == 'U' ) {
+ case 'U': {
+ Timer t;
+ RWLock::Upgradable u(m);
+ LOG(Z) << x << ' ' << ch << " got" << endl;
+ if (ch == 'U') {
#if defined(NTDDI_VERSION) && defined(NTDDI_WIN7) && (NTDDI_VERSION >= NTDDI_WIN7)
- // SRW locks are neither fair nor FIFO, as per docs
- if( t.millis() > 2000 ) {
+ // SRW locks are neither fair nor FIFO, as per docs
+ if (t.millis() > 2000) {
#else
- if( t.millis() > 20 ) {
+ if (t.millis() > 20) {
#endif
- DEV {
- // a debug buildbot might be slow, try to avoid false positives
- mongo::unittest::log() <<
- "warning lock upgrade was slow " << t.millis() << endl;
- }
- else {
- mongo::unittest::log() <<
- "assertion failure: lock upgrade was too slow: " <<
- t.millis() << endl;
- ASSERT( false );
- }
+ DEV {
+ // a debug buildbot might be slow, try to avoid false positives
+ mongo::unittest::log() << "warning lock upgrade was slow " << t.millis()
+ << endl;
+ }
+ else {
+ mongo::unittest::log()
+ << "assertion failure: lock upgrade was too slow: " << t.millis()
+ << endl;
+ ASSERT(false);
}
}
- sleepsecs(1);
- LOG(Z) << x << ' ' << ch << " unlock" << endl;
}
- break;
+ sleepsecs(1);
+ LOG(Z) << x << ' ' << ch << " unlock" << endl;
+ } break;
case 'r':
- case 'R':
- {
- Timer t;
- m.lock_shared();
- LOG(Z) << x << ' ' << ch << " got " << endl;
- if( what[x] == 'R' ) {
- if( t.millis() > 15 ) {
- // commented out for less chatter, we aren't using upgradeable anyway right now:
- // log() << x << " info: when in upgradable, write locks are still greedy on this platform" << endl;
- }
+ case 'R': {
+ Timer t;
+ m.lock_shared();
+ LOG(Z) << x << ' ' << ch << " got " << endl;
+ if (what[x] == 'R') {
+ if (t.millis() > 15) {
+ // commented out for less chatter, we aren't using upgradeable anyway right now:
+ // log() << x << " info: when in upgradable, write locks are still greedy on this platform" << endl;
}
- sleepmillis(200);
- LOG(Z) << x << ' ' << ch << " unlock" << endl;
- m.unlock_shared();
}
- break;
+ sleepmillis(200);
+ LOG(Z) << x << ' ' << ch << " unlock" << endl;
+ m.unlock_shared();
+ } break;
default:
ASSERT(false);
- }
- }
- };
-
- void sleepalittle() {
- Timer t;
- while( 1 ) {
- stdx::this_thread::yield();
- if( t.micros() > 8 )
- break;
}
}
+};
+
+void sleepalittle() {
+ Timer t;
+ while (1) {
+ stdx::this_thread::yield();
+ if (t.micros() > 8)
+ break;
+ }
+}
- int once;
+int once;
+
+/* This test is to see how long it takes to get a lock after there has been contention -- the OS
+ will need to reschedule us. if a spinlock, it will be fast of course, but these aren't spin locks.
+ Experimenting with different # of threads would be a good idea.
+*/
+template <class whichmutex, class scoped>
+class Slack : public ThreadedTest<17> {
+public:
+ Slack() {
+ k = 0;
+ done = false;
+ a = b = 0;
+ locks = 0;
+ }
- /* This test is to see how long it takes to get a lock after there has been contention -- the OS
- will need to reschedule us. if a spinlock, it will be fast of course, but these aren't spin locks.
- Experimenting with different # of threads would be a good idea.
- */
- template <class whichmutex, class scoped>
- class Slack : public ThreadedTest<17> {
- public:
- Slack() {
- k = 0;
- done = false;
- a = b = 0;
- locks = 0;
- }
- private:
- whichmutex m;
- char pad1[128];
- unsigned a, b;
- char pad2[128];
- unsigned locks;
- char pad3[128];
- volatile int k;
-
- virtual void validate() {
- if( once++ == 0 ) {
- // <= 1.35 we use a different rwmutex impl so worth noting
- cout << "Boost version : " << BOOST_VERSION << endl;
+private:
+ whichmutex m;
+ char pad1[128];
+ unsigned a, b;
+ char pad2[128];
+ unsigned locks;
+ char pad3[128];
+ volatile int k;
+
+ virtual void validate() {
+ if (once++ == 0) {
+ // <= 1.35 we use a different rwmutex impl so worth noting
+ cout << "Boost version : " << BOOST_VERSION << endl;
+ }
+ cout << typeid(whichmutex).name() << " Slack useful work fraction: " << ((double)a) / b
+ << " locks:" << locks << endl;
+ }
+ void watch() {
+ while (1) {
+ b++;
+ //__sync_synchronize();
+ if (k) {
+ a++;
}
- cout << typeid(whichmutex).name() <<
- " Slack useful work fraction: " << ((double)a)/b << " locks:" << locks << endl;
+ sleepmillis(0);
+ if (done)
+ break;
}
- void watch() {
- while( 1 ) {
- b++;
- //__sync_synchronize();
- if( k ) {
- a++;
- }
- sleepmillis(0);
- if( done )
- break;
- }
+ }
+ volatile bool done;
+ virtual void subthread(int x) {
+ if (x == 1) {
+ watch();
+ return;
}
- volatile bool done;
- virtual void subthread(int x) {
- if( x == 1 ) {
- watch();
- return;
- }
- Timer t;
- unsigned lks = 0;
- while( 1 ) {
- scoped lk(m);
- k = 1;
- // not very long, we'd like to simulate about 100K locks per second
- sleepalittle();
- lks++;
- if( done || t.millis() > 1500 ) {
- locks += lks;
- k = 0;
- break;
- }
+ Timer t;
+ unsigned lks = 0;
+ while (1) {
+ scoped lk(m);
+ k = 1;
+ // not very long, we'd like to simulate about 100K locks per second
+ sleepalittle();
+ lks++;
+ if (done || t.millis() > 1500) {
+ locks += lks;
k = 0;
- //__sync_synchronize();
+ break;
}
- done = true;
+ k = 0;
+ //__sync_synchronize();
}
- };
+ done = true;
+ }
+};
- class CondSlack : public ThreadedTest<17> {
- Notification n;
- public:
- CondSlack() {
- k = 0;
- done = false;
- a = b = 0;
- locks = 0;
+class CondSlack : public ThreadedTest<17> {
+ Notification n;
+
+public:
+ CondSlack() {
+ k = 0;
+ done = false;
+ a = b = 0;
+ locks = 0;
+ }
+
+private:
+ unsigned a, b;
+ virtual void validate() {
+ cout << "CondSlack useful work fraction: " << ((double)a) / b << " locks:" << locks << endl;
+ }
+ unsigned locks;
+ volatile int k;
+ void watch() {
+ while (1) {
+ b++;
+ if (k) {
+ a++;
+ }
+ sleepmillis(0);
+ if (done)
+ break;
}
- private:
- unsigned a, b;
- virtual void validate() {
- cout << "CondSlack useful work fraction: " << ((double)a)/b << " locks:" << locks << endl;
+ }
+ volatile bool done;
+ virtual void subthread(int x) {
+ if (x == 1) {
+ n.notifyOne();
+ watch();
+ return;
}
- unsigned locks;
- volatile int k;
- void watch() {
- while( 1 ) {
- b++;
- if( k ) {
- a++;
- }
- sleepmillis(0);
- if( done )
- break;
- }
+ Timer t;
+ while (1) {
+ n.waitToBeNotified();
+ verify(k == 0);
+ k = 1;
+ // not very long, we'd like to simulate about 100K locks per second
+ sleepalittle();
+ k = 0;
+ locks++;
+ n.notifyOne();
+ if (done || t.millis() > 1500)
+ break;
}
- volatile bool done;
- virtual void subthread(int x) {
- if( x == 1 ) {
- n.notifyOne();
- watch();
- return;
- }
+ done = true;
+ }
+};
+
+const int WriteLocksAreGreedy_ThreadCount = 3;
+class WriteLocksAreGreedy : public ThreadedTest<WriteLocksAreGreedy_ThreadCount> {
+public:
+ WriteLocksAreGreedy() : m("gtest"), _barrier(WriteLocksAreGreedy_ThreadCount) {}
+
+private:
+ RWLock m;
+ boost::barrier _barrier;
+ virtual void validate() {}
+ virtual void subthread(int x) {
+ _barrier.wait();
+ int Z = 0;
+ Client::initThread("utest");
+ if (x == 1) {
+ LOG(Z) << mongo::curTimeMillis64() % 10000 << " 1" << endl;
+ rwlock_shared lk(m);
+ sleepmillis(400);
+ LOG(Z) << mongo::curTimeMillis64() % 10000 << " 1x" << endl;
+ }
+ if (x == 2) {
+ sleepmillis(100);
+ LOG(Z) << mongo::curTimeMillis64() % 10000 << " 2" << endl;
+ rwlock lk(m, true);
+ LOG(Z) << mongo::curTimeMillis64() % 10000 << " 2x" << endl;
+ }
+ if (x == 3) {
+ sleepmillis(200);
Timer t;
- while( 1 ) {
- n.waitToBeNotified();
- verify( k == 0 );
- k = 1;
- // not very long, we'd like to simulate about 100K locks per second
- sleepalittle();
- k = 0;
- locks++;
- n.notifyOne();
- if( done || t.millis() > 1500 )
- break;
- }
- done = true;
- }
- };
-
- const int WriteLocksAreGreedy_ThreadCount = 3;
- class WriteLocksAreGreedy : public ThreadedTest<WriteLocksAreGreedy_ThreadCount> {
- public:
- WriteLocksAreGreedy() : m("gtest"), _barrier(WriteLocksAreGreedy_ThreadCount) {}
- private:
- RWLock m;
- boost::barrier _barrier;
- virtual void validate() { }
- virtual void subthread(int x) {
- _barrier.wait();
- int Z = 0;
- Client::initThread("utest");
- if( x == 1 ) {
- LOG(Z) << mongo::curTimeMillis64() % 10000 << " 1" << endl;
- rwlock_shared lk(m);
- sleepmillis(400);
- LOG(Z) << mongo::curTimeMillis64() % 10000 << " 1x" << endl;
- }
- if( x == 2 ) {
- sleepmillis(100);
- LOG(Z) << mongo::curTimeMillis64() % 10000 << " 2" << endl;
- rwlock lk(m, true);
- LOG(Z) << mongo::curTimeMillis64() % 10000 << " 2x" << endl;
- }
- if( x == 3 ) {
- sleepmillis(200);
- Timer t;
- LOG(Z) << mongo::curTimeMillis64() % 10000 << " 3" << endl;
- rwlock_shared lk(m);
- LOG(Z) << mongo::curTimeMillis64() % 10000 << " 3x" << endl;
- LOG(Z) << t.millis() << endl;
- ASSERT( t.millis() > 50 );
- }
+ LOG(Z) << mongo::curTimeMillis64() % 10000 << " 3" << endl;
+ rwlock_shared lk(m);
+ LOG(Z) << mongo::curTimeMillis64() % 10000 << " 3x" << endl;
+ LOG(Z) << t.millis() << endl;
+ ASSERT(t.millis() > 50);
}
- };
+ }
+};
- // Tests waiting on the TicketHolder by running many more threads than can fit into the "hotel", but only
- // max _nRooms threads should ever get in at once
- class TicketHolderWaits : public ThreadedTest<10> {
+// Tests waiting on the TicketHolder by running many more threads than can fit into the "hotel", but only
+// max _nRooms threads should ever get in at once
+class TicketHolderWaits : public ThreadedTest<10> {
+ static const int checkIns = 1000;
+ static const int rooms = 3;
- static const int checkIns = 1000;
- static const int rooms = 3;
+public:
+ TicketHolderWaits() : _hotel(rooms), _tickets(_hotel._nRooms) {}
+private:
+ class Hotel {
public:
- TicketHolderWaits() : _hotel( rooms ), _tickets( _hotel._nRooms ) {}
-
- private:
-
- class Hotel {
- public:
- Hotel( int nRooms ) : _nRooms( nRooms ), _checkedIn( 0 ), _maxRooms( 0 ) {}
-
- void checkIn(){
- stdx::lock_guard<stdx::mutex> lk( _frontDesk );
- _checkedIn++;
- verify( _checkedIn <= _nRooms );
- if( _checkedIn > _maxRooms ) _maxRooms = _checkedIn;
- }
-
- void checkOut(){
- stdx::lock_guard<stdx::mutex> lk( _frontDesk );
- _checkedIn--;
- verify( _checkedIn >= 0 );
- }
-
- stdx::mutex _frontDesk;
- int _nRooms;
- int _checkedIn;
- int _maxRooms;
- };
-
- Hotel _hotel;
- TicketHolder _tickets;
-
- virtual void subthread(int x) {
+ Hotel(int nRooms) : _nRooms(nRooms), _checkedIn(0), _maxRooms(0) {}
- string threadName = ( str::stream() << "ticketHolder" << x );
- Client::initThread( threadName.c_str() );
+ void checkIn() {
+ stdx::lock_guard<stdx::mutex> lk(_frontDesk);
+ _checkedIn++;
+ verify(_checkedIn <= _nRooms);
+ if (_checkedIn > _maxRooms)
+ _maxRooms = _checkedIn;
+ }
- for( int i = 0; i < checkIns; i++ ){
+ void checkOut() {
+ stdx::lock_guard<stdx::mutex> lk(_frontDesk);
+ _checkedIn--;
+ verify(_checkedIn >= 0);
+ }
- _tickets.waitForTicket();
- TicketHolderReleaser whenDone( &_tickets );
+ stdx::mutex _frontDesk;
+ int _nRooms;
+ int _checkedIn;
+ int _maxRooms;
+ };
- _hotel.checkIn();
+ Hotel _hotel;
+ TicketHolder _tickets;
- sleepalittle();
- if( i == checkIns - 1 ) sleepsecs( 2 );
+ virtual void subthread(int x) {
+ string threadName = (str::stream() << "ticketHolder" << x);
+ Client::initThread(threadName.c_str());
- _hotel.checkOut();
+ for (int i = 0; i < checkIns; i++) {
+ _tickets.waitForTicket();
+ TicketHolderReleaser whenDone(&_tickets);
- if( ( i % ( checkIns / 10 ) ) == 0 )
- mongo::unittest::log() << "checked in " << i << " times..." << endl;
+ _hotel.checkIn();
- }
- }
-
- virtual void validate() {
+ sleepalittle();
+ if (i == checkIns - 1)
+ sleepsecs(2);
- // This should always be true, assuming that it takes < 1 sec for the hardware to process a check-out/check-in
- // Time for test is then ~ #threads / _nRooms * 2 seconds
- verify( _hotel._maxRooms == _hotel._nRooms );
+ _hotel.checkOut();
+ if ((i % (checkIns / 10)) == 0)
+ mongo::unittest::log() << "checked in " << i << " times..." << endl;
}
+ }
- };
+ virtual void validate() {
+ // This should always be true, assuming that it takes < 1 sec for the hardware to process a check-out/check-in
+ // Time for test is then ~ #threads / _nRooms * 2 seconds
+ verify(_hotel._maxRooms == _hotel._nRooms);
+ }
+};
- class All : public Suite {
- public:
- All() : Suite( "threading" ) { }
+class All : public Suite {
+public:
+ All() : Suite("threading") {}
- void setupTests() {
- add< WriteLocksAreGreedy >();
+ void setupTests() {
+ add<WriteLocksAreGreedy>();
- // Slack is a test to see how long it takes for another thread to pick up
- // and begin work after another relinquishes the lock. e.g. a spin lock
- // would have very little slack.
- add< Slack<SimpleMutex,stdx::lock_guard<SimpleMutex>> >();
- add< Slack<SimpleRWLock,SimpleRWLock::Exclusive> >();
- add< CondSlack >();
+ // Slack is a test to see how long it takes for another thread to pick up
+ // and begin work after another relinquishes the lock. e.g. a spin lock
+ // would have very little slack.
+ add<Slack<SimpleMutex, stdx::lock_guard<SimpleMutex>>>();
+ add<Slack<SimpleRWLock, SimpleRWLock::Exclusive>>();
+ add<CondSlack>();
- add< UpgradableTest >();
+ add<UpgradableTest>();
- add< IsAtomicWordAtomic<AtomicUInt32> >();
- add< IsAtomicWordAtomic<AtomicUInt64> >();
- add< MVarTest >();
- add< ThreadPoolTest >();
+ add<IsAtomicWordAtomic<AtomicUInt32>>();
+ add<IsAtomicWordAtomic<AtomicUInt64>>();
+ add<MVarTest>();
+ add<ThreadPoolTest>();
- add< RWLockTest1 >();
- add< RWLockTest2 >();
- add< RWLockTest3 >();
- add< RWLockTest4 >();
+ add<RWLockTest1>();
+ add<RWLockTest2>();
+ add<RWLockTest3>();
+ add<RWLockTest4>();
- add< MongoMutexTest >();
- add< TicketHolderWaits >();
- }
- };
+ add<MongoMutexTest>();
+ add<TicketHolderWaits>();
+ }
+};
- SuiteInstance<All> myall;
+SuiteInstance<All> myall;
}