From 54c3ccc37d4534f699437f9c1854d8f5a857069e Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 21 Jun 2017 17:29:05 +0300 Subject: [core] Make the mbgl/actor headers public They will be needed by the DefaultFileSource, something that we also export as public. --- include/mbgl/actor/actor.hpp | 77 ++++++++++++++++++++++++++++++++++++++++ include/mbgl/actor/actor_ref.hpp | 43 ++++++++++++++++++++++ include/mbgl/actor/message.hpp | 48 +++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 include/mbgl/actor/actor.hpp create mode 100644 include/mbgl/actor/actor_ref.hpp create mode 100644 include/mbgl/actor/message.hpp (limited to 'include') diff --git a/include/mbgl/actor/actor.hpp b/include/mbgl/actor/actor.hpp new file mode 100644 index 0000000000..810114c513 --- /dev/null +++ b/include/mbgl/actor/actor.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace mbgl { + +/* + An `Actor` is an owning reference to an asynchronous object of type `O`: an "actor". + Communication with an actor happens via message passing: you send a message to the object + (using `invoke`), passing a pointer to the member function to call and arguments which + are then forwarded to the actor. + + The actor receives messages sent to it asynchronously, in a manner defined its `Scheduler`. + To store incoming messages before their receipt, each actor has a `Mailbox`, which acts as + a FIFO queue. Messages sent from actor S to actor R are guaranteed to be processed in the + order sent. However, relative order of messages sent by two *different* actors S1 and S2 + to R is *not* guaranteed (and can't be: S1 and S2 may be acting asynchronously with respect + to each other). + + An `Actor` can be converted to an `ActorRef`, a non-owning value object representing + a (weak) reference to the actor. Messages can be sent via the `Ref` as well. + + It's safe -- and encouraged -- to pass `Ref`s between actors via messages. This is how two-way + communication and other forms of collaboration between multiple actors is accomplished. + + It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend + the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is + a no-op. (In the future, a dead-letters queue or log may be implemented.) + + Construction and destruction of an actor is currently synchronous: the corresponding `O` + object is constructed synchronously by the `Actor` constructor, and destructed synchronously + by the `~Actor` destructor, after ensuring that the `O` is not currently receiving an + asynchronous message. (Construction and destruction may change to be asynchronous in the + future.) The constructor of `O` is passed an `ActorRef` referring to itself (which it + can use to self-send messages), followed by the forwarded arguments passed to `Actor`. + + Please don't send messages that contain shared pointers or references. That subverts the + purpose of the actor model: prohibiting direct concurrent access to shared state. +*/ + +template +class Actor : public util::noncopyable { +public: + template + Actor(Scheduler& scheduler, Args&&... args_) + : mailbox(std::make_shared(scheduler)), + object(self(), std::forward(args_)...) { + } + + ~Actor() { + mailbox->close(); + } + + template + void invoke(Fn fn, Args&&... args) { + mailbox->push(actor::makeMessage(object, fn, std::forward(args)...)); + } + + ActorRef> self() { + return ActorRef>(object, mailbox); + } + + operator ActorRef>() { + return self(); + } + +private: + std::shared_ptr mailbox; + Object object; +}; + +} // namespace mbgl diff --git a/include/mbgl/actor/actor_ref.hpp b/include/mbgl/actor/actor_ref.hpp new file mode 100644 index 0000000000..9d858d823f --- /dev/null +++ b/include/mbgl/actor/actor_ref.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include + +namespace mbgl { + +/* + An `ActorRef` is a *non*-owning, weak reference to an actor of type `O`. You can send it + messages just like an `Actor`. It's a value object: safe to copy and pass between actors + via messages. + + An `ActorRef` does not extend the lifetime of the corresponding `Actor`. That's determined + entirely by whichever object owns the `Actor` -- the actor's "supervisor". + + It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend + the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is + a no-op. (In the future, a dead-letters queue or log may be implemented.) +*/ + +template +class ActorRef { +public: + ActorRef(Object& object_, std::weak_ptr weakMailbox_) + : object(object_), + weakMailbox(std::move(weakMailbox_)) { + } + + template + void invoke(Fn fn, Args&&... args) { + if (auto mailbox = weakMailbox.lock()) { + mailbox->push(actor::makeMessage(object, fn, std::forward(args)...)); + } + } + +private: + Object& object; + std::weak_ptr weakMailbox; +}; + +} // namespace mbgl diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp new file mode 100644 index 0000000000..cf071d4933 --- /dev/null +++ b/include/mbgl/actor/message.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace mbgl { + +// A movable type-erasing function wrapper. This allows to store arbitrary invokable +// things (like std::function<>, or the result of a movable-only std::bind()) in the queue. +// Source: http://stackoverflow.com/a/29642072/331379 +class Message { +public: + virtual ~Message() = default; + virtual void operator()() = 0; +}; + +template +class MessageImpl : public Message { +public: + MessageImpl(Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)) { + } + + void operator()() override { + invoke(std::make_index_sequence::value>()); + } + + template + void invoke(std::index_sequence) { + (object.*memberFn)(std::move(std::get(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; +}; + +namespace actor { + +template +std::unique_ptr makeMessage(Object& object, MemberFn memberFn, Args&&... args) { + auto tuple = std::make_tuple(std::forward(args)...); + return std::make_unique>(object, memberFn, std::move(tuple)); +} + +} // namespace actor +} // namespace mbgl -- cgit v1.2.1