// threadedtests.cpp - Tests for threaded code // /** * Copyright (C) 2008 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "pch.h" #include "../bson/util/atomic_int.h" #include "../util/concurrency/mvar.h" #include "../util/concurrency/thread_pool.h" #include "../util/timer.h" #include #include #include "dbtests.h" namespace ThreadedTests { template class ThreadedTest { public: virtual void setup() {} //optional virtual void subthread() = 0; virtual void validate() = 0; static const int nthreads = nthreads_param; void run() { setup(); launch_subthreads(nthreads); validate(); } virtual ~ThreadedTest() {}; // not necessary, but makes compilers happy private: void launch_subthreads(int remaining) { if (!remaining) return; boost::thread athread(boost::bind(&ThreadedTest::subthread, this)); launch_subthreads(remaining - 1); athread.join(); } }; class MongoMutexTest : public ThreadedTest<135> { #if defined(_DEBUG) enum { N = 5000 }; #else enum { N = 40000 }; #endif MongoMutex *mm; public: void run() { Timer t; cout << "MongoMutexTest N:" << N << endl; ThreadedTest<135>::run(); cout << "MongoMutexTest " << t.millis() << "ms" << endl; } private: virtual void setup() { mm = new MongoMutex("MongoMutexTest"); } virtual void subthread() { Client::initThread("mongomutextest"); sleepmillis(0); for( int i = 0; i < N; i++ ) { if( i % 7 == 0 ) { mm->lock_shared(); mm->lock_shared(); mm->unlock_shared(); mm->unlock_shared(); } else if( i % 7 == 1 ) { mm->lock_shared(); ASSERT( mm->atLeastReadLocked() ); mm->unlock_shared(); } else if( i % 7 == 2 ) { mm->lock(); ASSERT( mm->isWriteLocked() ); mm->unlock(); } else if( i % 7 == 3 ) { mm->lock(); mm->lock_shared(); ASSERT( mm->isWriteLocked() ); mm->unlock_shared(); mm->unlock(); } else if( i % 7 == 4 ) { mm->lock(); mm->releaseEarly(); mm->unlock(); } else if( i % 7 == 5 ) { if( mm->lock_try(1) ) { mm->unlock(); } } else if( i % 7 == 6 ) { if( mm->lock_shared_try(0) ) { mm->unlock_shared(); } } else { mm->lock_shared(); mm->unlock_shared(); } } cc().shutdown(); } virtual void validate() { ASSERT( !mm->atLeastReadLocked() ); mm->lock(); mm->unlock(); mm->lock_shared(); mm->unlock_shared(); } }; // Tested with up to 30k threads class IsAtomicUIntAtomic : public ThreadedTest<> { static const int iterations = 1000000; AtomicUInt target; void subthread() { for(int i=0; i < iterations; i++) { //target.x++; // verified to fail with this version target++; } } void validate() { ASSERT_EQUALS(target.x , unsigned(nthreads * iterations)); AtomicUInt u; ASSERT_EQUALS(0u, u); ASSERT_EQUALS(0u, u++); ASSERT_EQUALS(2u, ++u); ASSERT_EQUALS(2u, u--); ASSERT_EQUALS(0u, --u); ASSERT_EQUALS(0u, u); u++; ASSERT( u > 0 ); u--; ASSERT( ! ( u > 0 ) ); } }; class MVarTest : public ThreadedTest<> { static const int iterations = 10000; MVar target; public: MVarTest() : target(0) {} void subthread() { for(int i=0; i < iterations; i++) { int val = target.take(); #if BOOST_VERSION >= 103500 //increase chances of catching failure boost::this_thread::yield(); #endif target.put(val+1); } } void validate() { ASSERT_EQUALS(target.take() , nthreads * iterations); } }; class ThreadPoolTest { static const int iterations = 10000; static const int nThreads = 8; AtomicUInt counter; void increment(int n) { for (int i=0; i(); add< MVarTest >(); add< ThreadPoolTest >(); add< LockTest >(); add< MongoMutexTest >(); } } myall; }