1
#pragma once
2

            
3
#include <memory>
4

            
5
#include "source/common/common/assert.h"
6

            
7
#include "absl/base/call_once.h"
8

            
9
namespace Envoy {
10

            
11
/**
12
 * ThreadSafeSingleton allows easy global cross-thread access to a non-const object.
13
 *
14
 * This singleton class should be used for singletons which must be globally
15
 * accessible but can not be marked as const. All functions in the singleton class
16
 * *must* be threadsafe.
17
 *
18
 * Note that there is heavy resistance in Envoy to adding this type of singleton
19
 * if data will persist with state changes across tests, as it becomes difficult
20
 * to write clean unit tests if a state change in one test will persist into
21
 * another test. Be wary of using it. A example of acceptable usage is OsSyscallsImpl,
22
 * where the functions are not strictly speaking const, but affect the OS rather than the
23
 * class itself. An example of unacceptable usage upstream would be for
24
 * globally accessible stat counters, it would have the aforementioned problem
25
 * where state "leaks" across tests.
26
 *
27
 * */
28
template <class T> class TestThreadsafeSingletonInjector;
29
template <class T> class ThreadSafeSingleton {
30
public:
31
4175813
  static T& get() {
32
4175813
    absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create);
33
4175813
    return *ThreadSafeSingleton<T>::instance_;
34
4175813
  }
35

            
36
protected:
37
  template <typename A> friend class TestThreadsafeSingletonInjector;
38

            
39
3618
  static void Create() { instance_ = new T(); }
40

            
41
  static absl::once_flag create_once_;
42
  static T* instance_;
43
};
44

            
45
template <class T> absl::once_flag ThreadSafeSingleton<T>::create_once_;
46

            
47
template <class T> T* ThreadSafeSingleton<T>::instance_ = nullptr;
48

            
49
// An instance of a singleton class which has the same thread safety properties
50
// as ThreadSafeSingleton, but must be created via initialize prior to access.
51
//
52
// As with ThreadSafeSingleton the use of this class is generally discouraged.
53
template <class T> class InjectableSingleton {
54
public:
55
3784341
  static T& get() {
56
3784341
    RELEASE_ASSERT(loader_ != nullptr, "InjectableSingleton used prior to initialization");
57
3784341
    return *loader_;
58
3784341
  }
59

            
60
2
  static T* getExisting() { return loader_; }
61

            
62
2999
  static void initialize(T* value) {
63
2999
    RELEASE_ASSERT(value != nullptr, "InjectableSingleton initialized with null value.");
64
2999
    RELEASE_ASSERT(loader_ == nullptr, "InjectableSingleton initialized multiple times.");
65
2999
    loader_ = value;
66
2999
  }
67
6
  static void clear() { loader_ = nullptr; }
68

            
69
  // Atomically replace the value, returning the old value.
70
492
  static T* replaceForTest(T* new_value) { return loader_.exchange(new_value); }
71

            
72
protected:
73
  static std::atomic<T*> loader_;
74
};
75

            
76
template <class T> std::atomic<T*> InjectableSingleton<T>::loader_ = nullptr;
77

            
78
template <class T> class ScopedInjectableLoader {
79
public:
80
2993
  explicit ScopedInjectableLoader(std::unique_ptr<T>&& instance) {
81
2993
    instance_ = std::move(instance);
82
2993
    InjectableSingleton<T>::initialize(instance_.get());
83
2993
  }
84
  ~ScopedInjectableLoader() { InjectableSingleton<T>::clear(); }
85

            
86
  T& instance() { return *instance_; }
87

            
88
private:
89
  std::unique_ptr<T> instance_;
90
};
91

            
92
// This class saves the singleton object and restore the original singleton at destroy.
93
template <class T> class StackedScopedInjectableLoaderForTest {
94
public:
95
246
  explicit StackedScopedInjectableLoaderForTest(std::unique_ptr<T>&& instance) {
96
246
    instance_ = std::move(instance);
97
246
    original_loader_ = InjectableSingleton<T>::replaceForTest(instance_.get());
98
246
  }
99
246
  ~StackedScopedInjectableLoaderForTest() {
100
246
    InjectableSingleton<T>::replaceForTest(original_loader_);
101
246
  }
102

            
103
private:
104
  std::unique_ptr<T> instance_;
105
  T* original_loader_;
106
};
107

            
108
// An instance of a singleton class which is thread local and could only be initialized
109
// and accessed in the same thread. Different threads could have different T instances.
110
//
111
// As with ThreadSafeSingleton the use of this class is generally discouraged.
112
template <class T> class ThreadLocalInjectableSingleton {
113
public:
114
91
  static T& get() {
115
91
    RELEASE_ASSERT(loader_, "ThreadLocalInjectableSingleton used prior to initialization");
116
91
    return *loader_;
117
91
  }
118

            
119
89
  static T* getExisting() { return loader_; }
120

            
121
10921
  static void initialize(T* value) {
122
10921
    RELEASE_ASSERT(value, "ThreadLocalInjectableSingleton initialized with null value.");
123
10921
    RELEASE_ASSERT(!loader_, "ThreadLocalInjectableSingleton initialized multiple times.");
124
10921
    loader_ = value;
125
10921
  }
126
10921
  static void clear() { loader_ = nullptr; }
127

            
128
protected:
129
  static thread_local T* loader_;
130
};
131

            
132
template <class T> thread_local T* ThreadLocalInjectableSingleton<T>::loader_ = nullptr;
133

            
134
} // namespace Envoy