• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   using EnableIfIsCrossThreadTask = EnableIfIsBaseCallback<CallbackType>;
65 };
66 
67 template <typename T, typename CrossThreadTraits>
68 class Storage {
69  public:
70   using Ptr = T*;
71 
get()72   Ptr get() const { return ptr_; }
73 
74   // Marked NO_SANITIZE because cfi doesn't like casting uninitialized memory to
75   // `T*`. However, this is safe here because:
76   //
77   // 1. The cast is well-defined (see https://eel.is/c++draft/basic.life#6) and
78   // 2. The resulting pointer is only ever dereferenced on `task_runner`.
79   //    By the time SequenceBound's constructor returns, the task to construct
80   //    `T` will already be posted; thus, subsequent dereference of `ptr_` on
81   //    `task_runner` are safe.
82   template <typename... Args>
83   NO_SANITIZE("cfi-unrelated-cast")
Construct(SequencedTaskRunner & task_runner,Args &&...args)84   void Construct(SequencedTaskRunner& task_runner, Args&&... args) {
85     // TODO(https://crbug.com/1382549): Use universal forwarding and assert that
86     // T is constructible from args for better error messages.
87     DCHECK(!alloc_);
88     DCHECK(!ptr_);
89 
90     // Allocate space for but do not construct an instance of `T`.
91     // AlignedAlloc() requires alignment be a multiple of sizeof(void*).
92     alloc_ = AlignedAlloc(
93         sizeof(T), sizeof(void*) > alignof(T) ? sizeof(void*) : alignof(T));
94     ptr_ = reinterpret_cast<Ptr>(alloc_.get());
95 
96     // Ensure that `ptr_` will be initialized.
97     CrossThreadTraits::PostTask(
98         task_runner, FROM_HERE,
99         CrossThreadTraits::BindOnce(&InternalConstruct<Args...>,
100                                     CrossThreadTraits::Unretained(ptr_),
101                                     std::forward<Args>(args)...));
102   }
103 
104   // Marked NO_SANITIZE since:
105   // 1. SequenceBound can be moved before `ptr_` is constructed on its managing
106   //    `SequencedTaskRunner` but
107   // 2. Implicit conversions to non-virtual base classes are allowed before the
108   //    lifetime of the object that `ptr_` points at has begun (see
109   //    https://eel.is/c++draft/basic.life#6).
110   template <typename U>
111   NO_SANITIZE("cfi-unrelated-cast")
TakeFrom(Storage<U,CrossThreadTraits> && other)112   void TakeFrom(Storage<U, CrossThreadTraits>&& other) {
113     // Subtle: this must not use static_cast<>, since the lifetime of the
114     // managed `T` may not have begun yet. However, the standard explicitly
115     // still allows implicit conversion to a non-virtual base class.
116     ptr_ = std::exchange(other.ptr_, nullptr);
117     alloc_ = std::exchange(other.alloc_, nullptr);
118   }
119 
Destruct(SequencedTaskRunner & task_runner)120   void Destruct(SequencedTaskRunner& task_runner) {
121     CrossThreadTraits::PostTask(
122         task_runner, FROM_HERE,
123         CrossThreadTraits::BindOnce(
124             &InternalDestruct, CrossThreadTraits::Unretained(ptr_),
125             CrossThreadTraits::Unretained(alloc_.get())));
126     ptr_ = nullptr;
127     alloc_ = nullptr;
128   }
129 
130  private:
131   // Needed to allow conversions from compatible `U`s.
132   template <typename U, typename V>
133   friend class Storage;
134 
135   // Helpers for constructing and destroying `T` on its managing
136   // `SequencedTaskRunner`.
137   template <typename... Args>
InternalConstruct(T * ptr,std::decay_t<Args> &&...args)138   static void InternalConstruct(T* ptr, std::decay_t<Args>&&... args) {
139     new (ptr) T(std::move(args)...);
140   }
141 
InternalDestruct(T * ptr,void * alloc)142   static void InternalDestruct(T* ptr, void* alloc) {
143     ptr->~T();
144     AlignedFree(alloc);
145   }
146 
147   // Pointer to the managed `T`.
148   Ptr ptr_ = nullptr;
149 
150   // Storage originally allocated by `AlignedAlloc()`. Maintained separately
151   // from  `ptr_` since the original, unadjusted pointer needs to be passed to
152   // `AlignedFree()`.
153   raw_ptr<void, DanglingUntriaged> alloc_ = nullptr;
154 };
155 
156 template <typename T, typename CrossThreadTraits>
157 struct Storage<std::unique_ptr<T>, CrossThreadTraits> {
158  public:
159   using Ptr = T*;
160 
161   Ptr get() const { return ptr_; }
162 
163   template <typename U>
164   void Construct(SequencedTaskRunner& task_runner, std::unique_ptr<U> arg) {
165     // TODO(https://crbug.com/1382549): Use universal forwarding and assert that
166     // there is one arg that is a unique_ptr for better error messages.
167     DCHECK(!ptr_);
168 
169     ptr_ = arg.release();
170     // No additional storage needs to be allocated since `T` is already
171     // constructed and lives on the heap.
172   }
173 
174   template <typename U>
175   void TakeFrom(Storage<std::unique_ptr<U>, CrossThreadTraits>&& other) {
176     ptr_ = std::exchange(other.ptr_, nullptr);
177   }
178 
179   void Destruct(SequencedTaskRunner& task_runner) {
180     CrossThreadTraits::PostTask(
181         task_runner, FROM_HERE,
182         CrossThreadTraits::BindOnce(&InternalDestruct,
183                                     CrossThreadTraits::Unretained(ptr_)));
184 
185     ptr_ = nullptr;
186   }
187 
188  private:
189   // Needed to allow conversions from compatible `U`s.
190   template <typename U, typename V>
191   friend class Storage;
192 
193   static void InternalDestruct(T* ptr) { delete ptr; }
194 
195   // Pointer to the heap-allocated `T`.
196   Ptr ptr_ = nullptr;
197 };
198 
199 }  // namespace base::sequence_bound_internal
200 
201 #endif  // BASE_THREADING_SEQUENCE_BOUND_INTERNAL_H_
202