// 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 #include #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, a SequenceBound 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 main_task_runner = ...; // auto widget = SequenceBound(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 widget) // : widget_(std::move(widget)) { ... } // // ~SomeConsumer() { // // |widget_| will be destroyed on the associated task runner. // } // // SequenceBound widget_; // }; // // // Implementation of MyClass. // class MyDerivedClass : public MyClass { ... }; // // auto widget = // SequenceBound(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 struct is_virtual_base_of : public std::is_base_of {}; // 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 struct is_virtual_base_of( static_cast(nullptr)))> : std::false_type { }; } // namespace internal template 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 NO_SANITIZE("cfi-unrelated-cast") SequenceBound(scoped_refptr 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(storage_); // Post construction to the impl thread. impl_task_runner_->PostTask( FROM_HERE, base::BindOnce(&ConstructOwnerRecord, base::Unretained(t_), std::forward(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 SequenceBound(SequenceBound&& other) { MoveRecordFrom(other); } SequenceBound& operator=(SequenceBound&& other) { // Clean up any object we currently own. Reset(); MoveRecordFrom(other); return *this; } template SequenceBound& operator=(SequenceBound&& other) { // Clean up any object that we currently own. Reset(); MoveRecordFrom(other); return *this; } // Post a call to |method| to |impl_task_runner_|. template 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)...)); } // 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 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 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 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 impl_task_runner_; // For move conversion. template friend class SequenceBound; // Run on impl thread to construct |t|'s storage. template static void ConstructOwnerRecord(T* t, std::decay_t&&... 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_