/proc/self/cwd/source/common/singleton/threadsafe_singleton.h
Line | Count | Source (jump to first uncovered line) |
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 | 559k | static T& get() { |
32 | 559k | absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); |
33 | 559k | return *ThreadSafeSingleton<T>::instance_; |
34 | 559k | } Envoy::ThreadSafeSingleton<Envoy::Http::PrefixValue>::get() Line | Count | Source | 31 | 43.9k | static T& get() { | 32 | 43.9k | absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); | 33 | 43.9k | return *ThreadSafeSingleton<T>::instance_; | 34 | 43.9k | } |
Envoy::ThreadSafeSingleton<Envoy::Api::OsSysCallsImpl>::get() Line | Count | Source | 31 | 492k | static T& get() { | 32 | 492k | absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); | 33 | 492k | return *ThreadSafeSingleton<T>::instance_; | 34 | 492k | } |
Unexecuted instantiation: Envoy::ThreadSafeSingleton<Envoy::Api::LinuxOsSysCallsImpl>::get() grpc_mux_impl.cc:Envoy::ThreadSafeSingleton<Envoy::Config::(anonymous namespace)::AllMuxesState>::get() Line | Count | Source | 31 | 6.85k | static T& get() { | 32 | 6.85k | absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); | 33 | 6.85k | return *ThreadSafeSingleton<T>::instance_; | 34 | 6.85k | } |
new_grpc_mux_impl.cc:Envoy::ThreadSafeSingleton<Envoy::Config::(anonymous namespace)::AllMuxesState>::get() Line | Count | Source | 31 | 5.79k | static T& get() { | 32 | 5.79k | absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); | 33 | 5.79k | return *ThreadSafeSingleton<T>::instance_; | 34 | 5.79k | } |
grpc_mux_impl.cc:Envoy::ThreadSafeSingleton<Envoy::Config::XdsMux::(anonymous namespace)::AllMuxesState>::get() Line | Count | Source | 31 | 9.99k | static T& get() { | 32 | 9.99k | absl::call_once(ThreadSafeSingleton<T>::create_once_, &ThreadSafeSingleton<T>::Create); | 33 | 9.99k | return *ThreadSafeSingleton<T>::instance_; | 34 | 9.99k | } |
|
35 | | |
36 | | protected: |
37 | | template <typename A> friend class TestThreadsafeSingletonInjector; |
38 | | |
39 | 120 | static void Create() { instance_ = new T(); } Envoy::ThreadSafeSingleton<Envoy::Http::PrefixValue>::Create() Line | Count | Source | 39 | 37 | static void Create() { instance_ = new T(); } |
Envoy::ThreadSafeSingleton<Envoy::Api::OsSysCallsImpl>::Create() Line | Count | Source | 39 | 47 | static void Create() { instance_ = new T(); } |
Unexecuted instantiation: Envoy::ThreadSafeSingleton<Envoy::Api::LinuxOsSysCallsImpl>::Create() grpc_mux_impl.cc:Envoy::ThreadSafeSingleton<Envoy::Config::(anonymous namespace)::AllMuxesState>::Create() Line | Count | Source | 39 | 12 | static void Create() { instance_ = new T(); } |
new_grpc_mux_impl.cc:Envoy::ThreadSafeSingleton<Envoy::Config::(anonymous namespace)::AllMuxesState>::Create() Line | Count | Source | 39 | 12 | static void Create() { instance_ = new T(); } |
grpc_mux_impl.cc:Envoy::ThreadSafeSingleton<Envoy::Config::XdsMux::(anonymous namespace)::AllMuxesState>::Create() Line | Count | Source | 39 | 12 | 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 | 2.45M | static T& get() { |
56 | 2.45M | RELEASE_ASSERT(loader_ != nullptr, "InjectableSingleton used prior to initialization"); |
57 | 2.45M | return *loader_; |
58 | 2.45M | } Envoy::InjectableSingleton<Envoy::Network::SocketInterface>::get() Line | Count | Source | 55 | 2.45M | static T& get() { | 56 | 2.45M | RELEASE_ASSERT(loader_ != nullptr, "InjectableSingleton used prior to initialization"); | 57 | 2.45M | return *loader_; | 58 | 2.45M | } |
Envoy::InjectableSingleton<Envoy::Common::Crypto::Utility>::get() Line | Count | Source | 55 | 5.02k | static T& get() { | 56 | 5.02k | RELEASE_ASSERT(loader_ != nullptr, "InjectableSingleton used prior to initialization"); | 57 | 5.02k | return *loader_; | 58 | 5.02k | } |
|
59 | | |
60 | | static T* getExisting() { return loader_; } |
61 | | |
62 | 254 | static void initialize(T* value) { |
63 | 254 | RELEASE_ASSERT(value != nullptr, "InjectableSingleton initialized with null value."); |
64 | 254 | RELEASE_ASSERT(loader_ == nullptr, "InjectableSingleton initialized multiple times."); |
65 | 254 | loader_ = value; |
66 | 254 | } Envoy::InjectableSingleton<Envoy::Common::Crypto::Utility>::initialize(Envoy::Common::Crypto::Utility*) Line | Count | Source | 62 | 122 | static void initialize(T* value) { | 63 | 122 | RELEASE_ASSERT(value != nullptr, "InjectableSingleton initialized with null value."); | 64 | 122 | RELEASE_ASSERT(loader_ == nullptr, "InjectableSingleton initialized multiple times."); | 65 | 122 | loader_ = value; | 66 | 122 | } |
Envoy::InjectableSingleton<Envoy::Network::SocketInterface>::initialize(Envoy::Network::SocketInterface*) Line | Count | Source | 62 | 132 | static void initialize(T* value) { | 63 | 132 | RELEASE_ASSERT(value != nullptr, "InjectableSingleton initialized with null value."); | 64 | 132 | RELEASE_ASSERT(loader_ == nullptr, "InjectableSingleton initialized multiple times."); | 65 | 132 | loader_ = value; | 66 | 132 | } |
|
67 | 0 | static void clear() { loader_ = nullptr; } |
68 | | |
69 | | // Atomically replace the value, returning the old value. |
70 | | 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 | 254 | explicit ScopedInjectableLoader(std::unique_ptr<T>&& instance) { |
81 | 254 | instance_ = std::move(instance); |
82 | 254 | InjectableSingleton<T>::initialize(instance_.get()); |
83 | 254 | } Envoy::ScopedInjectableLoader<Envoy::Common::Crypto::Utility>::ScopedInjectableLoader(std::__1::unique_ptr<Envoy::Common::Crypto::Utility, std::__1::default_delete<Envoy::Common::Crypto::Utility> >&&) Line | Count | Source | 80 | 122 | explicit ScopedInjectableLoader(std::unique_ptr<T>&& instance) { | 81 | 122 | instance_ = std::move(instance); | 82 | 122 | InjectableSingleton<T>::initialize(instance_.get()); | 83 | 122 | } |
Envoy::ScopedInjectableLoader<Envoy::Network::SocketInterface>::ScopedInjectableLoader(std::__1::unique_ptr<Envoy::Network::SocketInterface, std::__1::default_delete<Envoy::Network::SocketInterface> >&&) Line | Count | Source | 80 | 132 | explicit ScopedInjectableLoader(std::unique_ptr<T>&& instance) { | 81 | 132 | instance_ = std::move(instance); | 82 | 132 | InjectableSingleton<T>::initialize(instance_.get()); | 83 | 132 | } |
|
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 | | explicit StackedScopedInjectableLoaderForTest(std::unique_ptr<T>&& instance) { |
96 | | instance_ = std::move(instance); |
97 | | original_loader_ = InjectableSingleton<T>::replaceForTest(instance_.get()); |
98 | | } |
99 | | ~StackedScopedInjectableLoaderForTest() { |
100 | | InjectableSingleton<T>::replaceForTest(original_loader_); |
101 | | } |
102 | | |
103 | | private: |
104 | | std::unique_ptr<T> instance_; |
105 | | T* original_loader_; |
106 | | }; |
107 | | |
108 | | } // namespace Envoy |