summaryrefslogtreecommitdiff
path: root/include/mbgl/actor/actor.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/mbgl/actor/actor.hpp')
-rw-r--r--include/mbgl/actor/actor.hpp95
1 files changed, 68 insertions, 27 deletions
diff --git a/include/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp
index 338b7a559d..f87f191b73 100644
--- a/include/mbgl/actor/actor.hpp
+++ b/include/mbgl/actor/actor.hpp
@@ -53,38 +53,55 @@ namespace util {
template <class Object>
class Actor : public util::noncopyable {
public:
+ // TODO: handle std::reference_wrapper case like make_tuple does.
+ template <class... Args>
+ using Arguments = std::tuple<std::decay_t<Args>...>;
- // Enabled for Objects with a constructor taking ActorRef<Object> as the first parameter
- template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
- Actor(Scheduler& scheduler, Args&&... args_)
- : mailbox(std::make_shared<Mailbox>(scheduler)),
- object(self(), std::forward<Args>(args_)...) {
+ template <class... Args>
+ static Arguments<Args...> captureArguments(Args&&... args) {
+ return std::make_tuple(std::forward<Args>(args)...);
}
- template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
- Actor(std::shared_ptr<Mailbox> mailbox_, Args&&... args_)
- : mailbox(std::move(mailbox_)),
- object(self(), std::forward<Args>(args_)...) {
- }
-
- // Enabled for plain Objects
- template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
- Actor(Scheduler& scheduler, Args&& ... args_)
- : mailbox(std::make_shared<Mailbox>(scheduler)), object(std::forward<Args>(args_)...) {
+ // Target thread constructor: constructs the Object synchronously and
+ // immediately begins processing messages to it.
+ template<class... Args>
+ Actor(Scheduler& scheduler, Args&& ... args) : mailbox(std::make_shared<Mailbox>()) {
+ emplaceObject(std::forward<Args>(args)...);
+ mailbox->activate(scheduler);
}
- template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
- Actor(std::shared_ptr<Mailbox> mailbox_, Args&& ... args_)
- : mailbox(std::move(mailbox_)), object(std::forward<Args>(args_)...) {
+ // Parent thread constructor, allowing an Actor to be pre-created on a
+ // parent thread before its target thread is up and running.
+ // This constructor:
+ // * does not construct an Object
+ // * pre-creates a "holding" mailbox, whose messages are guaranteed not to
+ // be consumed until we explicitly call start().
+ //
+ // An Actor created in this manner must be manually activated by a call to
+ // activate() from the target thread, at which point the Object is
+ // constructed and the Mailbox starts being consumed.
+ //
+ // Meanwhile, this allows us to immediately provide ActorRefs to which
+ // messages can safely be sent, since we won't process those messages
+ // until their target object is in place.
+ Actor() : mailbox(std::make_shared<Mailbox>()) {}
+
+ template <class ArgsTuple>
+ void activate(Scheduler& scheduler, ArgsTuple&& args) {
+ emplaceObject(args, std::make_index_sequence<std::tuple_size<ArgsTuple>::value>{});
+ mailbox->activate(scheduler);
}
-
+
~Actor() {
mailbox->close();
+ if (initialized) {
+ (&object())->~Object();
+ }
}
template <typename Fn, class... Args>
void invoke(Fn fn, Args&&... args) {
- mailbox->push(actor::makeMessage(object, fn, std::forward<Args>(args)...));
+ mailbox->push(actor::makeMessage(object(), fn, std::forward<Args>(args)...));
}
template <typename Fn, class... Args>
@@ -94,24 +111,48 @@ public:
std::promise<ResultType> promise;
auto future = promise.get_future();
- mailbox->push(actor::makeMessage(std::move(promise), object, fn, std::forward<Args>(args)...));
+ mailbox->push(actor::makeMessage(std::move(promise), object(), fn, std::forward<Args>(args)...));
return future;
}
ActorRef<std::decay_t<Object>> self() {
- return ActorRef<std::decay_t<Object>>(object, mailbox);
+ return ActorRef<std::decay_t<Object>>(object(), mailbox);
}
operator ActorRef<std::decay_t<Object>>() {
return self();
}
-
-private:
- template <typename O>
- friend class util::Thread;
+private:
std::shared_ptr<Mailbox> mailbox;
- Object object;
+ std::aligned_storage_t<sizeof(Object)> objectStorage;
+ std::atomic<bool> initialized { false };
+
+ Object& object() {
+ assert(initialized || !mailbox->isActive());
+ return *reinterpret_cast<Object *>(&objectStorage);
+ }
+
+ // Enabled for Objects with a constructor taking ActorRef<Object> as the first parameter
+ template <typename U = Object, class... Args, typename std::enable_if<std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
+ void emplaceObject(Args&&... args_) {
+ new (&objectStorage) Object(self(), std::forward<Args>(args_)...);
+ initialized = true;
+ }
+
+ // Enabled for plain Objects
+ template<typename U = Object, class... Args, typename std::enable_if<!std::is_constructible<U, ActorRef<U>, Args...>::value>::type * = nullptr>
+ void emplaceObject(Args&&... args_) {
+ new (&objectStorage) Object(std::forward<Args>(args_)...);
+ initialized = true;
+ }
+
+ // Used to expand a tuple of arguments created by Actor<Object>::captureArguments()
+ template<class ArgsTuple, std::size_t... I>
+ void emplaceObject(ArgsTuple args, std::index_sequence<I...>) {
+ emplaceObject(std::move(std::get<I>(std::forward<ArgsTuple>(args)))...);
+ }
+
};
} // namespace mbgl