4 * Copyright (c) 2003, 2004
7 * This material is provided "as is", with absolutely no warranty expressed
8 * or implied. Any use is at your own risk.
10 * Permission to use or copy this software for any purpose is hereby granted
11 * without fee, provided the above notices are retained on all copies.
12 * Permission to modify the code and to distribute modified code is granted,
13 * provided the above notices are retained, and a notice that the code was
14 * modified is included with the above copyright notice.
19 #if defined(_DEBUG) && _MSC_VER == 1500
20 # define _CRTDBG_MAP_ALLOC
26 #include "cppunit/cppunit_proxy.h"
27 #include "cppunit/file_reporter.h"
30 #include <cds/gc/hp.h>
31 #include <cds/gc/dhp.h>
32 #include <cds/urcu/general_instant.h>
33 #include <cds/urcu/general_buffered.h>
34 #include <cds/urcu/general_threaded.h>
35 #include <cds/urcu/signal_buffered.h>
36 #include <cds/urcu/signal_threaded.h>
37 #include <cds/os/topology.h>
43 #include <boost/date_time/posix_time/posix_time.hpp>
44 #include <boost/thread/mutex.hpp>
46 // Visual leak detector (see http://vld.codeplex.com/)
47 #if defined(CDS_USE_VLD) && CDS_COMPILER == CDS_COMPILER_MSVC
54 std::ostream& operator << (std::ostream& s, const cds::gc::hp::GarbageCollector::InternalState& stat)
56 s << "\nHZP GC internal state:"
57 << "\n\t HP record allocated=" << stat.nHPRecAllocated
58 << "\n\t HP records used=" << stat.nHPRecUsed
59 << "\n\t Total retired ptr count=" << stat.nTotalRetiredPtrCount
60 << "\n\t Retired ptr in free HP records=" << stat.nRetiredPtrInFreeHPRecs
62 << "\n\t HPRec allocations=" << stat.evcAllocHPRec
63 << "\n\t HPRec retire events=" << stat.evcRetireHPRec
64 << "\n\tnew HPRec allocations from heap=" << stat.evcAllocNewHPRec
65 << "\n\t HPRec deletions=" << stat.evcDeleteHPRec
66 << "\n\t Scan call count=" << stat.evcScanCall
67 << "\n\t HelpScan call count=" << stat.evcHelpScanCall
68 << "\n\t Scan calls from HelpScan=" << stat.evcScanFromHelpScan
69 << "\n\t retired object deleting=" << stat.evcDeletedNode
70 << "\n\t guarded object on Scan=" << stat.evcDeferredNode
78 int TestCase::m_numErrors = 0;
79 int TestCase::m_numTests = 0;
80 std::vector<std::string> TestCase::m_arrStrings;
81 bool TestCase::m_bPrintGCState = false;
82 std::string TestCase::m_strTestDataDir(".");
83 Config TestCase::m_Cfg;
84 bool TestCase::m_bExactMatch = false;
86 // random shuffle support
87 /*static*/ std::random_device TestCase::m_RandomDevice;
88 /*static*/ std::mt19937 TestCase::m_RandomGen( TestCase::m_RandomDevice() );
90 TestCase * TestCase::m_pCurTestCase = nullptr;
92 TestCase *TestCase::m_root = 0;
93 Reporter *TestCase::m_reporter = 0;
95 void TestCase::registerTestCase(TestCase *in_testCase) {
96 in_testCase->m_next = m_root;
100 int TestCase::run(Reporter *in_reporter, const char *in_testName, bool invert)
102 TestCase::m_reporter = in_reporter;
107 TestCase *tmp = m_root;
109 m_pCurTestCase = tmp;
111 tmp->myRun(in_testName, invert);
112 } catch ( std::exception& ex ) {
113 in_reporter->message( "EXCEPTION: ");
114 in_reporter->message( ex.what() );
122 bool TestCase::shouldRunThis(const char *in_desiredTest, const char *in_className, const char *in_functionName,
123 bool invert, bool explicit_test, bool &do_progress)
125 if ((in_desiredTest) && (in_desiredTest[0] != '\0')) {
127 const char *ptr = strstr(in_desiredTest, "::");
130 if ( m_bExactMatch ) {
131 match = (strncmp( in_desiredTest, in_className, strlen( in_className )) == 0 && in_desiredTest[strlen( in_className )] == ':')
132 && (strcmp( ptr + 2, in_functionName ) == 0);
135 match = (strncmp( in_desiredTest, in_className, strlen( in_className )) == 0 && in_desiredTest[strlen( in_className )] == ':')
136 && (strncmp( ptr + 2, in_functionName, strlen( ptr + 2 ) ) == 0);
138 // Invert shall not make explicit test run:
139 return invert ? (match ? !match : !explicit_test)
142 bool match = (strcmp(in_desiredTest, in_className) == 0);
144 return !explicit_test && (match == !invert);
148 return !explicit_test;
152 void TestCase::print_gc_state()
154 if ( m_bPrintGCState ) {
156 cds::gc::hp::GarbageCollector::InternalState stat;
157 std::cout << cds::gc::hp::GarbageCollector::instance().getInternalState( stat ) << std::endl;
162 void TestCase::print_test_list()
164 TestCase *tmp = m_root;
165 std::cout << "Test list:\n";
167 std::cout << "\t" << tmp->test_name() << "\n";
172 void Config::load( const char * fileName )
176 if ( !s.is_open() ) {
177 std::cerr << "WARNING: Cannot open test cfg file " << fileName
178 << "\n\tUse default settings"
183 std::cout << "Using test config file: " << fileName << std::endl;
187 TestCfg * pMap = nullptr;
189 s.getline( buf, sizeof(buf)/sizeof(buf[0]) );
192 while ( *pszStr != 0 && (*pszStr == ' ' || *pszStr == '\t' )) ++pszStr;
194 char * pszEnd = strchr( pszStr, 0 );
195 if ( pszEnd == pszStr ) // empty srtring
198 while ( pszEnd != pszStr && (*pszEnd ==' ' || *pszEnd=='\t' || *pszEnd=='\n' || *pszEnd=='\r' )) --pszEnd;
200 if ( pszStr == pszEnd ) // empty string
205 if ( *pszStr == '#' ) // comment
208 if ( *pszStr == '[' && *pszEnd == ']' ) { // chapter header
210 pMap = &( m_Cfg[ pszStr + 1 ] );
217 char * pszEq = strchr( pszStr, '=' );
220 if ( pszEq == pszStr )
224 while ( pszStr <= --pszEnd && (*pszEnd ==' ' || *pszEnd=='\t' || *pszEnd=='\n' || *pszEnd=='\r') );
226 if ( pszEnd <= pszStr )
229 pMap->m_Cfg[ pszStr ] = pszEq + 1;
234 std::vector<std::string> const & TestCase::getTestStrings()
236 if ( m_arrStrings.empty() ) {
237 std::string strTestDir = m_strTestDataDir;
241 std::cout << "Loading test data " << strTestDir << "/dictionary.txt..." << std::endl;
242 fDict.open( (strTestDir + "/dictionary.txt").c_str() );
243 if ( fDict.is_open() ) {
244 cds::OS::Timer timer;
246 fDict >> str ; // number of lines in file
248 // Assume that dictionary.txt does not contain doubles.
249 CppUnitMini::TestCase::m_arrStrings.reserve( atol(str.c_str()) );
250 while ( !fDict.eof() ) {
251 fDict.getline( bufLine, sizeof(bufLine)/sizeof(bufLine[0]) );
253 m_arrStrings.push_back( bufLine );
257 std::cout << " Duration=" << timer.duration() << " String count " << CppUnitMini::TestCase::m_arrStrings.size() << std::endl;
260 std::cout << " Failed, file not found" << std::endl;
267 static void usage(const char* name)
269 std::cout << "Usage: " << name << " [options] [-t=<class>[::<test>]] [-x=<class>[::<test>]] [-f=<file>]\n"
270 "\t[-t=<class>[::<test>]] : test class or class::test to execute\n"
271 "\t[-d=dir] : test data directory (default is .)\n"
272 "\t[-f=<file>] : output file\n"
274 "\t-exact-match - class::test should be exactly matched to existing test\n"
275 "\t-gc_state - print gc state after each test\n"
276 "\t-cfg=<file> - config file name for tests\n"
277 "\t-list - list all tests\n"
282 int main(int argc, char** argv)
285 #ifdef CDS_MSVC_MEMORY_LEAKS_DETECTING_ENABLED
286 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
289 // CppUnit(mini) test launcher
290 // command line option syntax:
293 // -t=CLASS[::TEST] run the test class CLASS or member test CLASS::TEST
294 // -x=CLASS[::TEST] run all except the test class CLASS or member test CLASS::TEST
295 // -d=dir test data directory (default is .)
296 // -f=FILE save output in file FILE instead of stdout
297 // -m monitor test(s) execution
298 // -gc_state print GC state after test
299 const char *fileName = 0;
300 const char *testName = "";
301 const char *xtestName = "";
302 const char *testDataDir = ".";
303 const char *cfgFileName =
310 bool doMonitoring = false;
312 for (int i = 1; i < argc; ++i) {
313 if (argv[i][0] == '-') {
314 if ( strncmp(argv[i], "-t=", 3) == 0 ) {
315 testName = argv[i]+3;
318 else if ( strncmp(argv[i], "-f=", 3) == 0 ) {
319 fileName = argv[i]+3;
322 else if ( strncmp(argv[i], "-x=", 3) == 0 ) {
323 xtestName = argv[i]+3;
326 else if ( strncmp(argv[i], "-d=", 3) == 0 ) {
327 testDataDir = argv[i] + 3;
330 else if ( strncmp( argv[i], "-m", 2 ) == 0 ) {
334 else if ( strncmp( argv[i], "-exact-match", 12 ) == 0 ) {
335 CppUnitMini::TestCase::m_bExactMatch = true;
338 else if (strncmp(argv[i], "-list", 5) == 0) {
339 CppUnitMini::TestCase::print_test_list();
342 else if ( strncmp(argv[i], "-gc_state", 9) == 0 ) {
343 CppUnitMini::TestCase::m_bPrintGCState = true;
346 else if( strncmp(argv[i], "-cfg=", 5) == 0 ) {
347 cfgFileName = argv[i] + 5;
352 // invalid option, we display normal usage.
359 boost::posix_time::ptime cur( boost::posix_time::second_clock::local_time());
361 std::cout << "libcds version " << CDS_VERSION_STRING << "\n";
362 std::cout << "Test started " << cur << std::endl;
366 CppUnitMini::TestCase::m_strTestDataDir = testDataDir;
368 CppUnitMini::Reporter* reporter;
370 reporter = new CppUnitMini::FileReporter(fileName, doMonitoring);
372 reporter = new CppUnitMini::FileReporter(stdout, doMonitoring);
374 // Load config params
375 CppUnitMini::TestCase::m_Cfg.load( cfgFileName );
382 size_t nHazardPtrCount = 0;
383 size_t dhp_liberateThreshold;
384 size_t dhp_initialThreadGuardCount;
385 size_t dhp_epochCount;
387 CppUnitMini::TestCfg& cfg = CppUnitMini::TestCase::m_Cfg.get( "General" );
388 nHazardPtrCount = cfg.getULong( "hazard_pointer_count", 0 );
390 dhp_liberateThreshold = cfg.getSizeT( "dhp_liberate_threshold", 1024 );
391 dhp_initialThreadGuardCount = cfg.getSizeT( "dhp_init_guard_count", 8 );
392 dhp_epochCount = cfg.getSizeT( "dhp_epoch_count", 16 );
395 // Safe reclamation schemes
396 cds::gc::HP hzpGC( nHazardPtrCount );
397 cds::gc::DHP dhpGC( dhp_liberateThreshold, dhp_initialThreadGuardCount, dhp_epochCount );
400 typedef cds::urcu::gc< cds::urcu::general_instant<> > rcu_gpi;
403 typedef cds::urcu::gc< cds::urcu::general_buffered<> > rcu_gpb;
406 typedef cds::urcu::gc< cds::urcu::general_threaded<> > rcu_gpt;
409 #ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
410 typedef cds::urcu::gc< cds::urcu::signal_buffered<> > rcu_shb;
411 rcu_shb shbRCU( 256, SIGUSR1 );
413 typedef cds::urcu::gc< cds::urcu::signal_threaded<> > rcu_sht;
414 rcu_sht shtRCU( 256, SIGUSR2 );
420 << "System topology:\n"
421 << " Logical processor count: " << std::thread::hardware_concurrency() << "\n";
422 std::cout << std::endl;
426 CppUnitMini::TestCfg& cfg = CppUnitMini::TestCase::m_Cfg.get( "General" );
427 std::string strHZPScanStrategy = cfg.get( "HZP_scan_strategy", std::string("classic") );
428 if ( strHZPScanStrategy == "inplace" )
429 hzpGC.setScanType( cds::gc::HP::scan_type::inplace );
430 else if ( strHZPScanStrategy == "classic" )
431 hzpGC.setScanType( cds::gc::HP::scan_type::classic );
433 std::cout << "Error value of HZP_scan_strategy in General section of test config\n";
436 switch (hzpGC.getScanType()) {
437 case cds::gc::HP::scan_type::inplace:
438 std::cout << "Use in-place scan strategy for Hazard Pointer memory reclamation algorithm\n";
440 case cds::gc::HP::scan_type::classic:
441 std::cout << "Use classic scan strategy for Hazard Pointer memory reclamation algorithm\n";
444 std::cout << "ERROR: use unknown scan strategy for Hazard Pointer memory reclamation algorithm\n";
448 std::cout << " Hazard Pointer count: " << hzpGC.max_hazard_count() << "\n"
449 << " Max thread count for HP: " << hzpGC.max_thread_count() << "\n"
450 << "Retired HP array capacity: " << hzpGC.retired_array_capacity() << "\n";
453 if ( CppUnitMini::TestCase::m_bPrintGCState ) {
454 cds::gc::hp::GarbageCollector::InternalState stat;
455 cds::gc::hp::GarbageCollector::instance().getInternalState( stat );
457 std::cout << "HP constants:"
458 << "\n\tHP count per thread=" << stat.nHPCount
459 << "\n\tMax thread count=" << stat.nMaxThreadCount
460 << "\n\tMax retired pointer count per thread=" << stat.nMaxRetiredPtrCount
461 << "\n\tHP record size in bytes=" << stat.nHPRecSize
462 << "\n" << std::endl;
465 // Attach main thread to CDS GC
466 cds::threading::Manager::attachThread();
468 if (xtestName[0] != 0)
469 num_errors = CppUnitMini::TestCase::run(reporter, xtestName, true);
471 num_errors = CppUnitMini::TestCase::run(reporter, testName);
473 // Detach main thread from CDS GC
474 cds::threading::Manager::detachThread();
478 // Finalize CDS runtime
481 reporter->printSummary();