Skip to content

Commit 75d8177

Browse files
committed
Work-in-progress commit of a managed singleton
1 parent 6b87319 commit 75d8177

1 file changed

Lines changed: 203 additions & 0 deletions

File tree

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// This file is part of CoreLibrary containing useful reusable utility
2+
// classes.
3+
//
4+
// Copyright (C) 2014 to present, Duncan Crutchley
5+
// Contact <15799155+dac1976@users.noreply.github.com>
6+
//
7+
// This program is free software: you can redistribute it and/or modify
8+
// it under the terms of the GNU Lesser General Public License as published
9+
// by the Free Software Foundation, either version 3 of the License, or
10+
// (at your option) any later version.
11+
//
12+
// This program is distributed in the hope that it will be useful,
13+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
// GNU General Public License and GNU Lesser General Public License
16+
// for more details.
17+
//
18+
// You should have received a copy of the GNU General Public License
19+
// and GNU Lesser General Public License along with this program. If
20+
// not, see <http://www.gnu.org/licenses/>.
21+
22+
#ifndef MANAGED_SINGLETON_HPP
23+
#define MANAGED_SINGLETON_HPP
24+
25+
#include <atomic>
26+
#include <memory>
27+
#include <mutex>
28+
#include <utility>
29+
#include <type_traits>
30+
#include "Platform/PlatformDefines.h"
31+
32+
// TODO: This needs testing and maybe tweaking, it is a work-in-progress as a
33+
// hopeful replacement for the Loki singleton.
34+
35+
namespace core_lib
36+
{
37+
38+
#if defined(IS_CPP14)
39+
// NOTE: T must be default-constructible for fallback instance.
40+
template <typename T>
41+
class ManagedSingleton_Modern
42+
{
43+
static_assert(std::is_default_constructible<T>::value,
44+
"ManagedSingleton requires T to be default constructible");
45+
46+
public:
47+
template <typename... Args>
48+
static T& Instance(Args&&... args)
49+
{
50+
if (m_destroyed.load(std::memory_order_relaxed))
51+
{
52+
return DestroyedFallback();
53+
}
54+
55+
T* ptr = m_instance.load(std::memory_order_acquire);
56+
57+
if (nullptr == ptr)
58+
{
59+
std::lock_guard<std::mutex> lock(m_mutex);
60+
61+
ptr = m_instance.load(std::memory_order_relaxed);
62+
63+
if (nullptr == ptr)
64+
{
65+
if (m_destroyed.load(std::memory_order_relaxed))
66+
{
67+
return destroyed_fallback();
68+
}
69+
70+
std::unique_ptr<T> newInstance =
71+
std::make_unique<T>(std::forward<Args>(args)...);
72+
73+
ptr = newInstance.get();
74+
75+
m_instance.store(ptr, std::memory_order_release);
76+
m_owned = std::move(newInstance);
77+
}
78+
}
79+
80+
return *ptr;
81+
}
82+
83+
static T* TryInstance()
84+
{
85+
return m_instance.load(std::memory_order_acquire);
86+
}
87+
88+
// Destroy() must only be called when no other threads
89+
// are accessing Instance() or TryInstance().
90+
static void Destroy()
91+
{
92+
std::lock_guard<std::mutex> lock(m_mutex);
93+
94+
m_destroyed.store(true, std::memory_order_relaxed);
95+
m_owned.reset();
96+
m_instance.store(nullptr, std::memory_order_release);
97+
}
98+
99+
private:
100+
static T& DestroyedFallback()
101+
{
102+
static T fallbackInstance{};
103+
return fallbackInstance;
104+
}
105+
106+
private:
107+
static std::atomic<T*> m_instance;
108+
static std::unique_ptr<T> m_owned;
109+
static std::mutex m_mutex;
110+
static std::atomic<bool> m_destroyed;
111+
};
112+
113+
template <typename T>
114+
std::atomic<T*> ManagedSingleton_Modern<T>::m_instance{nullptr};
115+
116+
template <typename T>
117+
std::unique_ptr<T> ManagedSingleton_Modern<T>::m_owned;
118+
119+
template <typename T>
120+
std::mutex ManagedSingleton_Modern<T>::m_mutex;
121+
122+
template <typename T>
123+
std::atomic<bool> ManagedSingleton_Modern<T>::m_destroyed{false};
124+
125+
template <typename T>
126+
using ManagedSingleton = ManagedSingleton_Modern<T>;
127+
#else
128+
// NOTE: T must be default-constructible for fallback instance.
129+
template <typename T>
130+
class ManagedSingleton_Legacy
131+
{
132+
static_assert(std::is_default_constructible<T>::value,
133+
"ManagedSingleton requires T to be default constructible");
134+
135+
public:
136+
template <typename... Args>
137+
static T& Instance(Args&&... args)
138+
{
139+
if (m_destroyed.load(std::memory_order_relaxed))
140+
{
141+
return DestroyedFallback();
142+
}
143+
144+
std::lock_guard<std::mutex> lock(m_mutex);
145+
146+
if (nullptr == m_instance)
147+
{
148+
if (m_destroyed.load(std::memory_order_relaxed))
149+
{
150+
return DestroyedFallback();
151+
}
152+
153+
m_instance.reset(new T(std::forward<Args>(args)...));
154+
}
155+
156+
return *m_instance;
157+
}
158+
159+
static T* TryInstance()
160+
{
161+
std::lock_guard<std::mutex> lock(m_mutex);
162+
return m_instance.get();
163+
}
164+
165+
// Destroy() must only be called when no other threads are
166+
// accessing Instance() or TryInstance().
167+
static void Destroy()
168+
{
169+
std::lock_guard<std::mutex> lock(m_mutex);
170+
171+
m_destroyed.store(true, std::memory_order_relaxed);
172+
m_instance.reset();
173+
}
174+
175+
private:
176+
static T& DestroyedFallback()
177+
{
178+
static T fallbackInstance{};
179+
return fallbackInstance;
180+
}
181+
182+
private:
183+
static std::unique_ptr<T> m_instance;
184+
static std::mutex m_mutex;
185+
static std::atomic<bool> m_destroyed;
186+
};
187+
188+
template <typename T>
189+
std::unique_ptr<T> ManagedSingleton_Legacy<T>::m_instance{nullptr};
190+
191+
template <typename T>
192+
std::mutex ManagedSingleton_Legacy<T>::m_mutex;
193+
194+
template <typename T>
195+
std::atomic<bool> ManagedSingleton_Legacy<T>::m_destroyed{false};
196+
197+
template <typename T>
198+
using ManagedSingleton = ManagedSingleton_Legacy<T>;
199+
#endif
200+
201+
} // namespace core_lib
202+
203+
#endif // MANAGED_SINGLETON_HPP

0 commit comments

Comments
 (0)