summaryrefslogtreecommitdiff
path: root/include/mbgl/util/thread.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/mbgl/util/thread.hpp')
-rw-r--r--include/mbgl/util/thread.hpp62
1 files changed, 49 insertions, 13 deletions
diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp
index 74e722b02d..8230d8778f 100644
--- a/include/mbgl/util/thread.hpp
+++ b/include/mbgl/util/thread.hpp
@@ -19,6 +19,11 @@
namespace mbgl {
namespace util {
+class NoopScheduler : public Scheduler {
+public:
+ void schedule(std::weak_ptr<Mailbox>) override {}
+};
+
// Manages a thread with `Object`.
// Upon creation of this object, it launches a thread and creates an object of type `Object`
@@ -41,23 +46,37 @@ class Thread : public Scheduler {
public:
template <class... Args>
Thread(const std::string& name, Args&&... args) {
- std::promise<void> running;
-
- thread = std::thread([&] {
+ std::unique_ptr<std::promise<void>> running_ = std::make_unique<std::promise<void>>();
+ running = running_->get_future();
+
+ // Pre-create a mailbox for this actor, using a NoopScheduler that
+ // leaves the mailbox's queue unconsumed.
+ // Once the RunLoop on the target thread has been created, we'll replace
+ // the NoopScheduler with the RunLoop. Meanwhile, this allows us to
+ // immediately provide ActorRef using this mailbox, with any messages
+ // sent to them being queued in the holding mailbox until the thread is
+ // up and running.
+ std::shared_ptr<Mailbox> mailbox_ = std::make_shared<Mailbox>(noopScheduler);
+ mailbox = mailbox_;
+
+ thread = std::thread([&, sharedMailbox = std::move(mailbox_), runningPromise = std::move(running_)] {
platform::setCurrentThreadName(name);
platform::makeThreadLowPriority();
util::RunLoop loop_(util::RunLoop::Type::New);
loop = &loop_;
- object = std::make_unique<Actor<Object>>(*this, std::forward<Args>(args)...);
- running.set_value();
+ Actor<Object>* actor = new (&actorStorage) Actor<Object>(std::move(sharedMailbox), std::forward<Args>(args)...);
+ // Replace the NoopScheduler on the mailbox with the RunLoop to
+ // begin actually processing messages.
+ actor->mailbox->setScheduler(this);
+
+ runningPromise->set_value();
+
loop->run();
loop = nullptr;
});
-
- running.get_future().get();
}
~Thread() override {
@@ -66,12 +85,14 @@ public:
}
std::promise<void> joinable;
+
+ running.wait();
// Kill the actor, so we don't get more
// messages posted on this scheduler after
// we delete the RunLoop.
loop->invoke([&] {
- object.reset();
+ reinterpret_cast<const Actor<Object>*>(&actorStorage)->~Actor<Object>();
joinable.set_value();
});
@@ -85,8 +106,15 @@ 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() {
+ // The actor->object reference we provide here will not actually be
+ // valid until the child thread constructs Actor<Object> into
+ // actorStorage using "placement new".
+ // We guarantee that the object reference isn't actually used by
+ // using the NoopScheduler to prevent messages to this mailbox from
+ // being processed until after the actor has been constructed.
+ auto actor = reinterpret_cast<Actor<Object>*>(&actorStorage);
+ return ActorRef<std::decay_t<Object>>(actor->object, mailbox);
}
// Pauses the `Object` thread. It will prevent the object to wake
@@ -103,6 +131,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 +157,19 @@ public:
private:
MBGL_STORE_THREAD(tid);
- void schedule(std::weak_ptr<Mailbox> mailbox) override {
- loop->schedule(mailbox);
+ void schedule(std::weak_ptr<Mailbox> mailbox_) override {
+ assert(loop);
+ loop->schedule(mailbox_);
}
+ NoopScheduler noopScheduler;
+ std::weak_ptr<Mailbox> mailbox;
+ std::aligned_storage<sizeof(Actor<Object>)> actorStorage;
+
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;