123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- // 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 <type_traits>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/compiler_specific.h"
- #include "base/location.h"
- #include "base/memory/aligned_memory.h"
- #include "base/memory/ptr_util.h"
- #include "base/sequenced_task_runner.h"
- namespace base {
- // SequenceBound facilitates owning objects that live on a specified sequence,
- // which is potentially different than the owner's sequence. It encapsulates
- // the work of posting tasks to the specified sequence to construct T, call
- // methods on T, and destroy T.
- //
- // It does not provide explicit access to the underlying object directly, to
- // prevent accidentally using it from the wrong sequence.
- //
- // Like std::unique_ptr<T>, a SequenceBound<T> may be moved between owners,
- // and posted across threads. It may also be up-casted (only), to permit
- // SequenceBound to be used with interfaces.
- //
- // Basic usage looks like this:
- //
- // // Some class that lives on |main_task_runner|.
- // class MyClass {
- // public:
- // explicit MyClass(const char* widget_title) {}
- // virtual ~MyClass() { ... }
- // virtual void DoSomething(int arg) { ... }
- // };
- //
- // // On any thread...
- // scoped_refptr<SequencedTaskRunner> main_task_runner = ...;
- // auto widget = SequenceBound<MyClass>(main_task_runner, "My Title");
- //
- // // Execute a single method on the object, on |main_task_runner|.
- // widget.Post(FROM_HERE, &MyClass::DoSomething, 1234);
- //
- // // Execute an arbitrary task on |main_task_runner| with a non-const pointer
- // // to the object.
- // widget.PostTaskWithThisObject(
- // FROM_HERE,
- // base::BindOnce([](MyClass* widget) {
- // // Unlike with Post, we can issue multiple calls on |widget| within
- // // the same stack frame.
- // widget->DoSomething(42);
- // widget->DoSomething(13);
- // }));
- //
- // // Execute an arbitrary task on |main_task_runner| with a const reference
- // // to the object.
- // widget.PostTaskWithThisObject(
- // FROM_HERE,
- // base::BindOnce([](const MyClass& widget) { ... }));
- //
- // Note that |widget| is constructed asynchronously on |main_task_runner|,
- // but calling Post() immediately is safe, since the actual call is posted
- // to |main_task_runner| as well.
- //
- // |widget| will be deleted on |main_task_runner| asynchronously when it goes
- // out of scope, or when Reset() is called.
- //
- // Here is a more complicated example that shows injection and upcasting:
- //
- // // Some unrelated class that uses a |MyClass| to do something.
- // class SomeConsumer {
- // public:
- // // Note that ownership of |widget| is given to us!
- // explicit SomeConsumer(SequenceBound<MyClass> widget)
- // : widget_(std::move(widget)) { ... }
- //
- // ~SomeConsumer() {
- // // |widget_| will be destroyed on the associated task runner.
- // }
- //
- // SequenceBound<MyClass> widget_;
- // };
- //
- // // Implementation of MyClass.
- // class MyDerivedClass : public MyClass { ... };
- //
- // auto widget =
- // SequenceBound<MyDerivedClass>(main_task_runner, ctor args);
- // auto c = new SomeConsumer(std::move(widget)); // upcasts to MyClass
- namespace internal {
- // If we can't cast |Base*| into |Derived*|, then it's a virtual base if and
- // only if |Base| is actually a base class of |Derived|. Otherwise (including
- // unrelated types), it isn't. We default to Derived* so that the
- // specialization below will apply when the cast to |Derived*| is valid.
- template <typename Base, typename Derived, typename = Derived*>
- struct is_virtual_base_of : public std::is_base_of<Base, Derived> {};
- // If we can cast |Base*| into |Derived*|, then it's definitely not a virtual
- // base. When this happens, we'll match the default third template argument.
- template <typename Base, typename Derived>
- struct is_virtual_base_of<Base,
- Derived,
- decltype(static_cast<Derived*>(
- static_cast<Base*>(nullptr)))> : std::false_type {
- };
- } // namespace internal
- template <typename T>
- class SequenceBound {
- public:
- // Allow explicit null.
- SequenceBound() = default;
- // Construct a new instance of |T| that will be accessed only on
- // |task_runner|. One may post calls to it immediately upon return.
- // This is marked as NO_SANITIZE because cfi doesn't like that we're casting
- // uninitialized memory to a |T*|. However, it's safe since (a) the cast is
- // defined (see http://eel.is/c++draft/basic.life#6 for details), and (b) we
- // don't use the resulting pointer in any way that requries it to be
- // constructed, except by posting such a access to |impl_task_runner_| after
- // posting construction there as well.
- 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|.
- storage_ = AlignedAlloc(sizeof(T), alignof(T));
- t_ = reinterpret_cast<T*>(storage_);
- // Post construction to the impl thread.
- impl_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&ConstructOwnerRecord<Args...>, base::Unretained(t_),
- std::forward<Args>(args)...));
- }
- ~SequenceBound() { Reset(); }
- // Move construction from the same type can just take the pointer without
- // adjusting anything. This is required in addition to the move conversion
- // constructor below.
- SequenceBound(SequenceBound&& other) { MoveRecordFrom(other); }
- // Move construction is supported from any type that's compatible with |T|.
- // This case handles |From| != |T|, so we must adjust the pointer offset.
- template <typename From>
- SequenceBound(SequenceBound<From>&& other) {
- MoveRecordFrom(other);
- }
- SequenceBound& operator=(SequenceBound&& other) {
- // Clean up any object we currently own.
- Reset();
- MoveRecordFrom(other);
- return *this;
- }
- template <typename From>
- SequenceBound<T>& operator=(SequenceBound<From>&& other) {
- // Clean up any object that we currently own.
- Reset();
- MoveRecordFrom(other);
- return *this;
- }
- // Post a call to |method| to |impl_task_runner_|.
- 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.
- // TODO(liberato): Add PostAndReply()
- // TODO(liberato): Allow creation of callbacks that bind to a weak pointer,
- // and thread-hop to |impl_task_runner_| if needed.
- // Post destruction of any object we own, and return to the null state.
- void Reset() {
- if (is_null())
- return;
- // Destruct the object on the impl thread.
- 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.
- 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 whether we own anything. Note that this does not guarantee that any
- // previously owned object has been destroyed. In particular, it will return
- // true immediately after a call to Reset(), though the underlying object
- // might still be pending destruction on the impl thread.
- bool is_null() const { return !t_; }
- // True if and only if we have an object, with the same caveats as is_null().
- explicit operator bool() const { return !is_null(); }
- private:
- // Move everything from |other|, doing pointer adjustment as needed.
- // This method is marked as NO_SANITIZE since (a) it might run before the
- // posted ctor runs on |impl_task_runner_|, and (b) implicit conversions to
- // non-virtual base classes are allowed before construction by the standard.
- // See http://eel.is/c++draft/basic.life#6 for more information.
- template <typename From>
- void NO_SANITIZE("cfi-unrelated-cast") MoveRecordFrom(From&& other) {
- // |other| might be is_null(), but that's okay.
- impl_task_runner_ = std::move(other.impl_task_runner_);
- // Note that static_cast<> isn't, in general, safe, since |other| might not
- // be constructed yet. Implicit conversion is supported, as long as it
- // doesn't convert to a virtual base. Of course, it allows only upcasts.
- t_ = other.t_;
- // The original storage is kept unmodified, so we can free it later.
- storage_ = other.storage_;
- other.storage_ = nullptr;
- other.t_ = nullptr;
- }
- // Pointer to the object, Pointer may be modified on the owning thread.
- T* t_ = nullptr;
- // Original allocated storage for the object.
- void* storage_ = nullptr;
- // The task runner on which all access to |t_| should happen.
- scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
- // For move conversion.
- template <typename U>
- friend class SequenceBound;
- // Run on impl thread to construct |t|'s storage.
- template <typename... Args>
- static void ConstructOwnerRecord(T* t, std::decay_t<Args>&&... args) {
- new (t) T(std::move(args)...);
- }
- // Destruct the object associated with |t|, and delete |storage|.
- static void DeleteOwnerRecord(T* t, void* storage) {
- t->~T();
- AlignedFree(storage);
- }
- // To preserve ownership semantics, we disallow copy construction / copy
- // assignment. Move construction / assignment is fine.
- DISALLOW_COPY_AND_ASSIGN(SequenceBound);
- };
- } // namespace base
- #endif // BASE_THREADING_SEQUENCE_BOUND_H_
|