| 1 | #pragma once |
| 2 | |
| 3 | #include <mbgl/actor/aspiring_actor.hpp> |
| 4 | #include <mbgl/actor/established_actor.hpp> |
| 5 | #include <mbgl/actor/mailbox.hpp> |
| 6 | #include <mbgl/actor/message.hpp> |
| 7 | #include <mbgl/actor/actor_ref.hpp> |
| 8 | #include <mbgl/util/noncopyable.hpp> |
| 9 | |
| 10 | #include <memory> |
| 11 | #include <future> |
| 12 | #include <type_traits> |
| 13 | #include <cassert> |
| 14 | |
| 15 | namespace mbgl { |
| 16 | |
| 17 | /* |
| 18 | An `Actor<O>` is an owning reference to an asynchronous object of type `O`: an "actor". |
| 19 | Communication with an actor happens via message passing: you send a message to the object |
| 20 | (using `invoke`), passing a pointer to the member function to call and arguments which |
| 21 | are then forwarded to the actor. |
| 22 | |
| 23 | The actor receives messages sent to it asynchronously, in a manner defined its `Scheduler`. |
| 24 | To store incoming messages before their receipt, each actor has a `Mailbox`, which acts as |
| 25 | a FIFO queue. Messages sent from actor S to actor R are guaranteed to be processed in the |
| 26 | order sent. However, relative order of messages sent by two *different* actors S1 and S2 |
| 27 | to R is *not* guaranteed (and can't be: S1 and S2 may be acting asynchronously with respect |
| 28 | to each other). |
| 29 | |
| 30 | An `Actor<O>` can be converted to an `ActorRef<O>`, a non-owning value object representing |
| 31 | a (weak) reference to the actor. Messages can be sent via the `Ref` as well. |
| 32 | |
| 33 | It's safe -- and encouraged -- to pass `Ref`s between actors via messages. This is how two-way |
| 34 | communication and other forms of collaboration between multiple actors is accomplished. |
| 35 | |
| 36 | It's safe for a `Ref` to outlive its `Actor` -- the reference is "weak", and does not extend |
| 37 | the lifetime of the owning Actor, and sending a message to a `Ref` whose `Actor` has died is |
| 38 | a no-op. (In the future, a dead-letters queue or log may be implemented.) |
| 39 | |
| 40 | Construction and destruction of an Actor is synchronous: the corresponding `O` |
| 41 | object is constructed synchronously by the `Actor` constructor, and destructed synchronously |
| 42 | by the `~Actor` destructor, after ensuring that the `O` is not currently receiving an |
| 43 | asynchronous message. The constructor of `O` is passed an `ActorRef<O>` referring to itself |
| 44 | (which it can use to self-send messages), followed by the forwarded arguments passed to |
| 45 | `Actor<O>`. Asynchronous object construction can be accomplished by directly using the |
| 46 | lower-level types, `AspiringActor<O>` and `EstablishedActor<O>`. |
| 47 | |
| 48 | Please don't send messages that contain shared pointers or references. That subverts the |
| 49 | purpose of the actor model: prohibiting direct concurrent access to shared state. |
| 50 | */ |
| 51 | template <class Object> |
| 52 | class Actor { |
| 53 | public: |
| 54 | template <class... Args> |
| 55 | Actor(Scheduler& scheduler, Args&&... args) |
| 56 | : target(scheduler, parent, std::forward<Args>(args)...) {} |
| 57 | |
| 58 | Actor(const Actor&) = delete; |
| 59 | |
| 60 | ActorRef<std::decay_t<Object>> self() { |
| 61 | return parent.self(); |
| 62 | } |
| 63 | |
| 64 | private: |
| 65 | AspiringActor<Object> parent; |
| 66 | EstablishedActor<Object> target; |
| 67 | }; |
| 68 | |
| 69 | } // namespace mbgl |
| 70 | |