Line data Source code
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 47078 : static T& get() { 32 47078 : absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); 33 47078 : return *ThreadSafeSingleton<T>::instance_; 34 47078 : } 35 : 36 : protected: 37 : template <typename A> friend class TestThreadsafeSingletonInjector; 38 : 39 117 : 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 203184 : static T& get() { 56 203184 : RELEASE_ASSERT(loader_ != nullptr, "InjectableSingleton used prior to initialization"); 57 203184 : return *loader_; 58 203184 : } 59 : 60 : static T* getExisting() { return loader_; } 61 : 62 756 : static void initialize(T* value) { 63 756 : RELEASE_ASSERT(value != nullptr, "InjectableSingleton initialized with null value."); 64 756 : RELEASE_ASSERT(loader_ == nullptr, "InjectableSingleton initialized multiple times."); 65 756 : loader_ = value; 66 756 : } 67 628 : static void clear() { loader_ = nullptr; } 68 : 69 : protected: 70 : static std::atomic<T*> loader_; 71 : }; 72 : 73 : template <class T> std::atomic<T*> InjectableSingleton<T>::loader_ = nullptr; 74 : 75 : template <class T> class ScopedInjectableLoader { 76 : public: 77 399 : explicit ScopedInjectableLoader(std::unique_ptr<T>&& instance) { 78 399 : instance_ = std::move(instance); 79 399 : InjectableSingleton<T>::initialize(instance_.get()); 80 399 : } 81 271 : ~ScopedInjectableLoader() { InjectableSingleton<T>::clear(); } 82 : 83 : T& instance() { return *instance_; } 84 : 85 : private: 86 : std::unique_ptr<T> instance_; 87 : }; 88 : 89 : // This class saves the singleton object and restore the original singleton at destroy. This class 90 : // is not thread safe. It can be used in single thread test. 91 : template <class T> 92 : class StackedScopedInjectableLoader : 93 : // To access the protected loader_. 94 : protected InjectableSingleton<T> { 95 : public: 96 : explicit StackedScopedInjectableLoader(std::unique_ptr<T>&& instance) { 97 : original_loader_ = InjectableSingleton<T>::getExisting(); 98 : InjectableSingleton<T>::clear(); 99 : instance_ = std::move(instance); 100 : InjectableSingleton<T>::initialize(instance_.get()); 101 : } 102 : ~StackedScopedInjectableLoader() { InjectableSingleton<T>::loader_ = original_loader_; } 103 : 104 : private: 105 : std::unique_ptr<T> instance_; 106 : T* original_loader_; 107 : }; 108 : 109 : } // namespace Envoy