summaryrefslogtreecommitdiff
path: root/include/mbgl/util
diff options
context:
space:
mode:
authorAnand Thakker <anandthakker@users.noreply.github.com>2018-07-03 17:17:39 -0400
committerGitHub <noreply@github.com>2018-07-03 17:17:39 -0400
commitcfd436c287f4209d0d994042452ccbb552a6bd28 (patch)
tree6811590928d7ea19db8e8b3f9db8d1df54ba9965 /include/mbgl/util
parent840a5cf1207ed78df3302211a23d369dd3c12b89 (diff)
downloadqtlocation-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.hpp55
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;