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

source code of boost/libs/interprocess/test/intermodule_singleton_test.cpp