xref: /aosp_15_r20/external/cronet/base/threading/sequence_bound_internal.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2022 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_SEQUENCE_BOUND_INTERNAL_H_
6 #define BASE_THREADING_SEQUENCE_BOUND_INTERNAL_H_
7 
8 #include <memory>
9 #include <type_traits>
10 #include <utility>
11 
12 #include "base/compiler_specific.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/functional/callback_helpers.h"
16 #include "base/location.h"
17 #include "base/memory/aligned_memory.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/task/sequenced_task_runner.h"
20 
21 namespace base::sequence_bound_internal {
22 
23 struct CrossThreadTraits {
24   template <typename Signature>
25   using CrossThreadTask = OnceCallback<Signature>;
26 
27   template <typename Functor, typename... Args>
BindOnceCrossThreadTraits28   static inline auto BindOnce(Functor&& functor, Args&&... args) {
29     return ::base::BindOnce(std::forward<Functor>(functor),
30                             std::forward<Args>(args)...);
31   }
32 
33   template <typename T>
UnretainedCrossThreadTraits34   static inline auto Unretained(T ptr) {
35     return ::base::Unretained(ptr);
36   }
37 
PostTaskCrossThreadTraits38   static inline bool PostTask(SequencedTaskRunner& task_runner,
39                               const Location& location,
40                               OnceClosure&& task) {
41     return task_runner.PostTask(location, std::move(task));
42   }
43 
PostTaskAndReplyCrossThreadTraits44   static inline bool PostTaskAndReply(SequencedTaskRunner& task_runner,
45                                       const Location& location,
46                                       OnceClosure&& task,
47                                       OnceClosure&& reply) {
48     return task_runner.PostTaskAndReply(location, std::move(task),
49                                         std::move(reply));
50   }
51 
52   template <typename TaskReturnType, typename ReplyArgType>
PostTaskAndReplyWithResultCrossThreadTraits53   static inline bool PostTaskAndReplyWithResult(
54       SequencedTaskRunner& task_runner,
55       const Location& location,
56       OnceCallback<TaskReturnType()>&& task,
57       OnceCallback<void(ReplyArgType)>&& reply) {
58     return task_runner.PostTaskAndReplyWithResult(location, std::move(task),
59                                                   std::move(reply));
60   }
61 
62   // Accept RepeatingCallback here since it's convertible to a OnceCallback.
63   template <template <typename> class CallbackType>
64   static constexpr bool IsCrossThreadTask =
65       IsBaseCallback<CallbackType<void()>>;
66 };
67 
68 template <typename T, typename CrossThreadTraits>
69 class Storage {
70  public:
71   using element_type = T;
72 
is_null()73   bool is_null() const { return ptr_ == nullptr; }
GetPtrForBind()74   auto GetPtrForBind() const { return CrossThreadTraits::Unretained(ptr_); }
GetRefForBind()75   auto GetRefForBind() const { return std::cref(*ptr_); }
76 
77   // Marked NO_SANITIZE because cfi doesn't like casting uninitialized memory to
78   // `T*`. However, this is safe here because:
79   //
80   // 1. The cast is well-defined (see https://eel.is/c++draft/basic.life#6) and
81   // 2. The resulting pointer is only ever dereferenced on `task_runner`.
82   //    By the time SequenceBound's constructor returns, the task to construct
83   //    `T` will already be posted; thus, subsequent dereference of `ptr_` on
84   //    `task_runner` are safe.
85   template <typename... Args>
86   NO_SANITIZE("cfi-unrelated-cast")
Construct(SequencedTaskRunner & task_runner,Args &&...args)87   void Construct(SequencedTaskRunner& task_runner, Args&&... args) {
88     // TODO(https://crbug.com/1382549): Use universal forwarding and assert that
89     // T is constructible from args for better error messages.
90     DCHECK(!alloc_);
91     DCHECK(!ptr_);
92 
93     // Allocate space for but do not construct an instance of `T`.
94     // AlignedAlloc() requires alignment be a multiple of sizeof(void*).
95     alloc_ = AlignedAlloc(
96         sizeof(T), sizeof(void*) > alignof(T) ? sizeof(void*) : alignof(T));
97     ptr_ = reinterpret_cast<T*>(alloc_.get());
98 
99     // Ensure that `ptr_` will be initialized.
100     CrossThreadTraits::PostTask(
101         task_runner, FROM_HERE,
102         CrossThreadTraits::BindOnce(&InternalConstruct<Args...>,
103                                     CrossThreadTraits::Unretained(ptr_),
104                                     std::forward<Args>(args)...));
105   }
106 
107   // Marked NO_SANITIZE since:
108   // 1. SequenceBound can be moved before `ptr_` is constructed on its managing
109   //    `SequencedTaskRunner` but
110   // 2. Implicit conversions to non-virtual base classes are allowed before the
111   //    lifetime of the object that `ptr_` points at has begun (see
112   //    https://eel.is/c++draft/basic.life#6).
113   template <typename U>
114   NO_SANITIZE("cfi-unrelated-cast")
TakeFrom(Storage<U,CrossThreadTraits> && other)115   void TakeFrom(Storage<U, CrossThreadTraits>&& other) {
116     // Subtle: this must not use static_cast<>, since the lifetime of the
117     // managed `T` may not have begun yet. However, the standard explicitly
118     // still allows implicit conversion to a non-virtual base class.
119     ptr_ = std::exchange(other.ptr_, nullptr);
120     alloc_ = std::exchange(other.alloc_, nullptr);
121   }
122 
Destruct(SequencedTaskRunner & task_runner)123   void Destruct(SequencedTaskRunner& task_runner) {
124     CrossThreadTraits::PostTask(
125         task_runner, FROM_HERE,
126         CrossThreadTraits::BindOnce(
127             &InternalDestruct,
128             CrossThreadTraits::Unretained(std::exchange(ptr_, nullptr)),
129             CrossThreadTraits::Unretained(std::exchange(alloc_, nullptr))));
130   }
131 
132  private:
133   // Needed to allow conversions from compatible `U`s.
134   template <typename U, typename V>
135   friend class Storage;
136 
137   // Helpers for constructing and destroying `T` on its managing
138   // `SequencedTaskRunner`.
139   template <typename... Args>
InternalConstruct(T * ptr,std::decay_t<Args> &&...args)140   static void InternalConstruct(T* ptr, std::decay_t<Args>&&... args) {
141     new (ptr) T(std::move(args)...);
142   }
143 
InternalDestruct(T * ptr,void * alloc)144   static void InternalDestruct(T* ptr, void* alloc) {
145     ptr->~T();
146     AlignedFree(alloc);
147   }
148 
149   // Pointer to the managed `T`.
150   raw_ptr<T> ptr_ = nullptr;
151 
152   // Storage originally allocated by `AlignedAlloc()`. Maintained separately
153   // from  `ptr_` since the original, unadjusted pointer needs to be passed to
154   // `AlignedFree()`.
155   raw_ptr<void> alloc_ = nullptr;
156 };
157 
158 template <typename T, typename CrossThreadTraits>
159 struct Storage<std::unique_ptr<T>, CrossThreadTraits> {
160  public:
161   using element_type = T;
162 
163   bool is_null() const { return ptr_ == nullptr; }
164   auto GetPtrForBind() const { return CrossThreadTraits::Unretained(ptr_); }
165   auto GetRefForBind() const { return std::cref(*ptr_); }
166 
167   template <typename U>
168   void Construct(SequencedTaskRunner& task_runner, std::unique_ptr<U> arg) {
169     // TODO(https://crbug.com/1382549): Use universal forwarding and assert that
170     // there is one arg that is a unique_ptr for better error messages.
171     DCHECK(!ptr_);
172 
173     ptr_ = arg.release();
174     // No additional storage needs to be allocated since `T` is already
175     // constructed and lives on the heap.
176   }
177 
178   template <typename U>
179   void TakeFrom(Storage<std::unique_ptr<U>, CrossThreadTraits>&& other) {
180     ptr_ = std::exchange(other.ptr_, nullptr);
181   }
182 
183   void Destruct(SequencedTaskRunner& task_runner) {
184     CrossThreadTraits::PostTask(
185         task_runner, FROM_HERE,
186         CrossThreadTraits::BindOnce(
187             &InternalDestruct,
188             CrossThreadTraits::Unretained(std::exchange(ptr_, nullptr))));
189   }
190 
191  private:
192   // Needed to allow conversions from compatible `U`s.
193   template <typename U, typename V>
194   friend class Storage;
195 
196   static void InternalDestruct(T* ptr) { delete ptr; }
197 
198   // Pointer to the heap-allocated `T`.
199   raw_ptr<T> ptr_ = nullptr;
200 };
201 
202 }  // namespace base::sequence_bound_internal
203 
204 #endif  // BASE_THREADING_SEQUENCE_BOUND_INTERNAL_H_
205