xref: /aosp_15_r20/external/cronet/base/threading/thread_local_internal.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
6 #define BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
7 
8 #include "base/dcheck_is_on.h"
9 
10 #if DCHECK_IS_ON()
11 
12 #include <atomic>
13 #include <memory>
14 #include <ostream>
15 
16 #include "base/check_op.h"
17 #include "base/memory/raw_ptr.h"
18 #include "base/threading/thread_local_storage.h"
19 
20 namespace base {
21 namespace internal {
22 
23 // A version of ThreadLocalOwnedPointer which verifies that it's only destroyed
24 // when no threads, other than the one it is destroyed on, have remaining state
25 // set in it. A ThreadLocalOwnedPointer instance being destroyed too early would
26 // result in leaks per unregistering the TLS slot (and thus the DeleteTlsPtr
27 // hook).
28 template <typename T>
29 class CheckedThreadLocalOwnedPointer {
30  public:
31   CheckedThreadLocalOwnedPointer() = default;
32 
33   CheckedThreadLocalOwnedPointer(const CheckedThreadLocalOwnedPointer<T>&) =
34       delete;
35   CheckedThreadLocalOwnedPointer<T>& operator=(
36       const CheckedThreadLocalOwnedPointer<T>&) = delete;
37 
~CheckedThreadLocalOwnedPointer()38   ~CheckedThreadLocalOwnedPointer() {
39     Set(nullptr);
40 
41     DCHECK_EQ(num_assigned_threads_.load(std::memory_order_relaxed), 0)
42         << "Memory leak: Must join all threads or release all associated "
43            "thread-local slots before ~ThreadLocalOwnedPointer";
44   }
45 
Get()46   T* Get() const {
47     PtrTracker* const ptr_tracker = static_cast<PtrTracker*>(slot_.Get());
48     return ptr_tracker ? ptr_tracker->ptr_.get() : nullptr;
49   }
50 
Set(std::unique_ptr<T> ptr)51   std::unique_ptr<T> Set(std::unique_ptr<T> ptr) {
52     std::unique_ptr<T> existing_ptr;
53     auto existing_tracker = static_cast<PtrTracker*>(slot_.Get());
54     if (existing_tracker) {
55       existing_ptr = std::move(existing_tracker->ptr_);
56       delete existing_tracker;
57     }
58 
59     if (ptr)
60       slot_.Set(new PtrTracker(this, std::move(ptr)));
61     else
62       slot_.Set(nullptr);
63 
64     return existing_ptr;
65   }
66 
67   T& operator*() { return *Get(); }
68 
69  private:
70   struct PtrTracker {
71    public:
PtrTrackerPtrTracker72     PtrTracker(CheckedThreadLocalOwnedPointer<T>* outer, std::unique_ptr<T> ptr)
73         : outer_(outer), ptr_(std::move(ptr)) {
74       outer_->num_assigned_threads_.fetch_add(1, std::memory_order_relaxed);
75     }
76 
~PtrTrackerPtrTracker77     ~PtrTracker() {
78       outer_->num_assigned_threads_.fetch_sub(1, std::memory_order_relaxed);
79     }
80 
81     const raw_ptr<CheckedThreadLocalOwnedPointer<T>> outer_;
82     std::unique_ptr<T> ptr_;
83   };
84 
DeleteTlsPtr(void * ptr)85   static void DeleteTlsPtr(void* ptr) { delete static_cast<PtrTracker*>(ptr); }
86 
87   ThreadLocalStorage::Slot slot_{&DeleteTlsPtr};
88 
89   std::atomic_int num_assigned_threads_{0};
90 };
91 
92 }  // namespace internal
93 }  // namespace base
94 
95 #endif  // DCHECK_IS_ON()
96 
97 #endif  // BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
98