1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 #if DCHECK_IS_ON()
9 
10 #include <atomic>
11 #include <memory>
12 
13 #include "base/macros.h"
14 #include "base/threading/thread_local_storage.h"
15 
16 namespace base {
17 namespace internal {
18 
19 // A version of ThreadLocalOwnedPointer which verifies that it's only destroyed
20 // when no threads, other than the one it is destroyed on, have remaining state
21 // set in it. A ThreadLocalOwnedPointer instance being destroyed too early would
22 // result in leaks per unregistering the TLS slot (and thus the DeleteTlsPtr
23 // hook).
24 template <typename T>
25 class CheckedThreadLocalOwnedPointer {
26  public:
27   CheckedThreadLocalOwnedPointer() = default;
28 
~CheckedThreadLocalOwnedPointer()29   ~CheckedThreadLocalOwnedPointer() {
30     Set(nullptr);
31 
32     DCHECK_EQ(num_assigned_threads_.load(std::memory_order_relaxed), 0)
33         << "Memory leak: Must join all threads or release all associated "
34            "thread-local slots before ~ThreadLocalOwnedPointer";
35   }
36 
Get()37   T* Get() const {
38     PtrTracker* const ptr_tracker = static_cast<PtrTracker*>(slot_.Get());
39     return ptr_tracker ? ptr_tracker->ptr_.get() : nullptr;
40   }
41 
Set(std::unique_ptr<T> ptr)42   void Set(std::unique_ptr<T> ptr) {
43     delete static_cast<PtrTracker*>(slot_.Get());
44     if (ptr)
45       slot_.Set(new PtrTracker(this, std::move(ptr)));
46     else
47       slot_.Set(nullptr);
48   }
49 
50  private:
51   struct PtrTracker {
52    public:
PtrTrackerPtrTracker53     PtrTracker(CheckedThreadLocalOwnedPointer<T>* outer, std::unique_ptr<T> ptr)
54         : outer_(outer), ptr_(std::move(ptr)) {
55       outer_->num_assigned_threads_.fetch_add(1, std::memory_order_relaxed);
56     }
57 
~PtrTrackerPtrTracker58     ~PtrTracker() {
59       outer_->num_assigned_threads_.fetch_sub(1, std::memory_order_relaxed);
60     }
61 
62     CheckedThreadLocalOwnedPointer<T>* const outer_;
63     const std::unique_ptr<T> ptr_;
64   };
65 
DeleteTlsPtr(void * ptr)66   static void DeleteTlsPtr(void* ptr) { delete static_cast<PtrTracker*>(ptr); }
67 
68   ThreadLocalStorage::Slot slot_{&DeleteTlsPtr};
69 
70   std::atomic_int num_assigned_threads_{0};
71 
72   DISALLOW_COPY_AND_ASSIGN(CheckedThreadLocalOwnedPointer<T>);
73 };
74 
75 }  // namespace internal
76 }  // namespace base
77 
78 #endif  // DCHECK_IS_ON()
79 
80 #endif  // BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
81