mirror of https://github.com/ARMmbed/mbed-os.git
SingletonPtr: API extensions, make constexpr
* Adjust definition to make the default constructor `constexpr`. This permits use in classes that want lazy initialization and their own `constexpr` constructor, such as `mstd::mutex`. * Add `get_no_init()` method to allow an explicit optimisation for paths that know they won be the first call (such as `mstd::mutex::unlock`). * Add `destroy()` method to permit destruction of the contained object. (`SingletonPtr`'s destructor does not call its destructor - a cheat to omit destructors of static objects). Needed if using in a class that needs proper destruction.pull/11039/head
parent
0aab1a9ea1
commit
0bb4c050b7
|
@ -345,15 +345,21 @@ protected:
|
|||
enum SPIName { GlobalSPI };
|
||||
#endif
|
||||
|
||||
// All members of spi_peripheral_s must be initialized to make the structure
|
||||
// constant-initialized, and hence able to be omitted by the linker,
|
||||
// as SingletonPtr now relies on C++ constant-initialization. (Previously it
|
||||
// worked through C++ zero-initialization). And all the constants should be zero
|
||||
// to ensure it stays in the actual zero-init part of the image if used, avoiding
|
||||
// an initialized-data cost.
|
||||
struct spi_peripheral_s {
|
||||
/* Internal SPI name identifying the resources. */
|
||||
SPIName name;
|
||||
SPIName name = SPIName(0);
|
||||
/* Internal SPI object handling the resources' state. */
|
||||
spi_t spi;
|
||||
spi_t spi{};
|
||||
/* Used by lock and unlock for thread safety */
|
||||
SingletonPtr<PlatformMutex> mutex;
|
||||
/* Current user of the SPI */
|
||||
SPI *owner;
|
||||
SPI *owner = nullptr;
|
||||
#if DEVICE_SPI_ASYNCH && TRANSACTION_QUEUE_SIZE_SPI
|
||||
/* Queue of pending transfers */
|
||||
SingletonPtr<CircularBuffer<Transaction<SPI>, TRANSACTION_QUEUE_SIZE_SPI> > transaction_buffer;
|
||||
|
|
|
@ -42,11 +42,17 @@ static const fhss_api_t *fhss_active_handle = NULL;
|
|||
static EventQueue *equeue;
|
||||
#endif
|
||||
|
||||
// All members of fhss_timeout_s must be initialized to make the structure
|
||||
// constant-initialized, and hence able to be omitted by the linker,
|
||||
// as SingletonPtr now relies on C++ constant-initialization. (Previously it
|
||||
// worked through C++ zero-initialization). And all the constants should be zero
|
||||
// to ensure it stays in the actual zero-init part of the image if used, avoiding
|
||||
// an initialized-data cost.
|
||||
struct fhss_timeout_s {
|
||||
void (*fhss_timer_callback)(const fhss_api_t *fhss_api, uint16_t);
|
||||
uint32_t start_time;
|
||||
uint32_t stop_time;
|
||||
bool active;
|
||||
void (*fhss_timer_callback)(const fhss_api_t *fhss_api, uint16_t) = nullptr;
|
||||
uint32_t start_time = 0;
|
||||
uint32_t stop_time = 0;
|
||||
bool active = false;
|
||||
SingletonPtr<Timeout> timeout;
|
||||
};
|
||||
|
||||
|
|
|
@ -71,21 +71,51 @@ inline static void singleton_unlock(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
/** Utility class for creating an using a singleton
|
||||
/** Utility class for creating and using a singleton
|
||||
*
|
||||
* @note Synchronization level: Thread safe
|
||||
*
|
||||
* @note: This class must only be used in a static context -
|
||||
* this class must never be allocated or created on the
|
||||
* stack.
|
||||
*
|
||||
* @note: This class is lazily initialized on first use.
|
||||
* This class is a POD type so if it is not used it will
|
||||
* be garbage collected.
|
||||
* 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
|
||||
|
@ -93,13 +123,13 @@ struct SingletonPtr {
|
|||
*/
|
||||
T *get() const
|
||||
{
|
||||
T *p = static_cast<T *>(core_util_atomic_load_ptr(&_ptr));
|
||||
T *p = core_util_atomic_load(&_ptr);
|
||||
if (p == NULL) {
|
||||
singleton_lock();
|
||||
p = static_cast<T *>(_ptr);
|
||||
p = _ptr;
|
||||
if (p == NULL) {
|
||||
p = new (_data) T();
|
||||
core_util_atomic_store_ptr(&_ptr, p);
|
||||
core_util_atomic_store(&_ptr, p);
|
||||
}
|
||||
singleton_unlock();
|
||||
}
|
||||
|
@ -129,8 +159,42 @@ struct SingletonPtr {
|
|||
return *get();
|
||||
}
|
||||
|
||||
// This is zero initialized when in global scope
|
||||
mutable void *_ptr;
|
||||
/** 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)];
|
||||
|
|
|
@ -46,7 +46,9 @@ retval
|
|||
dequeue
|
||||
assertation
|
||||
destructor
|
||||
destructors
|
||||
constructor
|
||||
constructors
|
||||
ctor
|
||||
dtor
|
||||
dereference
|
||||
|
@ -71,6 +73,7 @@ deasserted
|
|||
getter
|
||||
setter
|
||||
preallocated
|
||||
excludable
|
||||
ascii
|
||||
IPv
|
||||
param
|
||||
|
|
Loading…
Reference in New Issue