123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_THREADING_SEQUENCE_BOUND_H_
- #define BASE_THREADING_SEQUENCE_BOUND_H_
- #include <new>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/compiler_specific.h"
- #include "base/location.h"
- #include "base/memory/aligned_memory.h"
- #include "base/memory/ptr_util.h"
- #include "base/sequence_checker.h"
- #include "base/sequenced_task_runner.h"
- #include "base/threading/sequence_bound_internal.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- namespace base {
- // Performing blocking work on a different task runner is a common pattern for
- // improving responsiveness of foreground task runners. `SequenceBound<T>`
- // provides an abstraction for an owner object living on the owner sequence, to
- // construct, call methods on, and destroy an object of type T that lives on a
- // different sequence (the bound sequence).
- //
- // This makes it natural for code running on different sequences to be
- // partitioned along class boundaries, e.g.:
- //
- // class Tab {
- // private:
- // void OnScroll() {
- // // ...
- // io_helper_.AsyncCall(&IOHelper::SaveScrollPosition);
- // }
- // SequenceBound<IOHelper> io_helper_{GetBackgroundTaskRunner()};
- // };
- //
- // Note: `SequenceBound<T>` intentionally does not expose a raw pointer to the
- // managed `T` to ensure its internal sequence-safety invariants are not
- // violated. As a result, `AsyncCall()` cannot simply use `base::OnceCallback`
- //
- // SequenceBound also supports replies:
- //
- // class Database {
- // public:
- // int Query(int value) {
- // return value * value;
- // }
- // };
- //
- // // SequenceBound itself is owned on `SequencedTaskRunnerHandle::Get()`.
- // // The managed Database instance managed by it is constructed and owned on
- // // `GetDBTaskRunner()`.
- // SequenceBound<Database> db(GetDBTaskRunner());
- //
- // // `Database::Query()` runs on `GetDBTaskRunner()`, but
- // // `reply_callback` will run on the owner task runner.
- // auto reply_callback = [] (int result) {
- // LOG(ERROR) << result; // Prints 25.
- // };
- // db.AsyncCall(&Database::Query).WithArgs(5)
- // .Then(base::BindOnce(reply_callback));
- //
- // // When `db` goes out of scope, the Database instance will also be
- // // destroyed via a task posted to `GetDBTaskRunner()`.
- //
- // TODO(dcheng): SequenceBound should only be constructed, used, and destroyed
- // on a single sequence. This enforcement will gradually be enabled over time.
- template <typename T>
- class SequenceBound {
- public:
- // Note: on construction, SequenceBound binds to the current sequence. Any
- // subsequent SequenceBound calls (including destruction) must run on that
- // same sequence.
- // Constructs a null SequenceBound with no managed `T`.
- // TODO(dcheng): Add an `Emplace()` method to go with `Reset()`.
- SequenceBound() = default;
- // Schedules asynchronous construction of a new instance of `T` on
- // `task_runner`.
- //
- // Once the SequenceBound constructor completes, the caller can immediately
- // use `AsyncCall()`, et cetera, to schedule work after the construction of
- // `T` on `task_runner`.
- //
- // Marked NO_SANITIZE because cfi doesn't like casting uninitialized memory to
- // `T*`. However, this is safe here because:
- //
- // 1. The cast is well-defined (see https://eel.is/c++draft/basic.life#6) and
- // 2. The resulting pointer is only ever dereferenced on `impl_task_runner_`.
- // By the time SequenceBound's constructor returns, the task to construct
- // `T` will already be posted; thus, subsequent dereference of `t_` on
- // `impl_task_runner_` are safe.
- template <typename... Args>
- NO_SANITIZE("cfi-unrelated-cast")
- SequenceBound(scoped_refptr<base::SequencedTaskRunner> task_runner,
- Args&&... args)
- : impl_task_runner_(std::move(task_runner)) {
- // Allocate space for but do not construct an instance of `T`.
- // AlignedAlloc() requires alignment be a multiple of sizeof(void*).
- storage_ = AlignedAlloc(
- sizeof(T), sizeof(void*) > alignof(T) ? sizeof(void*) : alignof(T));
- t_ = reinterpret_cast<T*>(storage_);
- // Ensure that `t_` will be initialized
- impl_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&ConstructOwnerRecord<Args...>, base::Unretained(t_),
- std::forward<Args>(args)...));
- }
- // If non-null, destruction of the managed `T` is posted to
- // `impl_task_runner_`.`
- ~SequenceBound() { Reset(); }
- // Disallow copy or assignment. SequenceBound has single ownership of the
- // managed `T`.
- SequenceBound(const SequenceBound&) = delete;
- SequenceBound& operator=(const SequenceBound&) = delete;
- // Move construction and assignment.
- SequenceBound(SequenceBound&& other) { MoveRecordFrom(other); }
- SequenceBound& operator=(SequenceBound&& other) {
- Reset();
- MoveRecordFrom(other);
- return *this;
- }
- // Move conversion helpers: allows upcasting from SequenceBound<Derived> to
- // SequenceBound<Base>.
- template <typename From>
- SequenceBound(SequenceBound<From>&& other) {
- MoveRecordFrom(other);
- }
- template <typename From>
- SequenceBound<T>& operator=(SequenceBound<From>&& other) {
- Reset();
- MoveRecordFrom(other);
- return *this;
- }
- // Invokes `method` of the managed `T` on `impl_task_runner_`. May only be
- // used when `is_null()` is false.
- //
- // Basic usage:
- //
- // helper.AsyncCall(&IOHelper::DoWork);
- //
- // If `method` accepts arguments, use of `WithArgs()` to bind them is
- // mandatory:
- //
- // helper.AsyncCall(&IOHelper::DoWorkWithArgs).WithArgs(args);
- //
- // Optionally, use `Then()` to chain to a callback on the owner sequence after
- // `method` completes. If `method` returns a non-void type, the return value
- // will be passed to the chained callback.
- //
- // helper.AsyncCall(&IOHelper::GetValue).Then(std::move(process_result));
- //
- // `WithArgs()` and `Then()` may also be combined:
- //
- // helper.AsyncCall(&IOHelper::GetValueWithArgs).WithArgs(args)
- // .Then(std::move(process_result));
- //
- // but note that ordering is strict: `Then()` must always be last.
- //
- // Note: internally, this is implemented using a series of templated builders.
- // Destruction of the builder may trigger task posting; as a result, using the
- // builder as anything other than a temporary is not allowed.
- //
- // Similarly, triggering lifetime extension of the temporary (e.g. by binding
- // to a const lvalue reference) is not allowed.
- template <typename R, typename... Args>
- auto AsyncCall(R (T::*method)(Args...),
- const Location& location = Location::Current()) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return AsyncCallBuilder<R (T::*)(Args...)>(this, &location, method);
- }
- template <typename R, typename... Args>
- auto AsyncCall(R (T::*method)(Args...) const,
- const Location& location = Location::Current()) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return AsyncCallBuilder<R (T::*)(Args...) const>(this, &location, method);
- }
- // Post a call to `method` to `impl_task_runner_`.
- // TODO(dcheng): Deprecate this in favor of `AsyncCall()`.
- template <typename... MethodArgs, typename... Args>
- void Post(const base::Location& from_here,
- void (T::*method)(MethodArgs...),
- Args&&... args) const {
- DCHECK(t_);
- impl_task_runner_->PostTask(from_here,
- base::BindOnce(method, base::Unretained(t_),
- std::forward<Args>(args)...));
- }
- // Posts `task` to `impl_task_runner_`, passing it a reference to the wrapped
- // object. This allows arbitrary logic to be safely executed on the object's
- // task runner. The object is guaranteed to remain alive for the duration of
- // the task.
- using ConstPostTaskCallback = base::OnceCallback<void(const T&)>;
- void PostTaskWithThisObject(const base::Location& from_here,
- ConstPostTaskCallback callback) const {
- DCHECK(t_);
- impl_task_runner_->PostTask(
- from_here,
- base::BindOnce([](ConstPostTaskCallback callback,
- const T* t) { std::move(callback).Run(*t); },
- std::move(callback), t_));
- }
- // Same as above, but for non-const operations. The callback takes a pointer
- // to the wrapped object rather than a const ref.
- using PostTaskCallback = base::OnceCallback<void(T*)>;
- void PostTaskWithThisObject(const base::Location& from_here,
- PostTaskCallback callback) const {
- DCHECK(t_);
- impl_task_runner_->PostTask(from_here,
- base::BindOnce(std::move(callback), t_));
- }
- // TODO(liberato): Add PostOrCall(), to support cases where synchronous calls
- // are okay if it's the same task runner.
- // Resets `this` to null. If `this` is not currently null, posts destruction
- // of the managed `T` to `impl_task_runner_`.
- void Reset() {
- if (is_null())
- return;
- impl_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&DeleteOwnerRecord, base::Unretained(t_),
- base::Unretained(storage_)));
- impl_task_runner_ = nullptr;
- t_ = nullptr;
- storage_ = nullptr;
- }
- // Same as above, but allows the caller to provide a closure to be invoked
- // immediately after destruction. The Closure is invoked on
- // `impl_task_runner_`, iff the owned object was non-null.
- //
- // TODO(dcheng): Consider removing this; this appears to be used for test
- // synchronization, but that could be achieved by posting
- // `run_loop.QuitClosure()` to the destination sequence after calling
- // `Reset()`.
- void ResetWithCallbackAfterDestruction(base::OnceClosure callback) {
- if (is_null())
- return;
- impl_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(
- [](base::OnceClosure callback, T* t, void* storage) {
- DeleteOwnerRecord(t, storage);
- std::move(callback).Run();
- },
- std::move(callback), t_, storage_));
- impl_task_runner_ = nullptr;
- t_ = nullptr;
- storage_ = nullptr;
- }
- // Return true if `this` is logically null; otherwise, returns false.
- //
- // A SequenceBound is logically null if there is no managed `T`; it is only
- // valid to call `AsyncCall()` on a non-null SequenceBound.
- //
- // Note that the concept of 'logically null' here does not exactly match the
- // lifetime of `T`, which lives on `impl_task_runner_`. In particular, when
- // SequenceBound is first constructed, `is_null()` may return false, even
- // though the lifetime of `T` may not have begun yet on `impl_task_runner_`.
- // Similarly, after `SequenceBound::Reset()`, `is_null()` may return true,
- // even though the lifetime of `T` may not have ended yet on
- // `impl_task_runner_`.
- bool is_null() const { return !t_; }
- // True if `this` is not logically null. See `is_null()`.
- explicit operator bool() const { return !is_null(); }
- private:
- // For move conversion.
- template <typename U>
- friend class SequenceBound;
- // Support helpers for `AsyncCall()` implementation.
- //
- // Several implementation notes:
- // 1. Tasks are posted via destroying the builder or an explicit call to
- // `Then()`.
- //
- // 2. A builder may be consumed by:
- //
- // - calling `Then()`, which immediately posts the task chain
- // - calling `WithArgs()`, which returns a new builder with the captured
- // arguments
- //
- // Builders that are consumed have the internal `sequence_bound_` field
- // nulled out; the hope is the compiler can see this and use it to
- // eliminate dead branches (e.g. correctness checks that aren't needed
- // since the code can be statically proved correct).
- //
- // 3. Builder methods are rvalue-qualified to try to enforce that the builder
- // is only used as a temporary. Note that this only helps so much; nothing
- // prevents a determined caller from using `std::move()` to force calls to
- // a non-temporary instance.
- //
- // TODO(dcheng): It might also be possible to use Gmock-style matcher
- // composition, e.g. something like:
- //
- // sb.AsyncCall(&Helper::DoWork, WithArgs(args),
- // Then(std::move(process_result));
- //
- // In theory, this might allow the elimination of magic destructors and
- // better static checking by the compiler.
- template <typename MethodPtrType>
- class AsyncCallBuilderBase {
- protected:
- AsyncCallBuilderBase(SequenceBound* sequence_bound,
- const Location* location,
- MethodPtrType method)
- : sequence_bound_(sequence_bound),
- location_(location),
- method_(method) {
- // Common entry point for `AsyncCall()`, so check preconditions here.
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_bound_->sequence_checker_);
- DCHECK(sequence_bound_->t_);
- }
- AsyncCallBuilderBase(AsyncCallBuilderBase&&) = default;
- AsyncCallBuilderBase& operator=(AsyncCallBuilderBase&&) = default;
- // `sequence_bound_` is consumed and set to `nullptr` when `Then()` is
- // invoked. This is used as a flag for two potential states
- //
- // - if a method returns void, invoking `Then()` is optional. The destructor
- // will check if `sequence_bound_` is null; if it is, `Then()` was
- // already invoked and the task chain has already been posted, so the
- // destructor does not need to do anything. Otherwise, the destructor
- // needs to post the task to make the async call. In theory, the compiler
- // should be able to eliminate this branch based on the presence or
- // absence of a call to `Then()`.
- //
- // - if a method returns a non-void type, `Then()` *must* be invoked. The
- // destructor will `CHECK()` if `sequence_bound_` is non-null, since that
- // indicates `Then()` was not invoked. Similarly, note this branch should
- // be eliminated by the optimizer if the code is free of bugs. :)
- SequenceBound* sequence_bound_;
- // Subtle: this typically points at a Location *temporary*. This is used to
- // try to detect errors resulting from lifetime extension of the async call
- // factory temporaries, since the factory destructors can perform work. If
- // the lifetime of the factory is incorrectly extended, dereferencing
- // `location_` will trigger a stack-use-after-scope when running with ASan.
- const Location* const location_;
- MethodPtrType method_;
- };
- template <typename MethodPtrType, typename ReturnType, typename... Args>
- class AsyncCallBuilderImpl;
- // Selected method has no arguments and returns void.
- template <typename MethodPtrType>
- class AsyncCallBuilderImpl<MethodPtrType, void, std::tuple<>>
- : public AsyncCallBuilderBase<MethodPtrType> {
- public:
- // Note: despite being here, this is actually still protected, since it is
- // protected on the base class.
- using AsyncCallBuilderBase<MethodPtrType>::AsyncCallBuilderBase;
- ~AsyncCallBuilderImpl() {
- if (this->sequence_bound_) {
- this->sequence_bound_->impl_task_runner_->PostTask(
- *this->location_,
- BindOnce(this->method_, Unretained(this->sequence_bound_->t_)));
- }
- }
- void Then(OnceClosure then_callback) && {
- this->sequence_bound_->PostTaskAndThenHelper(
- *this->location_,
- BindOnce(this->method_, Unretained(this->sequence_bound_->t_)),
- std::move(then_callback));
- this->sequence_bound_ = nullptr;
- }
- private:
- friend SequenceBound;
- AsyncCallBuilderImpl(AsyncCallBuilderImpl&&) = default;
- AsyncCallBuilderImpl& operator=(AsyncCallBuilderImpl&&) = default;
- };
- // Selected method has no arguments and returns non-void.
- template <typename MethodPtrType, typename ReturnType>
- class AsyncCallBuilderImpl<MethodPtrType, ReturnType, std::tuple<>>
- : public AsyncCallBuilderBase<MethodPtrType> {
- public:
- // Note: despite being here, this is actually still protected, since it is
- // protected on the base class.
- using AsyncCallBuilderBase<MethodPtrType>::AsyncCallBuilderBase;
- ~AsyncCallBuilderImpl() {
- // Must use Then() since the method's return type is not void.
- // Should be optimized out if the code is bug-free.
- CHECK(!this->sequence_bound_)
- << "Then() not invoked for a method that returns a non-void type; "
- << "make sure to invoke Then() or use base::IgnoreResult()";
- }
- template <template <typename> class CallbackType,
- typename ThenArg,
- typename = EnableIfIsBaseCallback<CallbackType>>
- void Then(CallbackType<void(ThenArg)> then_callback) && {
- this->sequence_bound_->PostTaskAndThenHelper(
- *this->location_,
- BindOnce(this->method_, Unretained(this->sequence_bound_->t_)),
- std::move(then_callback));
- this->sequence_bound_ = nullptr;
- }
- private:
- friend SequenceBound;
- AsyncCallBuilderImpl(AsyncCallBuilderImpl&&) = default;
- AsyncCallBuilderImpl& operator=(AsyncCallBuilderImpl&&) = default;
- };
- // Selected method has arguments. Return type can be void or non-void.
- template <typename MethodPtrType, typename ReturnType, typename... Args>
- class AsyncCallBuilderImpl<MethodPtrType, ReturnType, std::tuple<Args...>>
- : public AsyncCallBuilderBase<MethodPtrType> {
- public:
- // Note: despite being here, this is actually still protected, since it is
- // protected on the base class.
- using AsyncCallBuilderBase<MethodPtrType>::AsyncCallBuilderBase;
- ~AsyncCallBuilderImpl() {
- // Must use WithArgs() since the method takes arguments.
- // Should be optimized out if the code is bug-free.
- CHECK(!this->sequence_bound_);
- }
- template <typename... BoundArgs>
- auto WithArgs(BoundArgs&&... bound_args) {
- SequenceBound* const sequence_bound =
- std::exchange(this->sequence_bound_, nullptr);
- return AsyncCallWithBoundArgsBuilder<ReturnType>(
- sequence_bound, this->location_,
- BindOnce(this->method_, Unretained(sequence_bound->t_),
- std::forward<BoundArgs>(bound_args)...));
- }
- private:
- friend SequenceBound;
- AsyncCallBuilderImpl(AsyncCallBuilderImpl&&) = default;
- AsyncCallBuilderImpl& operator=(AsyncCallBuilderImpl&&) = default;
- };
- template <typename MethodPtrType,
- typename R = internal::ExtractMethodReturnType<MethodPtrType>,
- typename ArgsTuple =
- internal::ExtractMethodArgsTuple<MethodPtrType>>
- using AsyncCallBuilder = AsyncCallBuilderImpl<MethodPtrType, R, ArgsTuple>;
- // Support factories when arguments are bound using `WithArgs()`. These
- // factories don't need to handle raw method pointers, since everything has
- // already been packaged into a base::OnceCallback.
- template <typename ReturnType>
- class AsyncCallWithBoundArgsBuilderBase {
- protected:
- AsyncCallWithBoundArgsBuilderBase(SequenceBound* sequence_bound,
- const Location* location,
- base::OnceCallback<ReturnType()> callback)
- : sequence_bound_(sequence_bound),
- location_(location),
- callback_(std::move(callback)) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_bound_->sequence_checker_);
- DCHECK(sequence_bound_->t_);
- }
- // Subtle: the internal helpers rely on move elision. Preventing move
- // elision (e.g. using `std::move()` when returning the temporary) will
- // trigger a `CHECK()` since `sequence_bound_` is not reset to nullptr on
- // move.
- AsyncCallWithBoundArgsBuilderBase(
- AsyncCallWithBoundArgsBuilderBase&&) noexcept = default;
- AsyncCallWithBoundArgsBuilderBase& operator=(
- AsyncCallWithBoundArgsBuilderBase&&) noexcept = default;
- SequenceBound* sequence_bound_;
- const Location* const location_;
- base::OnceCallback<ReturnType()> callback_;
- };
- // Note: this doesn't handle a void return type, which has an explicit
- // specialization below.
- template <typename ReturnType>
- class AsyncCallWithBoundArgsBuilderDefault
- : public AsyncCallWithBoundArgsBuilderBase<ReturnType> {
- public:
- ~AsyncCallWithBoundArgsBuilderDefault() {
- // Must use Then() since the method's return type is not void.
- // Should be optimized out if the code is bug-free.
- CHECK(!this->sequence_bound_);
- }
- template <template <typename> class CallbackType,
- typename ThenArg,
- typename = EnableIfIsBaseCallback<CallbackType>>
- void Then(CallbackType<void(ThenArg)> then_callback) && {
- this->sequence_bound_->PostTaskAndThenHelper(*this->location_,
- std::move(this->callback_),
- std::move(then_callback));
- this->sequence_bound_ = nullptr;
- }
- protected:
- using AsyncCallWithBoundArgsBuilderBase<
- ReturnType>::AsyncCallWithBoundArgsBuilderBase;
- private:
- friend SequenceBound;
- AsyncCallWithBoundArgsBuilderDefault(
- AsyncCallWithBoundArgsBuilderDefault&&) = default;
- AsyncCallWithBoundArgsBuilderDefault& operator=(
- AsyncCallWithBoundArgsBuilderDefault&&) = default;
- };
- class AsyncCallWithBoundArgsBuilderVoid
- : public AsyncCallWithBoundArgsBuilderBase<void> {
- public:
- // Note: despite being here, this is actually still protected, since it is
- // protected on the base class.
- using AsyncCallWithBoundArgsBuilderBase<
- void>::AsyncCallWithBoundArgsBuilderBase;
- ~AsyncCallWithBoundArgsBuilderVoid() {
- if (this->sequence_bound_) {
- this->sequence_bound_->impl_task_runner_->PostTask(
- *this->location_, std::move(this->callback_));
- }
- }
- void Then(OnceClosure then_callback) && {
- this->sequence_bound_->PostTaskAndThenHelper(*this->location_,
- std::move(this->callback_),
- std::move(then_callback));
- this->sequence_bound_ = nullptr;
- }
- private:
- friend SequenceBound;
- AsyncCallWithBoundArgsBuilderVoid(AsyncCallWithBoundArgsBuilderVoid&&) =
- default;
- AsyncCallWithBoundArgsBuilderVoid& operator=(
- AsyncCallWithBoundArgsBuilderVoid&&) = default;
- };
- template <typename ReturnType>
- using AsyncCallWithBoundArgsBuilder = typename std::conditional<
- std::is_void<ReturnType>::value,
- AsyncCallWithBoundArgsBuilderVoid,
- AsyncCallWithBoundArgsBuilderDefault<ReturnType>>::type;
- void PostTaskAndThenHelper(const Location& location,
- OnceCallback<void()> callback,
- OnceClosure then_callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- impl_task_runner_->PostTaskAndReply(location, std::move(callback),
- std::move(then_callback));
- }
- template <typename ReturnType,
- template <typename>
- class CallbackType,
- typename ThenArg,
- typename = EnableIfIsBaseCallback<CallbackType>>
- void PostTaskAndThenHelper(const Location& location,
- OnceCallback<ReturnType()> callback,
- CallbackType<void(ThenArg)> then_callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- OnceCallback<void(ThenArg)>&& once_then_callback = std::move(then_callback);
- impl_task_runner_->PostTaskAndReplyWithResult(
- location, std::move(callback), std::move(once_then_callback));
- }
- // Helper to support move construction and move assignment.
- //
- // Marked NO_SANITIZE since:
- // 1. SequenceBound can be moved before `t_` is constructed on
- // `impl_task_runner_` but
- // 2. Implicit conversions to non-virtual base classes are allowed before the
- // lifetime of `t_` has started (see https://eel.is/c++draft/basic.life#6).
- template <typename From>
- void NO_SANITIZE("cfi-unrelated-cast") MoveRecordFrom(From&& other) {
- // TODO(dcheng): Consider adding a static_assert to provide a friendlier
- // error message.
- impl_task_runner_ = std::move(other.impl_task_runner_);
- // Subtle: this must not use static_cast<>, since the lifetime of the
- // managed `T` may not have begun yet. However, the standard explicitly
- // still allows implicit conversion to a non-virtual base class.
- t_ = std::exchange(other.t_, nullptr);
- storage_ = std::exchange(other.storage_, nullptr);
- }
- // Pointer to the managed `T`. This field is only read and written on
- // the sequence associated with `sequence_checker_`.
- T* t_ = nullptr;
- // Storage originally allocated by `AlignedAlloc()`. Maintained separately
- // from `t_` since the original, unadjusted pointer needs to be passed to
- // `AlignedFree()`.
- void* storage_ = nullptr;
- SEQUENCE_CHECKER(sequence_checker_);
- // Task runner which manages `t_`. `t_` is constructed, destroyed, and
- // dereferenced only on this task runner.
- scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
- // Helpers for constructing and destroying `T` on `impl_task_runner_`.
- template <typename... Args>
- static void ConstructOwnerRecord(T* t, std::decay_t<Args>&&... args) {
- new (t) T(std::move(args)...);
- }
- static void DeleteOwnerRecord(T* t, void* storage) {
- t->~T();
- AlignedFree(storage);
- }
- };
- } // namespace base
- #endif // BASE_THREADING_SEQUENCE_BOUND_H_
|