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 | |
16 | using namespace boost::interprocess; |
17 | |
18 | class 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 | |
37 | class MyDerivedClass |
38 | : public MyClass |
39 | {}; |
40 | |
41 | class MyThrowingClass |
42 | { |
43 | public: |
44 | MyThrowingClass() |
45 | { |
46 | throw int(0); |
47 | } |
48 | }; |
49 | |
50 | |
51 | template < template<class T, bool LazyInit, bool Phoenix> class IntermoduleType > |
52 | int 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. |
99 | template<class Tag> |
100 | class 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 | |
122 | template<class Tag> |
123 | unsigned int Logger<Tag>::constructed_times; |
124 | |
125 | template<class Tag> |
126 | unsigned 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 |
132 | template<class LogSingleton> |
133 | class 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 |
154 | template<class Tag> |
155 | class 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 |
198 | template<class LogSingleton> |
199 | class 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 | |
228 | template < template<class T, bool LazyInit, bool Phoenix> class IntermoduleType > |
229 | int 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 | |
260 | template < template<class T, bool LazyInit, bool Phoenix> class IntermoduleType > |
261 | int 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 |
286 | template<typename C, bool LazyInit, bool Phoenix> |
287 | class port_singleton |
288 | : public ipcdetail::portable_intermodule_singleton<C, LazyInit, Phoenix> |
289 | {}; |
290 | |
291 | #ifdef BOOST_INTERPROCESS_WINDOWS |
292 | template<typename C, bool LazyInit, bool Phoenix> |
293 | class win_singleton |
294 | : public ipcdetail::windows_intermodule_singleton< C, LazyInit, Phoenix> |
295 | {}; |
296 | #endif |
297 | |
298 | int 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 | |