/* Copyright (c) 2010 Marcus Geelnard This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #include #include #include using namespace std; using namespace tthread; // HACK: Mac OS X and early MinGW do not support thread-local storage #if defined(__APPLE__) || (defined(__MINGW32__) && (__GNUC__ < 4)) #define NO_TLS #endif // Thread local storage variable #ifndef NO_TLS thread_local int gLocalVar; #endif // Mutex + global count variable mutex gMutex; fast_mutex gFastMutex; int gCount; // Condition variable condition_variable gCond; // Thread function: Thread ID void ThreadIDs(void * aArg) { cout << " My thread id is " << this_thread::get_id() << "." << endl; } #ifndef NO_TLS // Thread function: Thread-local storage void ThreadTLS(void * aArg) { gLocalVar = 2; cout << " My gLocalVar is " << gLocalVar << "." << endl; } #endif // Thread function: Mutex locking void ThreadLock(void * aArg) { for(int i = 0; i < 10000; ++ i) { lock_guard lock(gMutex); ++ gCount; } } // Thread function: Mutex locking void ThreadLock2(void * aArg) { for(int i = 0; i < 10000; ++ i) { lock_guard lock(gFastMutex); ++ gCount; } } // Thread function: Condition notifier void ThreadCondition1(void * aArg) { lock_guard lock(gMutex); -- gCount; gCond.notify_all(); } // Thread function: Condition waiter void ThreadCondition2(void * aArg) { cout << " Wating..." << flush; lock_guard lock(gMutex); while(gCount > 0) { cout << "." << flush; gCond.wait(gMutex); } cout << "." << endl; } // Thread function: Yield void ThreadYield(void * aArg) { // Yield... this_thread::yield(); } // This is the main program (i.e. the main thread) int main() { // Test 1: Show number of CPU cores in the system cout << "PART I: Info" << endl; cout << " Number of processor cores: " << thread::hardware_concurrency() << endl; // Test 2: thread IDs cout << endl << "PART II: Thread IDs" << endl; { // Show the main thread ID cout << " Main thread id is " << this_thread::get_id() << "." << endl; // Start a bunch of child threads - only run a single thread at a time thread t1(ThreadIDs, 0); t1.join(); thread t2(ThreadIDs, 0); t2.join(); thread t3(ThreadIDs, 0); t3.join(); thread t4(ThreadIDs, 0); t4.join(); } // Test 3: thread local storage cout << endl << "PART III: Thread local storage" << endl; #ifndef NO_TLS { // Clear the TLS variable (it should keep this value after all threads are // finished). gLocalVar = 1; cout << " Main gLocalVar is " << gLocalVar << "." << endl; // Start a child thread that modifies gLocalVar thread t1(ThreadTLS, 0); t1.join(); // Check if the TLS variable has changed if(gLocalVar == 1) cout << " Main gLocalID was not changed by the child thread - OK!" << endl; else cout << " Main gLocalID was changed by the child thread - FAIL!" << endl; } #else cout << " TLS is not supported on this platform..." << endl; #endif // Test 4: mutex locking cout << endl << "PART IV: Mutex locking (100 threads x 10000 iterations)" << endl; { // Clear the global counter. gCount = 0; // Start a bunch of child threads list threadList; for(int i = 0; i < 100; ++ i) threadList.push_back(new thread(ThreadLock, 0)); // Wait for the threads to finish list::iterator it; for(it = threadList.begin(); it != threadList.end(); ++ it) { thread * t = *it; t->join(); delete t; } // Check the global count cout << " gCount = " << gCount << endl; } // Test 5: fast_mutex locking cout << endl << "PART V: Fast mutex locking (100 threads x 10000 iterations)" << endl; { // Clear the global counter. gCount = 0; // Start a bunch of child threads list threadList; for(int i = 0; i < 100; ++ i) threadList.push_back(new thread(ThreadLock2, 0)); // Wait for the threads to finish list::iterator it; for(it = threadList.begin(); it != threadList.end(); ++ it) { thread * t = *it; t->join(); delete t; } // Check the global count cout << " gCount = " << gCount << endl; } // Test 6: condition variable cout << endl << "PART VI: Condition variable (40 + 1 threads)" << endl; { // Set the global counter to the number of threads to run. gCount = 40; // Start the waiting thread (it will wait for gCount to reach zero). thread t1(ThreadCondition2, 0); // Start a bunch of child threads (these will decrease gCount by 1 when they // finish) list threadList; for(int i = 0; i < 40; ++ i) threadList.push_back(new thread(ThreadCondition1, 0)); // Wait for the waiting thread to finish t1.join(); // Wait for the other threads to finish list::iterator it; for(it = threadList.begin(); it != threadList.end(); ++ it) { thread * t = *it; t->join(); delete t; } } // Test 7: yield cout << endl << "PART VII: Yield (40 + 1 threads)" << endl; { // Start a bunch of child threads list threadList; for(int i = 0; i < 40; ++ i) threadList.push_back(new thread(ThreadYield, 0)); // Yield... this_thread::yield(); // Wait for the threads to finish list::iterator it; for(it = threadList.begin(); it != threadList.end(); ++ it) { thread * t = *it; t->join(); delete t; } } // Test 8: sleep cout << endl << "PART VIII: Sleep (10 x 100 ms)" << endl; { // Sleep... cout << " Sleeping" << flush; for(int i = 0; i < 10; ++ i) { this_thread::sleep_for(chrono::milliseconds(100)); cout << "." << flush; } cout << endl; } }