#pragma once #include #include #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: // Enabled for Objects with a constructor taking ActorRef as the first parameter template , Args...>::value>::type * = nullptr> Actor(Scheduler& scheduler, Args&&... args_) : mailbox(std::make_shared(scheduler)), object(self(), std::forward(args_)...) { } // Enabled for plain Objects template, Args...>::value>::type * = nullptr> Actor(Scheduler& scheduler, Args&& ... args_) : mailbox(std::make_shared(scheduler)), object(std::forward(args_)...) { } ~Actor() { mailbox->close(); } template void invoke(Fn fn, Args&&... args) { mailbox->push(actor::makeMessage(object, fn, std::forward(args)...)); } template auto ask(Fn fn, Args&&... args) { // Result type is deduced from the function's return type using ResultType = typename std::result_of::type; std::promise promise; auto future = promise.get_future(); mailbox->push(actor::makeMessage(std::move(promise), object, fn, std::forward(args)...)); return future; } ActorRef> self() { return ActorRef>(object, mailbox); } operator ActorRef>() { return self(); } private: std::shared_ptr mailbox; Object object; }; } // namespace mbgl