• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2004-2012. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // See http://www.boost.org/libs/interprocess for documentation.
8 //
9 //////////////////////////////////////////////////////////////////////////////
10 #include <boost/interprocess/detail/intermodule_singleton.hpp>
11 #include <boost/interprocess/detail/portable_intermodule_singleton.hpp>
12 #include <iostream>
13 #include <cstdlib> //for std::abort
14 
15 using namespace boost::interprocess;
16 
17 class MyClass
18 {
19    public:
MyClass()20    MyClass()
21    {
22       std::cout << "MyClass()\n" << std::endl;
23    }
24 
shout() const25    void shout() const
26    {
27       std::cout << "Shout\n" << std::endl;
28    }
29 
~MyClass()30    ~MyClass()
31    {
32       std::cout << "~MyClass()\n" << std::endl;
33    }
34 };
35 
36 class MyDerivedClass
37    : public MyClass
38 {};
39 
40 class MyThrowingClass
41 {
42    public:
MyThrowingClass()43    MyThrowingClass()
44    {
45       throw int(0);
46    }
47 };
48 
49 
50 template < template<class T, bool LazyInit, bool Phoenix> class IntermoduleType >
intermodule_singleton_test()51 int intermodule_singleton_test()
52 {
53    bool exception_thrown = false;
54    bool exception_2_thrown = false;
55 
56    try{
57       IntermoduleType<MyThrowingClass, true, false>::get();
58    }
59    catch(int &){
60       exception_thrown = true;
61       //Second try
62       try{
63          IntermoduleType<MyThrowingClass, true, false>::get();
64       }
65       catch(interprocess_exception &){
66          exception_2_thrown = true;
67       }
68    }
69 
70    if(!exception_thrown || !exception_2_thrown){
71       return 1;
72    }
73 
74    MyClass & mc = IntermoduleType<MyClass, true, false>::get();
75    mc.shout();
76    IntermoduleType<MyClass, true, false>::get().shout();
77    IntermoduleType<MyDerivedClass, true, false>::get().shout();
78 
79    //Second try
80    exception_2_thrown = false;
81    try{
82       IntermoduleType<MyThrowingClass, true, false>::get();
83    }
84    catch(interprocess_exception &){
85       exception_2_thrown = true;
86    }
87    if(!exception_2_thrown){
88       return 1;
89    }
90 
91    return 0;
92 }
93 
94 //A class simulating a logger
95 //We'll register constructor/destructor counts
96 //to test the singleton was correctly resurrected
97 //by LogUser singleton.
98 template<class Tag>
99 class Logger
100 {
101    public:
Logger()102    Logger()
103    {
104       ++constructed_times;
105       std::cout << "Logger(),tag:" << typeid(Tag).name() << "(construct #" << constructed_times << ")\n" << std::endl;
106    }
107 
log_it()108    void log_it()
109    {}
110 
~Logger()111    ~Logger()
112    {
113       ++destroyed_times;
114       std::cout << "~Logger(),tag:" << typeid(Tag).name() << "(destroy #" << destroyed_times << ")\n" << std::endl;
115    }
116 
117    static unsigned int constructed_times;
118    static unsigned int destroyed_times;
119 };
120 
121 template<class Tag>
122 unsigned int Logger<Tag>::constructed_times;
123 
124 template<class Tag>
125 unsigned int Logger<Tag>::destroyed_times;
126 
127 //A class simulating a logger user.
128 //The destructor uses the logger so that
129 //the logger is resurrected if it was
130 //already destroyed
131 template<class LogSingleton>
132 class LogUser
133 {
134    public:
LogUser()135    LogUser()
136    {
137       std::cout << "LogUser(),tag:" << typeid(LogSingleton).name() << "\n" << std::endl;
138    }
139 
function_using_log()140    void function_using_log()
141    {  LogSingleton::get().log_it();  }
142 
~LogUser()143    ~LogUser()
144    {
145       std::cout << "~LogUser(),tag:" << typeid(LogSingleton).name() << "\n" << std::endl;
146       LogSingleton::get().log_it();
147    }
148 };
149 
150 //A class that tests the correct
151 //phoenix singleton behaviour.
152 //Logger should be resurrected by LogUser
153 template<class Tag>
154 class LogPhoenixTester
155 {
156    public:
LogPhoenixTester()157    LogPhoenixTester()
158    {
159       std::cout << "LogPhoenixTester(), tag: " << typeid(Tag).name() << "\n" << std::endl;
160    }
161 
dummy()162    void dummy()
163    {}
164 
~LogPhoenixTester()165    ~LogPhoenixTester()
166    {
167       //Test Phoenix singleton was correctly executed:
168       //created and destroyed two times
169       //This test will be executed after main ends
170       std::cout << "~LogPhoenixTester(), tag: " << typeid(Tag).name() << "\n" << std::endl;
171       if(Logger<Tag>::constructed_times != Logger<Tag>::destroyed_times ||
172          Logger<Tag>::constructed_times != 2)
173       {
174          std::stringstream sstr;
175          sstr << "LogPhoenixTester failed for tag ";
176          sstr << typeid(Tag).name();
177          sstr << "\n";
178          if(Logger<Tag>::constructed_times != 2){
179             sstr << "Logger<Tag>::constructed_times != 2\n";
180             sstr << "(";
181             sstr << Logger<Tag>::constructed_times << ")\n";
182          }
183          else{
184             sstr << "Logger<Tag>::constructed_times != Logger<Tag>::destroyed_times\n";
185             sstr << "(" << Logger<Tag>::constructed_times << " vs. " << Logger<Tag>::destroyed_times << ")\n";
186          }
187          std::cout << "~LogPhoenixTester(), error: " << sstr.str() << std::endl;
188          std::abort();
189       }
190    }
191 };
192 
193 //A class simulating a logger user.
194 //The destructor uses the logger so that
195 //the logger is resurrected if it was
196 //already destroyed
197 template<class LogSingleton>
198 class LogDeadReferenceUser
199 {
200    public:
LogDeadReferenceUser()201    LogDeadReferenceUser()
202    {
203       std::cout << "LogDeadReferenceUser(), LogSingleton: " << typeid(LogSingleton).name() << "\n" << std::endl;
204    }
205 
function_using_log()206    void function_using_log()
207    {  LogSingleton::get().log_it();  }
208 
~LogDeadReferenceUser()209    ~LogDeadReferenceUser()
210    {
211       std::cout << "~LogDeadReferenceUser(), LogSingleton: " << typeid(LogSingleton).name() << "\n" << std::endl;
212       //Make sure the exception is thrown as we are
213       //trying to use a dead non-phoenix singleton
214       try{
215          LogSingleton::get().log_it();
216          std::string s("LogDeadReferenceUser failed for LogSingleton ");
217          s += typeid(LogSingleton).name();
218          std::cout << "~LogDeadReferenceUser(), error: " << s << std::endl;
219          std::abort();
220       }
221       catch(interprocess_exception &){
222          //Correct behaviour
223       }
224    }
225 };
226 
227 template < template<class T, bool LazyInit, bool Phoenix> class IntermoduleType >
phoenix_singleton_test()228 int phoenix_singleton_test()
229 {
230    typedef int DummyType;
231    typedef IntermoduleType<DummyType, true, true>              Tag;
232    typedef Logger<Tag>                                         LoggerType;
233    typedef IntermoduleType<LoggerType, true, true>             LoggerSingleton;
234    typedef LogUser<LoggerSingleton>                            LogUserType;
235    typedef IntermoduleType<LogUserType, true, true>            LogUserSingleton;
236    typedef IntermoduleType<LogPhoenixTester<Tag>, true, true>  LogPhoenixTesterSingleton;
237 
238    //Instantiate Phoenix tester singleton so that it will be destroyed the last
239    LogPhoenixTesterSingleton::get().dummy();
240 
241    //Now instantitate a log user singleton
242    LogUserType &log_user = LogUserSingleton::get();
243 
244    //Then force LoggerSingleton instantiation
245    //calling a function that will use it.
246    //After main ends, LoggerSingleton will be destroyed
247    //before LogUserSingleton due to LIFO
248    //singleton semantics
249    log_user.function_using_log();
250 
251    //Next, LogUserSingleton destructor will resurrect
252    //LoggerSingleton.
253    //After that LoggerSingleton will be destroyed and
254    //lastly LogPhoenixTester will be destroyed checking
255    //LoggerSingleton was correctly destroyed.
256    return 0;
257 }
258 
259 template < template<class T, bool LazyInit, bool Phoenix> class IntermoduleType >
dead_reference_singleton_test()260 int dead_reference_singleton_test()
261 {
262    typedef int DummyType;
263    typedef IntermoduleType<DummyType, true, false>                Tag;
264    typedef Logger<Tag>                                            LoggerType;
265    typedef IntermoduleType<LoggerType, true, false>               LoggerSingleton;
266    typedef LogDeadReferenceUser<LoggerSingleton>                  LogDeadReferenceUserType;
267    typedef IntermoduleType<LogDeadReferenceUserType, true, false> LogDeadReferenceUserSingleton;
268 
269    //Now instantitate a log user singleton
270    LogDeadReferenceUserType &log_user = LogDeadReferenceUserSingleton::get();
271 
272    //Then force LoggerSingleton instantiation
273    //calling a function that will use it.
274    //After main ends, LoggerSingleton will be destroyed
275    //before LogDeadReferenceUserType due to LIFO
276    //singleton semantics
277    log_user.function_using_log();
278 
279    //Next, LogDeadReferenceUserType destructor will try to use
280    //LoggerSingleton and an exception will be raised an catched.
281    return 0;
282 }
283 
284 //reduce name length
285 template<typename C, bool LazyInit, bool Phoenix>
286 class port_singleton
287    : public ipcdetail::portable_intermodule_singleton<C, LazyInit, Phoenix>
288 {};
289 
290 #ifdef BOOST_INTERPROCESS_WINDOWS
291 template<typename C, bool LazyInit, bool Phoenix>
292 class win_singleton
293    : public ipcdetail::windows_intermodule_singleton< C, LazyInit, Phoenix>
294 {};
295 #endif
296 
main()297 int main ()
298 {
299    if(0 != intermodule_singleton_test<port_singleton>()){
300       return 1;
301    }
302 
303    #ifdef BOOST_INTERPROCESS_WINDOWS
304    if(0 != intermodule_singleton_test<win_singleton>()){
305       return 1;
306    }
307    #endif
308 
309    //Only few platforms support this
310    #ifdef BOOST_INTERPROCESS_ATEXIT_CALLABLE_FROM_ATEXIT
311    //Phoenix singletons are tested after main ends,
312    //LogPhoenixTester does the work
313    phoenix_singleton_test<port_singleton>();
314    #ifdef BOOST_INTERPROCESS_WINDOWS
315    phoenix_singleton_test<win_singleton>();
316    #endif
317    #endif
318 
319    //Dead reference singletons are tested after main ends,
320    //LogDeadReferenceUser does the work
321    dead_reference_singleton_test<port_singleton>();
322    #ifdef BOOST_INTERPROCESS_WINDOWS
323    dead_reference_singleton_test<win_singleton>();
324    #endif
325 
326    return 0;
327 }
328