////////////////////////////////////////////////////////////////////////////// // // (C) Copyright Ion Gaztanaga 2004-2012. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/interprocess for documentation. // ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include //for std::abort using namespace boost::interprocess; class MyClass { public: MyClass() { std::cout << "MyClass()\n" << std::endl; } void shout() const { std::cout << "Shout\n" << std::endl; } ~MyClass() { std::cout << "~MyClass()\n" << std::endl; } }; class MyDerivedClass : public MyClass {}; class MyThrowingClass { public: MyThrowingClass() { throw int(0); } }; template < template class IntermoduleType > int intermodule_singleton_test() { bool exception_thrown = false; bool exception_2_thrown = false; try{ IntermoduleType::get(); } catch(int &){ exception_thrown = true; //Second try try{ IntermoduleType::get(); } catch(interprocess_exception &){ exception_2_thrown = true; } } if(!exception_thrown || !exception_2_thrown){ return 1; } MyClass & mc = IntermoduleType::get(); mc.shout(); IntermoduleType::get().shout(); IntermoduleType::get().shout(); //Second try exception_2_thrown = false; try{ IntermoduleType::get(); } catch(interprocess_exception &){ exception_2_thrown = true; } if(!exception_2_thrown){ return 1; } return 0; } //A class simulating a logger //We'll register constructor/destructor counts //to test the singleton was correctly resurrected //by LogUser singleton. template class Logger { public: Logger() { ++constructed_times; std::cout << "Logger(),tag:" << typeid(Tag).name() << "(construct #" << constructed_times << ")\n" << std::endl; } void log_it() {} ~Logger() { ++destroyed_times; std::cout << "~Logger(),tag:" << typeid(Tag).name() << "(destroy #" << destroyed_times << ")\n" << std::endl; } static unsigned int constructed_times; static unsigned int destroyed_times; }; template unsigned int Logger::constructed_times; template unsigned int Logger::destroyed_times; //A class simulating a logger user. //The destructor uses the logger so that //the logger is resurrected if it was //already destroyed template class LogUser { public: LogUser() { std::cout << "LogUser(),tag:" << typeid(LogSingleton).name() << "\n" << std::endl; } void function_using_log() { LogSingleton::get().log_it(); } ~LogUser() { std::cout << "~LogUser(),tag:" << typeid(LogSingleton).name() << "\n" << std::endl; LogSingleton::get().log_it(); } }; //A class that tests the correct //phoenix singleton behaviour. //Logger should be resurrected by LogUser template class LogPhoenixTester { public: LogPhoenixTester() { std::cout << "LogPhoenixTester(), tag: " << typeid(Tag).name() << "\n" << std::endl; } void dummy() {} ~LogPhoenixTester() { //Test Phoenix singleton was correctly executed: //created and destroyed two times //This test will be executed after main ends std::cout << "~LogPhoenixTester(), tag: " << typeid(Tag).name() << "\n" << std::endl; if(Logger::constructed_times != Logger::destroyed_times || Logger::constructed_times != 2) { std::stringstream sstr; sstr << "LogPhoenixTester failed for tag "; sstr << typeid(Tag).name(); sstr << "\n"; if(Logger::constructed_times != 2){ sstr << "Logger::constructed_times != 2\n"; sstr << "("; sstr << Logger::constructed_times << ")\n"; } else{ sstr << "Logger::constructed_times != Logger::destroyed_times\n"; sstr << "(" << Logger::constructed_times << " vs. " << Logger::destroyed_times << ")\n"; } std::cout << "~LogPhoenixTester(), error: " << sstr.str() << std::endl; std::abort(); } } }; //A class simulating a logger user. //The destructor uses the logger so that //the logger is resurrected if it was //already destroyed template class LogDeadReferenceUser { public: LogDeadReferenceUser() { std::cout << "LogDeadReferenceUser(), LogSingleton: " << typeid(LogSingleton).name() << "\n" << std::endl; } void function_using_log() { LogSingleton::get().log_it(); } ~LogDeadReferenceUser() { std::cout << "~LogDeadReferenceUser(), LogSingleton: " << typeid(LogSingleton).name() << "\n" << std::endl; //Make sure the exception is thrown as we are //trying to use a dead non-phoenix singleton try{ LogSingleton::get().log_it(); std::string s("LogDeadReferenceUser failed for LogSingleton "); s += typeid(LogSingleton).name(); std::cout << "~LogDeadReferenceUser(), error: " << s << std::endl; std::abort(); } catch(interprocess_exception &){ //Correct behaviour } } }; template < template class IntermoduleType > int phoenix_singleton_test() { typedef int DummyType; typedef IntermoduleType Tag; typedef Logger LoggerType; typedef IntermoduleType LoggerSingleton; typedef LogUser LogUserType; typedef IntermoduleType LogUserSingleton; typedef IntermoduleType, true, true> LogPhoenixTesterSingleton; //Instantiate Phoenix tester singleton so that it will be destroyed the last LogPhoenixTesterSingleton::get().dummy(); //Now instantitate a log user singleton LogUserType &log_user = LogUserSingleton::get(); //Then force LoggerSingleton instantiation //calling a function that will use it. //After main ends, LoggerSingleton will be destroyed //before LogUserSingleton due to LIFO //singleton semantics log_user.function_using_log(); //Next, LogUserSingleton destructor will resurrect //LoggerSingleton. //After that LoggerSingleton will be destroyed and //lastly LogPhoenixTester will be destroyed checking //LoggerSingleton was correctly destroyed. return 0; } template < template class IntermoduleType > int dead_reference_singleton_test() { typedef int DummyType; typedef IntermoduleType Tag; typedef Logger LoggerType; typedef IntermoduleType LoggerSingleton; typedef LogDeadReferenceUser LogDeadReferenceUserType; typedef IntermoduleType LogDeadReferenceUserSingleton; //Now instantitate a log user singleton LogDeadReferenceUserType &log_user = LogDeadReferenceUserSingleton::get(); //Then force LoggerSingleton instantiation //calling a function that will use it. //After main ends, LoggerSingleton will be destroyed //before LogDeadReferenceUserType due to LIFO //singleton semantics log_user.function_using_log(); //Next, LogDeadReferenceUserType destructor will try to use //LoggerSingleton and an exception will be raised an catched. return 0; } //reduce name length template class port_singleton : public ipcdetail::portable_intermodule_singleton {}; #ifdef BOOST_INTERPROCESS_WINDOWS template class win_singleton : public ipcdetail::windows_intermodule_singleton< C, LazyInit, Phoenix> {}; #endif int main () { if(0 != intermodule_singleton_test()){ return 1; } #ifdef BOOST_INTERPROCESS_WINDOWS if(0 != intermodule_singleton_test()){ return 1; } #endif //Only few platforms support this #ifdef BOOST_INTERPROCESS_ATEXIT_CALLABLE_FROM_ATEXIT //Phoenix singletons are tested after main ends, //LogPhoenixTester does the work phoenix_singleton_test(); #ifdef BOOST_INTERPROCESS_WINDOWS phoenix_singleton_test(); #endif #endif //Dead reference singletons are tested after main ends, //LogDeadReferenceUser does the work dead_reference_singleton_test(); #ifdef BOOST_INTERPROCESS_WINDOWS dead_reference_singleton_test(); #endif return 0; } #include