-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Expand file tree
/
Copy paththreadsafe_singleton.h
More file actions
134 lines (107 loc) · 4.44 KB
/
threadsafe_singleton.h
File metadata and controls
134 lines (107 loc) · 4.44 KB
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
#pragma once
#include <memory>
#include "source/common/common/assert.h"
#include "absl/base/call_once.h"
namespace Envoy {
/**
* ThreadSafeSingleton allows easy global cross-thread access to a non-const object.
*
* This singleton class should be used for singletons which must be globally
* accessible but can not be marked as const. All functions in the singleton class
* *must* be threadsafe.
*
* Note that there is heavy resistance in Envoy to adding this type of singleton
* if data will persist with state changes across tests, as it becomes difficult
* to write clean unit tests if a state change in one test will persist into
* another test. Be wary of using it. A example of acceptable usage is OsSyscallsImpl,
* where the functions are not strictly speaking const, but affect the OS rather than the
* class itself. An example of unacceptable usage upstream would be for
* globally accessible stat counters, it would have the aforementioned problem
* where state "leaks" across tests.
*
* */
template <class T> class TestThreadsafeSingletonInjector;
template <class T> class ThreadSafeSingleton {
public:
static T& get() {
absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create);
return *ThreadSafeSingleton<T>::instance_;
}
protected:
template <typename A> friend class TestThreadsafeSingletonInjector;
static void Create() { instance_ = new T(); }
static absl::once_flag create_once_;
static T* instance_;
};
template <class T> absl::once_flag ThreadSafeSingleton<T>::create_once_;
template <class T> T* ThreadSafeSingleton<T>::instance_ = nullptr;
// An instance of a singleton class which has the same thread safety properties
// as ThreadSafeSingleton, but must be created via initialize prior to access.
//
// As with ThreadSafeSingleton the use of this class is generally discouraged.
template <class T> class InjectableSingleton {
public:
static T& get() {
RELEASE_ASSERT(loader_ != nullptr, "InjectableSingleton used prior to initialization");
return *loader_;
}
static T* getExisting() { return loader_; }
static void initialize(T* value) {
RELEASE_ASSERT(value != nullptr, "InjectableSingleton initialized with null value.");
RELEASE_ASSERT(loader_ == nullptr, "InjectableSingleton initialized multiple times.");
loader_ = value;
}
static void clear() { loader_ = nullptr; }
// Atomically replace the value, returning the old value.
static T* replaceForTest(T* new_value) { return loader_.exchange(new_value); }
protected:
static std::atomic<T*> loader_;
};
template <class T> std::atomic<T*> InjectableSingleton<T>::loader_ = nullptr;
template <class T> class ScopedInjectableLoader {
public:
explicit ScopedInjectableLoader(std::unique_ptr<T>&& instance) {
instance_ = std::move(instance);
InjectableSingleton<T>::initialize(instance_.get());
}
~ScopedInjectableLoader() { InjectableSingleton<T>::clear(); }
T& instance() { return *instance_; }
private:
std::unique_ptr<T> instance_;
};
// This class saves the singleton object and restore the original singleton at destroy.
template <class T> class StackedScopedInjectableLoaderForTest {
public:
explicit StackedScopedInjectableLoaderForTest(std::unique_ptr<T>&& instance) {
instance_ = std::move(instance);
original_loader_ = InjectableSingleton<T>::replaceForTest(instance_.get());
}
~StackedScopedInjectableLoaderForTest() {
InjectableSingleton<T>::replaceForTest(original_loader_);
}
private:
std::unique_ptr<T> instance_;
T* original_loader_;
};
// An instance of a singleton class which is thread local and could only be initialized
// and accessed in the same thread. Different threads could have different T instances.
//
// As with ThreadSafeSingleton the use of this class is generally discouraged.
template <class T> class ThreadLocalInjectableSingleton {
public:
static T& get() {
RELEASE_ASSERT(loader_, "ThreadLocalInjectableSingleton used prior to initialization");
return *loader_;
}
static T* getExisting() { return loader_; }
static void initialize(T* value) {
RELEASE_ASSERT(value, "ThreadLocalInjectableSingleton initialized with null value.");
RELEASE_ASSERT(!loader_, "ThreadLocalInjectableSingleton initialized multiple times.");
loader_ = value;
}
static void clear() { loader_ = nullptr; }
protected:
static thread_local T* loader_;
};
template <class T> thread_local T* ThreadLocalInjectableSingleton<T>::loader_ = nullptr;
} // namespace Envoy