int main(int argc, char *argv[]) { int test = argc > 1 ? atoi(argv[1]) : 0; int verbose = argc > 2; int veryVerbose = argc > 3; int veryVeryVerbose = argc > 4; cout << "TEST " << __FILE__ << " CASE " << test << endl; switch (test) { case 0: case 7: { // -------------------------------------------------------------------- // TESTING CONCURRENT SEMAPHORE CREATION // // Concerns: // 1. On Darwin the creation of the semaphore object is synchronized // because it's implemented via named semaphores. Concurrent // creation of multiple semaphores should not lead to invalid // semaphore objects or deadlock. // // Plan: // 1. In multiple threads, create a number of semaphore objects and // verify that they are valid. // -------------------------------------------------------------------- if (verbose) cout << "Testing concurrent creation\n" << "===========================\n"; vector<bslmt::ThreadUtil::Handle> threads(16); for (int i = 0; i != threads.size(); ++i) { int rc = bslmt::ThreadUtil::create(&threads[i], &createSemaphoresWorker, NULL); ASSERT(rc == 0); } for (int i = 0; i != threads.size(); ++i) { bslmt::ThreadUtil::join(threads[i]); } } break; case 6: { // -------------------------------------------------------------------- // TESTING MULTIPLE POST // // Concern: that 'post(n)' for large n works properly. Two test modes: // when threads are not waiting, and when they are concurrently // waiting. // -------------------------------------------------------------------- enum { k_NUM_POST = 1048704, k_NUM_WAIT_THREADS = 8 }; BSLMF_ASSERT(0 == k_NUM_POST % k_NUM_WAIT_THREADS); Obj sem(0); bslmt::ThreadUtil::Handle threads[k_NUM_WAIT_THREADS]; MyBarrier barrier(k_NUM_WAIT_THREADS+1); struct ThreadInfo4 info; info.d_numIterations = k_NUM_POST / k_NUM_WAIT_THREADS; info.d_barrier = &barrier; info.d_sem = &sem; for (int i = 0; i < k_NUM_WAIT_THREADS; ++i) { int rc = bslmt::ThreadUtil::create(&threads[i], thread6wait, &info); ASSERT(0 == rc); } if (verbose) { bsl::cout << "case 6 mode 1: concurrent waiting" << bsl::endl; } // MODE 1: THREADS WAITING barrier.wait(); bslmt::ThreadUtil::microSleep(10000); // 10 ms sem.post(k_NUM_POST); for (int i = 0; i < k_NUM_WAIT_THREADS; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } // if we reach here, we woke up all the threads the correct number of // times for (int i = 0; i < k_NUM_WAIT_THREADS; ++i) { int rc = bslmt::ThreadUtil::create(&threads[i], thread6wait, &info); ASSERT(0 == rc); } if (verbose) { bsl::cout << "case 6 mode 2: no concurrent waiting" << bsl::endl; } // MODE 2: NO THREADS WAITING sem.post(k_NUM_POST); barrier.wait(); for (int i = 0; i < k_NUM_WAIT_THREADS; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } // if we reach here, we woke up all the threads the correct number of // times } break; case 5: { ///Usage ///----- // This component is an implementation detail of 'bslmt' and is *not* intended // for direct client use. It is subject to change without notice. As such, a // usage example is not provided. // USAGE EXAMPLE IntQueue testQueue; testQueue.pushInt(1); ASSERT(1 == testQueue.getInt()); testQueue.pushInt(2); ASSERT(2 == testQueue.getInt()); } break; case 4: { // -------------------------------------------------------------------- // TESTING 'tryWait' // // Concerns: // 1. 'tryWait' decrements the count if resources are available, // or return an error otherwise. // // Plan: // We create two groups of threads. One will call 'post', the other // 'tryWait'. First, we make sure that 'tryWait' fails if no resources // is available. Then we will make sure it succeeds if resources are. // We will also test 'tryWait' in the steady state works fine. // // Testing: // void tryWait(); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'trywait'" << endl << "=================" << endl; bslmt::ThreadUtil::Handle threads[10]; MyBarrier barrier(10); Obj sem(0); struct ThreadInfo4 info; info.d_numIterations = 5000; // number of ops per thread / 3 info.d_barrier = &barrier; info.d_sem = &sem; for (int i = 0; i < 5; ++i) { ASSERT(0 == bslmt::ThreadUtil::create(&threads[i * 2], thread4Post, &info)); ASSERT(0 == bslmt::ThreadUtil::create(&threads[i * 2 + 1], thread4Wait, &info)); } for (int i = 0; i < 10; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } } break; case 3: { // -------------------------------------------------------------------- // TESTING 'post(int)' // // Concerns: // 1. post(int) increments the count by the expected number // // Plan: // Create a set of threads calling 'wait' and use a thread to post a // number smaller than the set of threads. // // Testing: // void post(int number); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'post(int number)'" << endl << "==========================" << endl; bslmt::ThreadUtil::Handle threads[6]; MyBarrier barrier(6); Obj sem(0); struct ThreadInfo3 info; info.d_numIterations = 10000; // number of ops per thread info.d_numWaitThreads = 5; info.d_barrier = &barrier; info.d_sem = &sem; for (int i = 0; i < 5; ++i) { ASSERT(0 == bslmt::ThreadUtil::create(&threads[i], thread3Wait, &info)); } ASSERT(0 == bslmt::ThreadUtil::create(&threads[5], thread3Post, &info)); for (int i = 0; i < 6; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } } break; case 2: { // -------------------------------------------------------------------- // TESTING 'wait' and 'post' // // Concerns: // 1. wait() blocks the thread when no resource is available, // and then decrements the count // 2. post() increments the count // // Plan: // Create two groups of threads: one will call 'post' and the other // will call 'wait'. To address concern 1, we will use a barrier and // a counter to make sure that waiting threads are blocked into wait // state before any calls to 'post'. After that, we will post a small // limited number of times (between 0 and 5), and check that the // semaphore can indeed satisfy that number of waiters. Then we try // to reach a steady state by calling the two functions 'post' and // 'wait' in a number of threads each, perturbing the 'post' operation // by adding small delays to exercise different parts of the code. // // Testing: // void post(); // void wait(int *signalInterrupted = 0); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'wait' and 'post'" << endl << "=========================" << endl; enum { k_NUM_POSTERS = 5, k_NUM_WAITERS = 5 }; for (int n = 0; n < 5; ++n) { if (veryVerbose) cout << "\tPosting " << n << " first." << endl; bslmt::ThreadUtil::Handle threads[k_NUM_POSTERS + k_NUM_WAITERS]; MyBarrier barrier(k_NUM_POSTERS+ 1); bsls::AtomicInt posts(n); bsls::AtomicInt past (0); Obj sem(0); struct ThreadInfo2 info; info.d_numIterations = 1000; // number of ops per thread info.d_barrier = &barrier; info.d_sem = &sem; info.d_past = &past; info.d_numInitialPosts = &posts; info.d_verbose = veryVeryVerbose; for (int i = 0; i < k_NUM_POSTERS; ++i) { ASSERT(0 == bslmt::ThreadUtil::create(&threads[i], thread2Post, &info)); } for (int i = 0; i < k_NUM_WAITERS; ++i) { ASSERT(0 == bslmt::ThreadUtil::create( &threads[i + k_NUM_POSTERS], thread2Wait, &info)); } bslmt::ThreadUtil::microSleep(1000 * 100); ASSERT(0 == past); ASSERT(0 == past); barrier.wait(); // Wait until the initial posters complete. if (veryVerbose) cout << "\t\tFirst barrier passed." << endl; while (n != past) { // Wait for some waiters to grab the posts. if (veryVeryVerbose) MTCOUT << "\t\tWaiters still blocking, " << posts << "." << MTENDL; bslmt::ThreadUtil::microSleep(1000 * 100); } barrier.wait(); // Unleash the remaining posters. if (veryVerbose) cout << "\t\tSecond barrier passed." << endl; // The testing will complete once all the threads join, meaning // that all the waiters were satisfied. for (int i = 0; i < 10; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } } } break; case 1: { // -------------------------------------------------------------------- // BREATHING TEST: // // Exercises basic functionality. // -------------------------------------------------------------------- if (verbose) { cout << endl << "Breathing Test" << endl << "==============" << endl; #if defined(BSLS_PLATFORM_OS_AIX) || defined(BSLS_PLATFORM_OS_LINUX) cout << "INFO: SEM_VALUE_MAX=" << SEM_VALUE_MAX << endl; #endif } { Obj X(0); X.post(); X.post(2); X.wait(); X.wait(); ASSERT(0 == X.tryWait()); ASSERT(0 != X.tryWait()); } } break; case -2: { // -------------------------------------------------------------------- // A SIMPLE BENCHMARK // // imitates a producer-consumer system with a fixed size queue using // two semaphores // int numProducers = atoi(argv[2]); int numConsumers = atoi(argv[3]); int queueSize = atoi(argv[4]); int seconds = 5; int samples = 5; if (verbose) cout << endl << "Benchmarking....." << endl << "=================" << endl << "producers=" << numProducers << endl << "consumers=" << numConsumers << endl << "queue size=" << queueSize << endl; BenchData* producerData = new BenchData[numProducers]; BenchData* consumerData = new BenchData[numConsumers]; Obj resource(0); Obj queue(0); queue.post(queueSize); for(int i=0; i<numConsumers; i++) { consumerData[i].resource = &resource; consumerData[i].queue = &queue; consumerData[i].count = 0; consumerData[i].stop = false; bslmt::ThreadUtil::create(&consumerData[i].handle, benchConsumer, (void*)(consumerData+i)); } for(int i=0; i<numProducers; i++) { producerData[i].resource = &resource; producerData[i].queue = &queue; producerData[i].stop = false; bslmt::ThreadUtil::create(&producerData[i].handle, benchProducer, (void*)(producerData+i)); } for(int j=0; j<samples; j++) { bsls::Types::Int64 timeStart = bsls::TimeUtil::getTimer(); bsls::Types::Int64 timeStartCPU = ::clock(); int* consumerCount = new int[numConsumers]; for(int i=0; i<numConsumers; i++) { consumerCount[i] = consumerData[i].count; } bsls::Types::Int64 throughput; bsls::Types::Int64 throughputCPU; for(int i=0; i<seconds; i++) { bslmt::ThreadUtil::microSleep(1000000); bsls::Types::Int64 totalMessages = 0; for(int i=0; i<numConsumers;i++) { totalMessages += (consumerData[i].count-consumerCount[i]); } bsls::Types::Int64 elapsed_us = (bsls::TimeUtil::getTimer()-timeStart)/1000; bsls::Types::Int64 elapsed_usCPU = ::clock()-timeStartCPU; throughput = (totalMessages*1000000/elapsed_us); throughputCPU = (totalMessages*1000000/elapsed_usCPU); cout << "testing: " << elapsed_us/1000 << " ms, " << elapsed_usCPU*100/elapsed_us << " CPU%, " << totalMessages << " msg, " << fmt(throughput) << " msg/s, " << fmt(throughputCPU) << " msg/CPUs" << endl; } cout << "====== final:" << fmt(throughput) << " msg/s, " << fmt(throughputCPU) << " msg/CPUs\n" << endl; } cout << "stopping: " << flush; for(int i=0; i<numProducers; i++) { producerData[i].stop = true; } for(int i=0; i<numProducers; i++) { bslmt::ThreadUtil::join(producerData[i].handle); cout << 'p' << flush; } for(int i=0; i<numConsumers; i++) { consumerData[i].stop = true; } resource.post(numConsumers); for(int i=0; i<numConsumers;i++) { bslmt::ThreadUtil::join(consumerData[i].handle); cout << 'c' << flush; } cout << endl; delete[] producerData; delete[] consumerData; } break; default: { testStatus = -1; break; } } return testStatus; }
int main(int argc, char *argv[]) { int test = argc > 1 ? atoi(argv[1]) : 0; int verbose = argc > 2; cout << "TEST " << __FILE__ << " CASE " << test << endl; switch (test) { case 0: // Zero is always the leading case. case 6: { ///Usage ///----- // This component is an implementation detail of 'bslmt' and is *not* intended // for direct client use. It is subject to change without notice. As such, a // usage example is not provided. // USAGE EXAMPLE IntQueue testQueue; testQueue.pushInt(1); ASSERT(1 == testQueue.getInt()); testQueue.pushInt(2); ASSERT(2 == testQueue.getInt()); } break; case 5: { // -------------------------------------------------------------------- // TESTING 'tryWait' // // Concerns: // 1. 'tryWait' decrements the count if resources are available, // or return an error otherwise. // // Plan: // We create two groups of threads. One will call 'post', the other // 'tryWait'. First, we make sure that 'tryWait' fails if no // resources are available. Then we will make sure it succeeds if // resources are. We will also test 'tryWait' in the steady state // works fine. // // Testing: // void tryWait(); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'trywait'" << endl << "=================" << endl; bslmt::ThreadUtil::Handle threads[10]; MyBarrier barrier(10); Obj sem; struct ThreadInfo5 info; info.d_numIterations = 5000; // number of ops per thread / 3 info.d_barrier = &barrier; info.d_sem = &sem; for (int i = 0; i < 5; ++i) { ASSERT(0 == bslmt::ThreadUtil::create(&threads[i * 2], thread5Post, &info)); ASSERT(0 == bslmt::ThreadUtil::create(&threads[i * 2 + 1], thread5Wait, &info)); } for (int i = 0; i < 10; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } } break; case 4: { // -------------------------------------------------------------------- // TESTING 'post(int)' // // Concerns: // 1. post(int) increments the count by the expected number // // Plan: // Create a set of threads calling 'wait' and use a thread to post a // number smaller than the set of threads. // // Testing: // void post(int number); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'post(int number)'" << endl << "==========================" << endl; bslmt::ThreadUtil::Handle threads[6]; MyBarrier barrier(6); Obj sem; struct ThreadInfo4 info; info.d_numIterations = 10000; // number of ops per thread info.d_numWaitThreads = 5; info.d_barrier = &barrier; info.d_sem = &sem; for (int i = 0; i < 5; ++i) { ASSERT(0 == bslmt::ThreadUtil::create(&threads[i], thread4Wait, &info)); } ASSERT(0 == bslmt::ThreadUtil::create(&threads[5], thread4Post, &info)); for (int i = 0; i < 6; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } } break; case 3: { // -------------------------------------------------------------------- // TESTING 'timedWait' // // Concerns: // 1. timedWait() blocks the thread until a resource is available // or the timeout expires. // // Plan: // Create two groups of threads one will call 'post' and the other // will call 'timedWait'. First, we will make sure that the // 'timedWait' will timeout properly if no resource available by // calling the function with a reasonable timeout before any calls to // 'post'. Then, both groups of threads will enter a loop to simulate // the steady state. The 'post' loop will be perturbed to exercise // different portions of code. The specified timeout will be pretty // important and we will make sure we do not timeout. At the end // of this first run, we will make a second run with a much lower // timeout which will force *some* waits to timeout. // // Testing: // void timedWait(bsls::TimeInterval timeout, // int *signalInterrupted = 0); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'timedWait'" << endl << "===================" << endl; testCase3(bsls::SystemClockType::e_REALTIME); testCase3(bsls::SystemClockType::e_MONOTONIC); } break; case 2: { // -------------------------------------------------------------------- // TESTING 'wait' and 'post' // // Concerns: // 1. wait() blocks the thread when no resource is available, // and then decrements the count // 2. post() increments the count // // Plan: // Create two groups of threads one will call 'post' and the other // will call 'wait'. To address concern 1, we will use a barrier and // a counter to make sure that waiting threads are blocked into wait // state before any calls to 'post'. After that, we will try to reach // a steady state by calling the two functions in a loop and perturb // the 'post' operation by adding small delays to exercise different // parts of the code. // // Testing: // void post(); // void wait(int *signalInterrupted = 0); // -------------------------------------------------------------------- if (verbose) cout << endl << "Testing 'wait' and 'post'" << endl << "=========================" << endl; bslmt::ThreadUtil::Handle threads[10]; MyBarrier barrier(6); bsls::AtomicInt past(0); Obj sem; struct ThreadInfo2 info; info.d_numIterations = 10000; // number of ops per thread info.d_barrier = &barrier; info.d_sem = &sem; info.d_past = &past; for (int i = 0; i < 5; ++i) { ASSERT(0 == bslmt::ThreadUtil::create(&threads[i * 2], thread2Post, &info)); ASSERT(0 == bslmt::ThreadUtil::create(&threads[i * 2 + 1], thread2Wait, &info)); } bslmt::ThreadUtil::microSleep(1000 * 100); ASSERT(0 == past); barrier.wait(); bslmt::ThreadUtil::microSleep(1000 * 200); ASSERT(0 != past); for (int i = 0; i < 10; ++i) { ASSERT(0 == bslmt::ThreadUtil::join(threads[i])); } } break; case 1: { // -------------------------------------------------------------------- // BREATHING TEST: // // Exercises basic functionality. // -------------------------------------------------------------------- if (verbose) cout << endl << "Breathing Test" << endl << "==============" << endl; { Obj X; X.post(); X.post(2); X.wait(); ASSERT(0 == X.timedWait(bsls::SystemTime::nowRealtimeClock() + bsls::TimeInterval(60))); ASSERT(0 == X.tryWait()); ASSERT(0 != X.tryWait()); ASSERT(0 != X.timedWait(bsls::SystemTime::nowRealtimeClock() + bsls::TimeInterval(1))); } } break; default: { cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; testStatus = -1; } } if (testStatus > 0) { cerr << "Error, non-zero test status = " << testStatus << "." << endl; } return testStatus; }