forked from ARMmbed/mbed-os
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SingletonPtr.h
210 lines (195 loc) · 6.21 KB
/
SingletonPtr.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/** \addtogroup platform */
/** @{*/
/**
* \defgroup platform_SingletonPtr SingletonPtr class
* @{
*/
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SINGLETONPTR_H
#define SINGLETONPTR_H
#include <stdlib.h>
#include <stdint.h>
#include <new>
#include "platform/mbed_assert.h"
#include "platform/mbed_atomic.h"
#ifdef MBED_CONF_RTOS_PRESENT
#include "cmsis_os2.h"
#endif
#ifdef MBED_CONF_RTOS_PRESENT
extern osMutexId_t singleton_mutex_id;
#endif
/** Lock the singleton mutex
*
* This function is typically used to provide
* exclusive access when initializing a
* global object.
*/
inline static void singleton_lock(void)
{
#ifdef MBED_CONF_RTOS_PRESENT
if (!singleton_mutex_id) {
// RTOS has not booted yet so no mutex is needed
return;
}
osMutexAcquire(singleton_mutex_id, osWaitForever);
#endif
}
/** Unlock the singleton mutex
*
* This function is typically used to provide
* exclusive access when initializing a
* global object.
*/
inline static void singleton_unlock(void)
{
#ifdef MBED_CONF_RTOS_PRESENT
if (!singleton_mutex_id) {
// RTOS has not booted yet so no mutex is needed
return;
}
osMutexRelease(singleton_mutex_id);
#endif
}
/** Utility class for creating and using a singleton
*
* @note Synchronization level: Thread safe
*
* @note: This class is lazily initialized on first use.
* This class has a constexpr default constructor so if it is
* not used as a non-local variable it will be garbage collected.
*
* @note: This class would normally be used in a static standalone
* context. It does not call the destructor of the wrapped object
* when it is destroyed, effectively ensuring linker exclusion of the
* destructor for static objects. If used in another context, such as
* a member of a normal class wanting "initialize on first-use"
* semantics on a member, care should be taken to call the destroy
* method manually if necessary.
*
* @note: If used as a sub-object of a class, that class's own
* constructor must be constexpr to achieve its exclusion by
* the linker when unused. That will require explicit
* initialization of its other members.
*
* @note: More detail on initialization: Formerly, SingletonPtr
* had no constructor, so was "zero-initialized" when non-local.
* So if enclosed in another class with no constructor, the whole
* thing would be zero-initialized, and linker-excludable.
* Having no constructor meant SingletonPtr was not constexpr,
* which limited applicability in other contexts. With its new
* constexpr constructor, it is now "constant-initialized" when
* non-local. This achieves the same effect as a standalone
* non-local object, but as a sub-object linker exclusion is
* now only achieved if the outer object is itself using a
* constexpr constructor to get constant-initialization.
* Otherwise, the outer object will be neither zero-initialized
* nor constant-initialized, so will be "dynamic-initialized",
* and likely to be left in by the linker.
*/
template <class T>
struct SingletonPtr {
// Initializers are required to make default constructor constexpr
// This adds no overhead as a static object - the compiler and linker can
// figure out that we are effectively zero-init, and either place us in
// ".bss", or exclude us if unused.
constexpr SingletonPtr() noexcept : _ptr(), _data() { }
/** Get a pointer to the underlying singleton
*
* @returns
* A pointer to the singleton
*/
T *get() const
{
T *p = core_util_atomic_load(&_ptr);
if (p == NULL) {
singleton_lock();
p = _ptr;
if (p == NULL) {
p = new (_data) T();
core_util_atomic_store(&_ptr, p);
}
singleton_unlock();
}
// _ptr was not zero initialized or was
// corrupted if this assert is hit
MBED_ASSERT(p == reinterpret_cast<T *>(&_data));
return p;
}
/** Get a pointer to the underlying singleton
*
* @returns
* A pointer to the singleton
*/
T *operator->() const
{
return get();
}
/** Get a reference to the underlying singleton
*
* @returns
* A reference to the singleton
*/
T &operator*() const
{
return *get();
}
/** Get a pointer to the underlying singleton
*
* Gets a pointer without initialization - can be
* used as an optimization when it is known that
* initialization must have already occurred.
*
* @returns
* A pointer to the singleton, or NULL if not
* initialized.
*/
T *get_no_init() const
{
return _ptr;
}
/** Destroy the underlying singleton
*
* The underlying singleton is never automatically destroyed;
* this is a potential optimization to avoid destructors
* being pulled into an embedded image on the exit path,
* which should never occur. The destructor can be
* manually invoked via this call.
*
* Unlike construction, this is not thread-safe. After this call,
* no further operations on the object are permitted.
*
* Is a no-op if the object has not been constructed.
*/
void destroy()
{
if (_ptr) {
_ptr->~T();
}
}
mutable T *_ptr;
#if __cplusplus >= 201103L && !defined __CC_ARM
// Align data appropriately (ARM Compiler 5 does not support alignas in C++11 mode)
alignas(T) mutable char _data[sizeof(T)];
#else
// Force data to be 8 byte aligned
mutable uint64_t _data[(sizeof(T) + sizeof(uint64_t) - 1) / sizeof(uint64_t)];
#endif
};
#endif
/**@}*/
/**@}*/