/proc/self/cwd/source/common/thread_local/thread_local_impl.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <atomic> |
4 | | #include <cstdint> |
5 | | #include <list> |
6 | | #include <memory> |
7 | | #include <vector> |
8 | | |
9 | | #include "envoy/thread_local/thread_local.h" |
10 | | |
11 | | #include "source/common/common/logger.h" |
12 | | #include "source/common/common/non_copyable.h" |
13 | | |
14 | | namespace Envoy { |
15 | | namespace ThreadLocal { |
16 | | |
17 | | /** |
18 | | * Implementation of ThreadLocal that relies on static thread_local objects. |
19 | | */ |
20 | | class InstanceImpl : Logger::Loggable<Logger::Id::main>, public NonCopyable, public Instance { |
21 | | public: |
22 | | ~InstanceImpl() override; |
23 | | |
24 | | // ThreadLocal::Instance |
25 | | SlotPtr allocateSlot() override; |
26 | | void registerThread(Event::Dispatcher& dispatcher, bool main_thread) override; |
27 | | void shutdownGlobalThreading() override; |
28 | | void shutdownThread() override; |
29 | | Event::Dispatcher& dispatcher() override; |
30 | 2.64k | bool isShutdown() const override { return shutdown_; } |
31 | | |
32 | | private: |
33 | | // On destruction returns the slot index to the deferred delete queue (detaches it). This allows |
34 | | // a slot to be destructed on the main thread while controlling the lifetime of the underlying |
35 | | // slot as callbacks drain from workers. |
36 | | struct SlotImpl : public Slot { |
37 | | SlotImpl(InstanceImpl& parent, uint32_t index); |
38 | 48.0k | ~SlotImpl() override { parent_.removeSlot(index_); } |
39 | | std::function<void()> wrapCallback(const std::function<void()>& cb); |
40 | | std::function<void()> dataCallback(const UpdateCb& cb); |
41 | | static bool currentThreadRegisteredWorker(uint32_t index); |
42 | | static ThreadLocalObjectSharedPtr getWorker(uint32_t index); |
43 | | |
44 | | // ThreadLocal::Slot |
45 | | ThreadLocalObjectSharedPtr get() override; |
46 | | void runOnAllThreads(const UpdateCb& cb) override; |
47 | | void runOnAllThreads(const UpdateCb& cb, const std::function<void()>& complete_cb) override; |
48 | | bool currentThreadRegistered() override; |
49 | | void set(InitializeCb cb) override; |
50 | 0 | bool isShutdown() const override { return parent_.shutdown_; } |
51 | | |
52 | | InstanceImpl& parent_; |
53 | | const uint32_t index_; |
54 | | // The following is used to safely verify via weak_ptr that this slot is still alive. This |
55 | | // does not prevent all races if a callback does not capture appropriately, but it does fix |
56 | | // the common case of a slot destroyed immediately before anything is posted to a worker. |
57 | | // NOTE: The general safety model of a slot is that it is destroyed immediately on the main |
58 | | // thread. This means that *all* captures must not reference the slot object directly. |
59 | | // this is why index_ is captured manually in callbacks that require it. |
60 | | // NOTE: When the slot is destroyed, the index is immediately recycled. This is safe because |
61 | | // any new posts for a recycled index must come after any previous callbacks for the |
62 | | // previous owner of the index. |
63 | | // TODO(mattklein123): Add clang-tidy analysis rule to check that "this" is not captured by |
64 | | // a TLS function call. This check will not prevent all bad captures, but it will at least |
65 | | // make the programmer more aware of potential issues. |
66 | | std::shared_ptr<bool> still_alive_guard_; |
67 | | }; |
68 | | |
69 | | struct ThreadLocalData { |
70 | | Event::Dispatcher* dispatcher_{}; |
71 | | std::vector<ThreadLocalObjectSharedPtr> data_; |
72 | | }; |
73 | | |
74 | | void removeSlot(uint32_t slot); |
75 | | void runOnAllThreads(std::function<void()> cb); |
76 | | void runOnAllThreads(std::function<void()> cb, std::function<void()> main_callback); |
77 | | static void setThreadLocal(uint32_t index, ThreadLocalObjectSharedPtr object); |
78 | | |
79 | | static thread_local ThreadLocalData thread_local_data_; |
80 | | |
81 | | Thread::MainThread main_thread_; |
82 | | std::vector<Slot*> slots_; |
83 | | // A list of index of freed slots. |
84 | | std::list<uint32_t> free_slot_indexes_; |
85 | | std::list<std::reference_wrapper<Event::Dispatcher>> registered_threads_; |
86 | | Event::Dispatcher* main_thread_dispatcher_{}; |
87 | | std::atomic<bool> shutdown_{}; |
88 | | |
89 | | // Test only. |
90 | | friend class ThreadLocalInstanceImplTest; |
91 | | }; |
92 | | |
93 | | using InstanceImplPtr = std::unique_ptr<InstanceImpl>; |
94 | | |
95 | | } // namespace ThreadLocal |
96 | | } // namespace Envoy |