331 lines
9.3 KiB
C++
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>
|