interprocess/test/intermodule_singleton_test.cpp

331 lines
9.3 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// (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 <boost/interprocess/detail/config_begin.hpp>
#include <boost/interprocess/detail/intermodule_singleton.hpp>
#include <boost/interprocess/detail/portable_intermodule_singleton.hpp>
#include <iostream>
#include <cstdlib> //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 T, bool LazyInit, bool Phoenix> class IntermoduleType >
int intermodule_singleton_test()
{
bool exception_thrown = false;
bool exception_2_thrown = false;
try{
IntermoduleType<MyThrowingClass, true, false>::get();
}
catch(int &){
exception_thrown = true;
//Second try
try{
IntermoduleType<MyThrowingClass, true, false>::get();
}
catch(interprocess_exception &){
exception_2_thrown = true;
}
}
if(!exception_thrown || !exception_2_thrown){
return 1;
}
MyClass & mc = IntermoduleType<MyClass, true, false>::get();
mc.shout();
IntermoduleType<MyClass, true, false>::get().shout();
IntermoduleType<MyDerivedClass, true, false>::get().shout();
//Second try
exception_2_thrown = false;
try{
IntermoduleType<MyThrowingClass, true, false>::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 Tag>
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<class Tag>
unsigned int Logger<Tag>::constructed_times;
template<class Tag>
unsigned int Logger<Tag>::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 LogSingleton>
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 Tag>
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<Tag>::constructed_times != Logger<Tag>::destroyed_times ||
Logger<Tag>::constructed_times != 2)
{
std::stringstream sstr;
sstr << "LogPhoenixTester failed for tag ";
sstr << typeid(Tag).name();
sstr << "\n";
if(Logger<Tag>::constructed_times != 2){
sstr << "Logger<Tag>::constructed_times != 2\n";
sstr << "(";
sstr << Logger<Tag>::constructed_times << ")\n";
}
else{
sstr << "Logger<Tag>::constructed_times != Logger<Tag>::destroyed_times\n";
sstr << "(" << Logger<Tag>::constructed_times << " vs. " << Logger<Tag>::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 LogSingleton>
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 T, bool LazyInit, bool Phoenix> class IntermoduleType >
int phoenix_singleton_test()
{
typedef int DummyType;
typedef IntermoduleType<DummyType, true, true> Tag;
typedef Logger<Tag> LoggerType;
typedef IntermoduleType<LoggerType, true, true> LoggerSingleton;
typedef LogUser<LoggerSingleton> LogUserType;
typedef IntermoduleType<LogUserType, true, true> LogUserSingleton;
typedef IntermoduleType<LogPhoenixTester<Tag>, 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 T, bool LazyInit, bool Phoenix> class IntermoduleType >
int dead_reference_singleton_test()
{
typedef int DummyType;
typedef IntermoduleType<DummyType, true, false> Tag;
typedef Logger<Tag> LoggerType;
typedef IntermoduleType<LoggerType, true, false> LoggerSingleton;
typedef LogDeadReferenceUser<LoggerSingleton> LogDeadReferenceUserType;
typedef IntermoduleType<LogDeadReferenceUserType, true, false> 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<typename C, bool LazyInit, bool Phoenix>
class port_singleton
: public ipcdetail::portable_intermodule_singleton<C, LazyInit, Phoenix>
{};
#ifdef BOOST_INTERPROCESS_WINDOWS
template<typename C, bool LazyInit, bool Phoenix>
class win_singleton
: public ipcdetail::windows_intermodule_singleton< C, LazyInit, Phoenix>
{};
#endif
int main ()
{
if(0 != intermodule_singleton_test<port_singleton>()){
return 1;
}
#ifdef BOOST_INTERPROCESS_WINDOWS
if(0 != intermodule_singleton_test<win_singleton>()){
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<port_singleton>();
#ifdef BOOST_INTERPROCESS_WINDOWS
phoenix_singleton_test<win_singleton>();
#endif
#endif
//Dead reference singletons are tested after main ends,
//LogDeadReferenceUser does the work
dead_reference_singleton_test<port_singleton>();
#ifdef BOOST_INTERPROCESS_WINDOWS
dead_reference_singleton_test<win_singleton>();
#endif
return 0;
}
#include <boost/interprocess/detail/config_end.hpp>