FastLED 3.9.15
Loading...
Searching...
No Matches
singleton.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/align.h" // FL_ALIGN_AS_T macro for aligned storage
4#include "fl/stl/new.h" // Placement new operator // IWYU pragma: keep
5#include "fl/stl/compiler_control.h" // FL_HAS_SANITIZER_LSAN macro
7#include "fl/stl/noexcept.h"
8
9#if FL_HAS_SANITIZER_LSAN
10# include <sanitizer/lsan_interface.h>
11#endif
12
13namespace fl {
14
15namespace detail {
16
17// Process-wide singleton registry for cross-DLL singleton sharing.
18// On Windows, inline functions with static locals create per-DLL copies.
19// This registry ensures all DLLs share the same singleton instance.
20// Defined in singleton.cpp.hpp.
21void* singleton_registry_get(const char* key) FL_NOEXCEPT;
22void singleton_registry_set(const char* key, void* value) FL_NOEXCEPT;
23
24} // namespace detail
25
26// Internal singleton — NO registry lookup. Simple static storage + placement
27// new. For use in .cpp.hpp files only, where each compilation unit has exactly
28// one definition and cross-DLL sharing is handled by the .cpp.hpp include
29// pattern itself.
30//
31// IMPORTANT: Singleton instances are NEVER destroyed. This implementation uses
32// aligned char buffer storage and placement new to construct the instance on
33// first access, but intentionally never calls the destructor. This avoids:
34// 1. Static destruction order fiasco (undefined order at program exit)
35// 2. Unnecessary cleanup in long-lived embedded systems (which never exit)
36// 3. Crashes from singleton dependencies during destruction
37//
38// LSAN COMPATIBILITY: Uses __lsan::ScopedDisabler to prevent false positives.
39template <typename T, int N = 0> class Singleton {
40 public:
41 static T &instance() FL_NOEXCEPT {
42 // Aligned char buffer storage — never destroyed
43 struct FL_ALIGN_AS_T(alignof(T)) AlignedStorage {
44 char data[sizeof(T)];
45 };
46
47 static AlignedStorage storage;
48 static T* ptr = nullptr;
49 if (!ptr) {
50#if FL_HAS_SANITIZER_LSAN
51 __lsan::ScopedDisabler disabler;
52#endif
53 ptr = new (&storage.data) T();
54 }
55 return *ptr;
56 }
57
58 static T *instanceRef() FL_NOEXCEPT { return &instance(); }
59
60 Singleton(const Singleton &) FL_NOEXCEPT = delete;
62
63 private:
65 ~Singleton() FL_NOEXCEPT = default;
66};
67
68// Cross-DLL singleton — WITH FL_PRETTY_FUNCTION registry. For use in header
69// files where cross-DLL sharing matters. On Windows, inline functions with
70// static locals create per-DLL copies; the registry prevents this.
71//
72// IMPORTANT: SingletonShared instances are NEVER destroyed (same rationale as
73// Singleton above).
74//
75// CROSS-DLL SAFETY: Uses a process-wide registry to ensure all DLLs in a
76// process share the same singleton instance.
77//
78// LSAN COMPATIBILITY: Uses __lsan::ScopedDisabler to prevent false positives.
79template <typename T, int N = 0> class SingletonShared {
80 public:
81 static T &instance() FL_NOEXCEPT {
82 // Check the process-wide registry first (handles cross-DLL sharing).
83 // FL_PRETTY_FUNCTION produces a unique string per template instantiation
84 // (includes template parameters in the signature).
86 if (existing) {
87 return *static_cast<T*>(existing);
88 }
89 // First time for this type — create and register
90 T* ptr = instanceInner();
92 return *ptr;
93 }
94
95 static T *instanceRef() FL_NOEXCEPT { return &instance(); }
96
99
100 private:
103
105 // Aligned char buffer storage — never destroyed
106 struct FL_ALIGN_AS_T(alignof(T)) AlignedStorage {
107 char data[sizeof(T)];
108 };
109
110 static AlignedStorage storage;
111
112#if FL_HAS_SANITIZER_LSAN
113 __lsan::ScopedDisabler disabler; // Ignore all allocations in this scope
114#endif
115
116 T* ptr = new (&storage.data) T();
117 return ptr;
118 }
119};
120
121// Thread-local singleton — combines Singleton and ThreadLocal patterns.
122// Each thread gets its own T instance, but the ThreadLocal<T> container itself
123// is a process-wide singleton (never destroyed, same rationale as Singleton).
124//
125// Replaces the common pattern:
126// T& get() { static ThreadLocal<T> tl; return tl.access(); }
127// with:
128// T& get() { return SingletonThreadLocal<T>::instance(); }
129//
130// LSAN COMPATIBILITY: Uses __lsan::ScopedDisabler to prevent false positives.
131template <typename T, int N = 0> class SingletonThreadLocal {
132 public:
133 static T &instance() FL_NOEXCEPT {
134 // Aligned char buffer storage — never destroyed
135 struct FL_ALIGN_AS_T(alignof(ThreadLocal<T>)) AlignedStorage {
136 char data[sizeof(ThreadLocal<T>)];
137 };
138
139 static AlignedStorage storage;
140 static ThreadLocal<T>* ptr = nullptr;
141 if (!ptr) {
142 ptr = new (&storage.data) ThreadLocal<T>();
143 }
144 // The ThreadLocal container is never destroyed (singleton pattern).
145 // Each thread's createStorage() allocation is intentionally permanent.
146 // Suppress LSAN false positives for per-thread storage allocations.
147#if FL_HAS_SANITIZER_LSAN
148 __lsan::ScopedDisabler disabler;
149#endif
150 return ptr->access();
151 }
152
153 static T *instanceRef() FL_NOEXCEPT { return &instance(); }
154
157
158 private:
161};
162
163} // namespace fl
Alignment macros and utilities for FastLED.
Singleton(const Singleton &) FL_NOEXCEPT=delete
static T & instance() FL_NOEXCEPT
Definition singleton.h:41
Singleton & operator=(const Singleton &) FL_NOEXCEPT=delete
Singleton() FL_NOEXCEPT=default
static T * instanceRef() FL_NOEXCEPT
Definition singleton.h:58
static T & instance() FL_NOEXCEPT
Definition singleton.h:81
SingletonShared(const SingletonShared &) FL_NOEXCEPT=delete
SingletonShared & operator=(const SingletonShared &) FL_NOEXCEPT=delete
static T * instanceRef() FL_NOEXCEPT
Definition singleton.h:95
static T * instanceInner() FL_NOEXCEPT
Definition singleton.h:104
SingletonShared() FL_NOEXCEPT=default
static T & instance() FL_NOEXCEPT
Definition singleton.h:133
static T * instanceRef() FL_NOEXCEPT
Definition singleton.h:153
SingletonThreadLocal() FL_NOEXCEPT=default
SingletonThreadLocal(const SingletonThreadLocal &) FL_NOEXCEPT=delete
SingletonThreadLocal & operator=(const SingletonThreadLocal &) FL_NOEXCEPT=delete
T & access() FL_NOEXCEPT
void * singleton_registry_get(const char *key)
void singleton_registry_set(const char *key, void *value)
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_traits.h:48
constexpr int type_rank< T >::value
class FL_ALIGN_AS_T(max_align< Types... >::value) variant
Definition variant.h:17
ThreadLocalFake< T > ThreadLocal
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_PRETTY_FUNCTION
#define FL_NOEXCEPT