Library of Assembled Shared Sources
singleton_impl.cpp
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2011 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45#include "lass_common.h"
46#include "singleton_impl.h"
47#include "../singleton.h"
48#include "../thread.h"
49#include <cstdlib>
50#include <queue>
51
52namespace lass
53{
54namespace util
55{
56namespace impl
57{
58
59/** helper class to compare destruction priorities fo lass::util::SingletonBase.
60 * @internal
61 * @author Bram de Greve [Bramz]
62 */
64{
65public:
66 bool operator()(SingletonBase* a, SingletonBase* b) const
67 {
68 LASS_ASSERT(a != 0 && b != 0);
69 return a->destructionPriority() < b->destructionPriority();
70 }
71};
72
73
74
75
76/** The singleton guard will take care of the destruction of all singletons.
77 * @internal
78 * @author Bram de Greve [Bramz]
79 *
80 * All singletons will subscribe them to this guard with a DestructionPriority. On an explicit
81 * call of killEmAll() (or on destruction of the guard), all singletons will be destructed.
82 * The singletons with the highest DestructionPriority will be killed first. The order in which
83 * singletons of the same priority are destructed is undefined.
84 *
85 * The system of using this destruction priority controlled by a guard that is destructed by
86 * @c ::atexit() , is inspired by Alexandrescu's @e longevity singletons [1].
87 *
88 * @warning you should never call @c killEmAll() yourself!
89 *
90 * @b Reference:
91 * -# ALEXANDRESCU A. (2001), <i>Modern C++ Design: Generic Programming and Design Patterns
92 * applied</i>, C++ in depth series, Addison-Wesley, pages 129-156
93*/
94class SingletonGuard
95{
96public:
97 void subscribe(SingletonBase* singleton)
98 {
99 static std::atomic<int> semaphore{ 1 };
100 LASS_LOCK_INTEGRAL(semaphore)
101 {
102 deathRow_.push(singleton);
103 }
104 }
105 static SingletonGuard* instance()
106 {
107 static std::atomic<SingletonGuard*> neo{ nullptr };
108 static std::atomic<int> semaphore{ 1 };
109
110 if (deadReference(false))
111 {
112 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR: Usage of dead reference to SingletonGuard!" << std::endl;
113 return 0;
114 }
115
116 SingletonGuard* one = neo.load(std::memory_order_acquire);
117 if (!one)
118 {
119 LASS_LOCK_INTEGRAL(semaphore)
120 {
121 one = neo.load(std::memory_order_relaxed);
122 if (!one)
123 {
124 one = new SingletonGuard;
125 ::atexit(&SingletonGuard::killEmAll);
126 neo.store(one, std::memory_order_release);
127 }
128 }
129 }
130 LASS_ASSERT(one);
131 return one;
132 }
133
134private:
135 typedef std::priority_queue<SingletonBase*, std::vector<SingletonBase*>, CompareDestructionPriority> TDeathRow;
136
137 SingletonGuard()
138 {
139 }
140 ~SingletonGuard()
141 {
142 deadReference(true);
143 while (!deathRow_.empty())
144 {
145 SingletonBase* deadManWalking = deathRow_.top();
146 deathRow_.pop();
147 delete deadManWalking;
148 }
149 }
150 static void killEmAll()
151 {
152 // this should be called only once from ::atexit.
153 LASS_ASSERT(!deadReference(false));
154 delete instance();
155 }
156 static bool deadReference(bool setReferenceToDead)
157 {
158 static std::atomic<int> dead{ false };
159 if (setReferenceToDead)
160 {
161 dead.store(true, std::memory_order_release);
162 return true;
163 }
164 else
165 {
166 return dead.load(std::memory_order_acquire);
167 }
168 }
169
170 TDeathRow deathRow_;
171};
172
173
174
175/** constructor.
176 * @warning The singleton base DOES NOT subsribes itself at construction. Singleton<> must do this
177 * at the construction of a new singleton. Why? We better don't do it here, because
178 * the singleton is being constructed! it might not be complete yet.
179 */
181 destructionPriority_(0)
182{
183}
184
185
186
187SingletonBase::~SingletonBase()
188{
189}
190
191
192
193int SingletonBase::destructionPriority() const
194{
195 return destructionPriority_;
196}
197
198
199
200/** Subscribe to the singleton guard.
201 * Do this only once for each singleton, and in fact, don't do it yourself at all
202 * since Singleton<> already does it :)
203 *
204 * @warning this isn't thread safe as it is, but that's ok because its only caller
205 * Singleton::instance() is already locking.
206 */
207void SingletonBase::subscribeInstance(int destructionPriority)
208{
209 destructionPriority_ = destructionPriority;
210 if (destructionPriority_ == destructionPriorityNever)
211 {
212 return;
213 }
214 if (SingletonGuard* guard = SingletonGuard::instance())
215 {
216 guard->subscribe(this);
217 }
218}
219
220
221
222}
223}
224}
225
226// EOF
227
helper class to compare destruction priorities fo lass::util::SingletonBase.
base class of all singletons.
void subscribeInstance(int iDestructionPriority)
Subscribe to the singleton guard.
The singleton guard will take care of the destruction of all singletons.
general utility, debug facilities, ...
@ destructionPriorityNever
never destruct the singleton, let it live forever.
Definition singleton.h:128
Library for Assembled Shared Sources.
Definition config.h:53