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