diff options
author | Anand Thakker <anandthakker@users.noreply.github.com> | 2018-07-03 17:17:39 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-03 17:17:39 -0400 |
commit | cfd436c287f4209d0d994042452ccbb552a6bd28 (patch) | |
tree | 6811590928d7ea19db8e8b3f9db8d1df54ba9965 /include/mbgl/util | |
parent | 840a5cf1207ed78df3302211a23d369dd3c12b89 (diff) | |
download | qtlocation-mapboxgl-cfd436c287f4209d0d994042452ccbb552a6bd28.tar.gz |
[core] Avoid blocking in Thread<Object> constructor (#12151)
* Introduce AspiringActor, EstablishedActor
This pair of objects represents the two-phase (parent-thread /
child-thread) construction that's needed to support constructing
Thread<Object> without blocking until the child thread is up and
running.
An `AspiringActor<O>` is responsible for:
- ownership of the actor's `Mailbox`
- allocating the memory for (but *not* constructing) the target object `O`
Using these two pieces--the mailbox and a stable address for `O`--an
`AspiringActor<O>` can accept messages for the target object, or provide
`ActorRef<O>`s that do so, before the object has actually been
constructed by the corresponding `EstablishedActor<O>`. (Such messages
are queued in the mailbox until after the object is constructed.)
This allows for an `AspiringActor<O>` to be created and safely used by a
thread other than the one on which the target object will (eventually)
live.
An `EstablishedActor<O>` is responsible for managing the lifetime of the
target object `O` and the open/closed state of the parent's `mailbox`.
The `O` object's lifetime is contained by that of its owning
`EstablishedActor<O>`: the `EstablishedActor` constructor executes the
`O` constructor via "placement new", constructing it at the address
provided by the parent `AspiringActor`, and the `~EstablishedActor`
destructor similarly executes the `~O` destructor (after closing the
mailbox). `EstablishedActor` should therefore live entirely on the
thread intended to own `O`.
* Remove Actor#{invoke,ask}
Diffstat (limited to 'include/mbgl/util')
-rw-r--r-- | include/mbgl/util/thread.hpp | 55 |
1 files changed, 33 insertions, 22 deletions
diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp index 74e722b02d..bc58427349 100644 --- a/include/mbgl/util/thread.hpp +++ b/include/mbgl/util/thread.hpp @@ -37,45 +37,55 @@ namespace util { // - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events. // template<class Object> -class Thread : public Scheduler { +class Thread { public: template <class... Args> Thread(const std::string& name, Args&&... args) { - std::promise<void> running; - thread = std::thread([&] { + std::promise<void> running_; + running = running_.get_future(); + + auto capturedArgs = std::make_tuple(std::forward<Args>(args)...); + + thread = std::thread([ + this, + name, + capturedArgs = std::move(capturedArgs), + runningPromise = std::move(running_) + ] () mutable { platform::setCurrentThreadName(name); platform::makeThreadLowPriority(); util::RunLoop loop_(util::RunLoop::Type::New); loop = &loop_; + EstablishedActor<Object> establishedActor(loop_, object, std::move(capturedArgs)); - object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...); - running.set_value(); - + runningPromise.set_value(); + loop->run(); + + (void) establishedActor; + loop = nullptr; }); - - running.get_future().get(); } - ~Thread() override { + ~Thread() { if (paused) { resume(); } - std::promise<void> joinable; + std::promise<void> stoppable; + + running.wait(); - // Kill the actor, so we don't get more - // messages posted on this scheduler after - // we delete the RunLoop. + // Invoke a noop task on the run loop to ensure that we're executing + // run() before we call stop() loop->invoke([&] { - object.reset(); - joinable.set_value(); + stoppable.set_value(); }); - joinable.get_future().get(); + stoppable.get_future().get(); loop->stop(); thread.join(); @@ -85,8 +95,8 @@ public: // can be used to send messages to `Object`. It is safe // to the non-owning reference to outlive this object // and be used after the `Thread<>` gets destroyed. - ActorRef<std::decay_t<Object>> actor() const { - return object->self(); + ActorRef<std::decay_t<Object>> actor() { + return object.self(); } // Pauses the `Object` thread. It will prevent the object to wake @@ -103,6 +113,8 @@ public: auto pausing = paused->get_future(); + running.wait(); + loop->invoke(RunLoop::Priority::High, [this] { auto resuming = resumed->get_future(); paused->set_value(); @@ -127,13 +139,12 @@ public: private: MBGL_STORE_THREAD(tid); - void schedule(std::weak_ptr<Mailbox> mailbox) override { - loop->schedule(mailbox); - } + AspiringActor<Object> object; std::thread thread; - std::unique_ptr<Actor<Object>> object; + std::future<void> running; + std::unique_ptr<std::promise<void>> paused; std::unique_ptr<std::promise<void>> resumed; |