Переглянути джерело

* Modified the `RuntimeCallback` to hold a reference to a `Runtime`.
* Mocked up a simple protocol as an example.

Matthew Carr 1 рік тому
батько
коміт
b52fba0be0
2 змінених файлів з 457 додано та 83 видалено
  1. 12 12
      Cargo.lock
  2. 445 71
      crates/btrun/src/lib.rs

+ 12 - 12
Cargo.lock

@@ -70,7 +70,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.22",
 ]
 
 [[package]]
@@ -1036,7 +1036,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.22",
 ]
 
 [[package]]
@@ -1888,7 +1888,7 @@ dependencies = [
  "proc-macro2",
  "proc-macro2-diagnostics",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.22",
 ]
 
 [[package]]
@@ -1974,7 +1974,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.22",
 ]
 
 [[package]]
@@ -2087,9 +2087,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.56"
+version = "1.0.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
+checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
 dependencies = [
  "unicode-ident",
 ]
@@ -2102,7 +2102,7 @@ checksum = "606c4ba35817e2922a308af55ad51bab3645b59eae5c570d4a6cf07e36bd493b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.22",
  "version_check",
  "yansi",
 ]
@@ -2176,9 +2176,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.26"
+version = "1.0.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
+checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
 dependencies = [
  "proc-macro2",
 ]
@@ -2713,9 +2713,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.13"
+version = "2.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
+checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3119,7 +3119,7 @@ checksum = "3f67b459f42af2e6e1ee213cb9da4dbd022d3320788c3fb3e1b893093f1e45da"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.13",
+ "syn 2.0.22",
 ]
 
 [[package]]

+ 445 - 71
crates/btrun/src/lib.rs

@@ -29,27 +29,27 @@ use uuid::Uuid;
 macro_rules! declare_runtime {
     ($name:ident, $ip_addr:expr, $creds:expr) => {
         ::lazy_static::lazy_static! {
-            static ref $name: Runtime =  _new_runtime($ip_addr, $creds).unwrap();
+            static ref $name: &'static Runtime = {
+                ::lazy_static::lazy_static! {
+                    static ref RUNTIME: Runtime =  Runtime::_new($creds).unwrap();
+                    static ref RECEIVER: Receiver = _new_receiver($ip_addr, $creds, &*RUNTIME);
+                }
+                // By dereferencing RECEIVER we ensure it is started.
+                let _ = &*RECEIVER;
+                &*RUNTIME
+            };
         }
     };
 }
 
-///  This function is not intended to be called directly by downstream crates. Use the macro
-/// [declare_runtime] to create a [Runtime] instead.
-pub fn _new_runtime<C: 'static + Send + Sync + Creds>(
-    ip_addr: IpAddr,
-    creds: Arc<C>,
-) -> Result<Runtime> {
-    let path = Arc::new(creds.bind_path()?);
-    let handles = Arc::new(RwLock::new(HashMap::new()));
-    let callback = RuntimeCallback::new(handles.clone());
-    let rx = Receiver::new(ip_addr, creds, callback)?;
-    Ok(Runtime {
-        _rx: rx,
-        path,
-        handles,
-        peers: RwLock::new(HashMap::new()),
-    })
+/// This function is not intended to be called by downstream crates.
+#[doc(hidden)]
+pub fn _new_receiver<C>(ip_addr: IpAddr, creds: Arc<C>, runtime: &'static Runtime) -> Receiver
+where
+    C: 'static + Send + Sync + Creds,
+{
+    let callback = RuntimeCallback::new(runtime);
+    Receiver::new(ip_addr, creds, callback).unwrap()
 }
 
 /// An actor runtime.
@@ -59,13 +59,26 @@ pub fn _new_runtime<C: 'static + Send + Sync + Creds>(
 /// recipient. If a reply is needed, then `call` can be used, which returns a future that will
 /// be ready when the reply has been received.
 pub struct Runtime {
-    _rx: Receiver,
     path: Arc<BlockPath>,
-    handles: Arc<RwLock<HashMap<Uuid, ActorHandle>>>,
+    handles: RwLock<HashMap<Uuid, ActorHandle>>,
     peers: RwLock<HashMap<Arc<BlockPath>, Transmitter>>,
 }
 
 impl Runtime {
+    ///  This method is not intended to be called directly by downstream crates. Use the macro
+    /// [declare_runtime] to create a [Runtime].
+    ///
+    /// If you create a non-static [Runtime], your process will panic when it is dropped.
+    #[doc(hidden)]
+    pub fn _new<C: 'static + Send + Sync + Creds>(creds: Arc<C>) -> Result<Runtime> {
+        let path = Arc::new(creds.bind_path()?);
+        Ok(Runtime {
+            path,
+            handles: RwLock::new(HashMap::new()),
+            peers: RwLock::new(HashMap::new()),
+        })
+    }
+
     pub fn path(&self) -> &Arc<BlockPath> {
         &self.path
     }
@@ -79,14 +92,14 @@ impl Runtime {
     /// Sends a message to the actor identified by the given [ActorName].
     pub async fn send<T: 'static + SendMsg>(
         &self,
-        to: &ActorName,
-        from: Uuid,
+        to: ActorName,
+        from: ActorName,
         msg: T,
     ) -> Result<()> {
         if to.path == self.path {
             let guard = self.handles.read().await;
             if let Some(handle) = guard.get(&to.act_id) {
-                handle.send(msg).await
+                handle.send(from, msg).await
             } else {
                 Err(bterr!("invalid actor name"))
             }
@@ -95,7 +108,7 @@ impl Runtime {
             if let Some(peer) = guard.get(&to.path) {
                 let buf = to_vec(&msg)?;
                 let wire_msg = WireMsg {
-                    to: to.act_id,
+                    to,
                     from,
                     payload: &buf,
                 };
@@ -112,14 +125,14 @@ impl Runtime {
     /// is ready when a reply has been received.
     pub async fn call<T: 'static + CallMsg>(
         &self,
-        to: &ActorName,
-        from: Uuid,
+        to: ActorName,
+        from: ActorName,
         msg: T,
     ) -> Result<T::Reply> {
         if to.path == self.path {
             let guard = self.handles.read().await;
             if let Some(handle) = guard.get(&to.act_id) {
-                handle.call_through(msg).await
+                handle.call_through(from, msg).await
             } else {
                 Err(bterr!("invalid actor name"))
             }
@@ -128,7 +141,7 @@ impl Runtime {
             if let Some(peer) = guard.get(&to.path) {
                 let buf = to_vec(&msg)?;
                 let wire_msg = WireMsg {
-                    to: to.act_id,
+                    to,
                     from,
                     payload: &buf,
                 };
@@ -161,21 +174,24 @@ impl Runtime {
             }
             act_id
         };
+        let act_name = self.actor_name(act_id);
         let (tx, rx) = mpsc::channel::<Envelope<Msg>>(MAILBOX_LIMIT);
         // The deliverer closure is responsible for deserializing messages received over the wire
         // and delivering them to the actor's mailbox and sending replies to call messages.
         let deliverer = {
             let buffer = Arc::new(Mutex::new(Vec::<u8>::new()));
             let tx = tx.clone();
+            let act_name = act_name.clone();
             move |envelope: WireEnvelope| {
                 let (wire_msg, replier) = envelope.into_parts();
                 let result = from_slice(wire_msg.payload);
                 let buffer = buffer.clone();
                 let tx = tx.clone();
+                let act_name = act_name.clone();
                 let fut: FutureResult = Box::pin(async move {
                     let msg = result?;
                     if let Some(mut replier) = replier {
-                        let (envelope, rx) = Envelope::call(msg);
+                        let (envelope, rx) = Envelope::new_call(act_name, msg);
                         tx.send(envelope).await.map_err(|_| {
                             bterr!("failed to deliver message. Recipient may have halted.")
                         })?;
@@ -190,9 +206,11 @@ impl Runtime {
                             Err(err) => replier.reply_err(err.to_string(), None).await,
                         }
                     } else {
-                        tx.send(Envelope::Send { msg }).await.map_err(|_| {
-                            bterr!("failed to deliver message. Recipient may have halted.")
-                        })
+                        tx.send(Envelope::new_send(act_name, msg))
+                            .await
+                            .map_err(|_| {
+                                bterr!("failed to deliver message. Recipient may have halted.")
+                            })
                     }
                 });
                 fut
@@ -201,7 +219,7 @@ impl Runtime {
         let handle = tokio::task::spawn(activator(self, rx, act_id));
         let actor_handle = ActorHandle::new(handle, tx, deliverer);
         guard.insert(act_id, actor_handle);
-        ActorName::new(self.path.clone(), act_id)
+        act_name
     }
 
     /// Registers an actor as a service with the given [ServiceId].
@@ -240,6 +258,11 @@ impl Runtime {
             Err(RuntimeError::BadActorName(name.clone()).into())
         }
     }
+
+    /// Returns the name of the actor in this runtime with the given actor ID.
+    pub fn actor_name(&self, act_id: Uuid) -> ActorName {
+        ActorName::new(self.path.clone(), act_id)
+    }
 }
 
 impl Drop for Runtime {
@@ -289,15 +312,73 @@ impl<T: CallMsg> DeserCallback for ReplyCallback<T> {
     }
 }
 
+struct SendReplyCallback {
+    replier: Option<Replier>,
+}
+
+impl SendReplyCallback {
+    fn new(replier: Replier) -> Self {
+        Self {
+            replier: Some(replier),
+        }
+    }
+}
+
+impl DeserCallback for SendReplyCallback {
+    type Arg<'de> = WireReply<'de>;
+    type Return = Result<()>;
+    type CallFut<'de> = impl 'de + Future<Output = Self::Return>;
+    fn call<'de>(&'de mut self, arg: Self::Arg<'de>) -> Self::CallFut<'de> {
+        async move {
+            if let Some(mut replier) = self.replier.take() {
+                replier.reply(arg).await
+            } else {
+                Ok(())
+            }
+        }
+    }
+}
+
 /// This struct implements the server callback for network messages.
 #[derive(Clone)]
 struct RuntimeCallback {
-    handles: Arc<RwLock<HashMap<Uuid, ActorHandle>>>,
+    rt: &'static Runtime,
 }
 
 impl RuntimeCallback {
-    fn new(handles: Arc<RwLock<HashMap<Uuid, ActorHandle>>>) -> Self {
-        Self { handles }
+    fn new(rt: &'static Runtime) -> Self {
+        Self { rt }
+    }
+
+    async fn deliver_local(&self, msg: WireMsg<'_>, replier: Option<Replier>) -> Result<()> {
+        let guard = self.rt.handles.read().await;
+        if let Some(handle) = guard.get(&msg.to.act_id) {
+            let envelope = if let Some(replier) = replier {
+                WireEnvelope::Call { msg, replier }
+            } else {
+                WireEnvelope::Send { msg }
+            };
+            (handle.deliverer)(envelope).await
+        } else {
+            Err(bterr!("invalid actor name: {}", msg.to))
+        }
+    }
+
+    async fn route_msg(&self, msg: WireMsg<'_>, replier: Option<Replier>) -> Result<()> {
+        let guard = self.rt.peers.read().await;
+        if let Some(tx) = guard.get(msg.to.path()) {
+            if let Some(replier) = replier {
+                let callback = SendReplyCallback::new(replier);
+                tx.call(msg, callback).await?
+            } else {
+                tx.send(msg).await
+            }
+        } else {
+            Err(bterr!(
+                "unable to deliver message to peer at '{}'",
+                msg.to.path()
+            ))
+        }
     }
 }
 
@@ -307,16 +388,10 @@ impl MsgCallback for RuntimeCallback {
     fn call<'de>(&'de self, arg: btmsg::MsgReceived<Self::Arg<'de>>) -> Self::CallFut<'de> {
         async move {
             let (_, body, replier) = arg.into_parts();
-            let guard = self.handles.read().await;
-            if let Some(handle) = guard.get(&body.to) {
-                let envelope = if let Some(replier) = replier {
-                    WireEnvelope::Call { msg: body, replier }
-                } else {
-                    WireEnvelope::Send { msg: body }
-                };
-                (handle.deliverer)(envelope).await
+            if body.to.path() == self.rt.path() {
+                self.deliver_local(body, replier).await
             } else {
-                Err(bterr!("invalid actor ID: {}", body.to))
+                self.route_msg(body, replier).await
             }
         }
     }
@@ -366,6 +441,14 @@ impl ActorName {
     pub fn new(path: Arc<BlockPath>, act_id: Uuid) -> Self {
         Self { path, act_id }
     }
+
+    pub fn path(&self) -> &Arc<BlockPath> {
+        &self.path
+    }
+
+    pub fn act_id(&self) -> Uuid {
+        self.act_id
+    }
 }
 
 impl Display for ActorName {
@@ -389,8 +472,8 @@ const MAILBOX_LIMIT: usize = 32;
 /// The type of messages sent over the wire between runtimes.
 #[derive(Serialize, Deserialize)]
 struct WireMsg<'a> {
-    to: Uuid,
-    from: Uuid,
+    to: ActorName,
+    from: ActorName,
     payload: &'a [u8],
 }
 
@@ -425,26 +508,70 @@ impl<'de> WireEnvelope<'de> {
 ///
 /// If the message was sent with call, then this enum will contain a channel that can be used to
 /// reply to it.
-pub enum Envelope<T: CallMsg> {
-    /// The message was sent with `send` and does not expect a reply.
-    Send { msg: T },
-    /// The message was sent with `call` and expects a reply.
-    Call {
-        msg: T,
-        /// A reply message must be sent using this channel.
-        reply: oneshot::Sender<T::Reply>,
-    },
+pub struct Envelope<T: CallMsg> {
+    from: ActorName,
+    reply: Option<oneshot::Sender<T::Reply>>,
+    msg: T,
 }
 
 impl<T: CallMsg> Envelope<T> {
-    fn send(msg: T) -> Self {
-        Self::Send { msg }
+    pub fn new(msg: T, reply: Option<oneshot::Sender<T::Reply>>, from: ActorName) -> Self {
+        Self { from, reply, msg }
     }
 
-    fn call(msg: T) -> (Self, oneshot::Receiver<T::Reply>) {
+    /// Creates a new envelope containing the given message which does not expect a reply.
+    fn new_send(from: ActorName, msg: T) -> Self {
+        Self {
+            from,
+            msg,
+            reply: None,
+        }
+    }
+
+    /// Creates a new envelope containing the given message which expects exactly one reply.
+    fn new_call(from: ActorName, msg: T) -> (Self, oneshot::Receiver<T::Reply>) {
         let (tx, rx) = oneshot::channel::<T::Reply>();
-        let kind = Envelope::Call { msg, reply: tx };
-        (kind, rx)
+        let envelope = Self {
+            from,
+            msg,
+            reply: Some(tx),
+        };
+        (envelope, rx)
+    }
+
+    /// Returns the name of the actor which sent this message.
+    pub fn from(&self) -> &ActorName {
+        &self.from
+    }
+
+    /// Returns a reference to the message in this envelope.
+    pub fn msg(&self) -> &T {
+        &self.msg
+    }
+
+    /// Sends a reply to this message.
+    ///
+    /// If this message is not expecting a reply, or if this message has already been replied to,
+    /// then an error is returned.
+    pub fn reply(&mut self, reply: T::Reply) -> Result<()> {
+        if let Some(tx) = self.reply.take() {
+            if tx.send(reply).is_ok() {
+                Ok(())
+            } else {
+                Err(bterr!("failed to send reply"))
+            }
+        } else {
+            Err(bterr!("reply already sent"))
+        }
+    }
+
+    /// Returns true if this message expects a reply and it has not already been replied to.
+    pub fn needs_reply(&self) -> bool {
+        self.reply.is_some()
+    }
+
+    pub fn split(self) -> (T, Option<oneshot::Sender<T::Reply>>, ActorName) {
+        (self.msg, self.reply, self.from)
     }
 }
 
@@ -475,18 +602,23 @@ impl ActorHandle {
             .ok_or_else(|| bterr!("unexpected message type"))
     }
 
-    pub async fn send<T: 'static + SendMsg>(&self, msg: T) -> Result<()> {
+    /// Sends a message to the actor represented by this handle.
+    pub async fn send<T: 'static + SendMsg>(&self, from: ActorName, msg: T) -> Result<()> {
         let sender = self.sender()?;
         sender
-            .send(Envelope::send(msg))
+            .send(Envelope::new_send(from, msg))
             .await
             .map_err(|_| bterr!("failed to enqueue message"))?;
         Ok(())
     }
 
-    pub async fn call_through<T: 'static + CallMsg>(&self, msg: T) -> Result<T::Reply> {
+    pub async fn call_through<T: 'static + CallMsg>(
+        &self,
+        from: ActorName,
+        msg: T,
+    ) -> Result<T::Reply> {
         let sender = self.sender()?;
-        let (envelope, rx) = Envelope::call(msg);
+        let (envelope, rx) = Envelope::new_call(from, msg);
         sender
             .send(envelope)
             .await
@@ -528,7 +660,11 @@ mod tests {
     use btserde::to_vec;
     use ctor::ctor;
     use lazy_static::lazy_static;
-    use std::net::{IpAddr, Ipv4Addr};
+    use std::{
+        net::{IpAddr, Ipv4Addr},
+        sync::atomic::{AtomicU8, Ordering},
+        time::{Duration, Instant},
+    };
     use tokio::runtime::Builder;
 
     const RUNTIME_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
@@ -577,9 +713,10 @@ mod tests {
         mut mailbox: mpsc::Receiver<Envelope<EchoMsg>>,
         _act_id: Uuid,
     ) {
-        while let Some(msg) = mailbox.recv().await {
-            if let Envelope::Call { msg, reply } = msg {
-                if let Err(_) = reply.send(msg) {
+        while let Some(envelope) = mailbox.recv().await {
+            let (msg, replier, ..) = envelope.split();
+            if let Some(replier) = replier {
+                if let Err(_) = replier.send(msg) {
                     panic!("failed to send reply");
                 }
             }
@@ -591,9 +728,10 @@ mod tests {
         ASYNC_RT.block_on(async {
             const EXPECTED: &str = "hello";
             let name = RUNTIME.activate(echo).await;
+            let from = ActorName::new(name.path().clone(), Uuid::default());
 
             let reply = RUNTIME
-                .call(&name, Uuid::default(), EchoMsg(EXPECTED.into()))
+                .call(name.clone(), from, EchoMsg(EXPECTED.into()))
                 .await
                 .unwrap();
 
@@ -615,8 +753,8 @@ mod tests {
                 .unwrap();
             let buf = to_vec(&EchoMsg(EXPECTED.to_string())).unwrap();
             let wire_msg = WireMsg {
-                to: actor_name.act_id,
-                from: Uuid::default(),
+                to: actor_name.clone(),
+                from: RUNTIME.actor_name(Uuid::default()),
                 payload: &buf,
             };
 
@@ -649,4 +787,240 @@ mod tests {
         LOCAL_RT.take(&name).await.unwrap();
         assert_eq!(0, LOCAL_RT.num_running().await);
     }
+
+    struct Activate {
+        rt: &'static Runtime,
+        act_id: Uuid,
+    }
+
+    #[derive(Serialize, Deserialize)]
+    struct Ping;
+    impl CallMsg for Ping {
+        type Reply = ();
+    }
+    impl SendMsg for Ping {}
+
+    #[derive(Serialize, Deserialize)]
+    struct Pong;
+    impl CallMsg for Pong {
+        type Reply = ();
+    }
+    impl SendMsg for Pong {}
+
+    trait ClientInit {
+        type AfterActivate: SentPing;
+        type HandleActivateFut: Future<Output = Result<Self::AfterActivate>>;
+        fn handle_activate(self, msg: Activate) -> Self::HandleActivateFut;
+    }
+
+    trait SentPing {
+        type AfterPong: Returned;
+        type HandlePongFut: Future<Output = Result<Self::AfterPong>>;
+        fn handle_pong(self, msg: Envelope<Pong>) -> Self::HandlePongFut;
+    }
+
+    trait ServerInit {
+        type AfterActivate: Listening;
+        type HandleActivateFut: Future<Output = Result<Self::AfterActivate>>;
+        fn handle_activate(self, msg: Activate) -> Self::HandleActivateFut;
+    }
+
+    trait Listening {
+        type AfterPing: Returned;
+        type HandlePingFut: Future<Output = Result<Self::AfterPing>>;
+        fn handle_ping(self, msg: Envelope<Ping>) -> Self::HandlePingFut;
+    }
+
+    trait Returned {}
+
+    struct ReturnedState;
+
+    impl Returned for ReturnedState {}
+
+    trait PingProtocol {
+        type Client: ClientInit;
+        type Server: ServerInit;
+    }
+
+    #[derive(Serialize, Deserialize)]
+    enum PingProtocolMsg {
+        Ping(Ping),
+        Pong(Pong),
+    }
+    impl CallMsg for PingProtocolMsg {
+        type Reply = ();
+    }
+    impl SendMsg for PingProtocolMsg {}
+
+    #[allow(dead_code)]
+    enum PingClientState {
+        Init(ClientInitState),
+        SentPing(ClientState),
+        Returned(ReturnedState),
+    }
+
+    #[allow(dead_code)]
+    enum PingServerState {
+        ServerInit(ServerInitState),
+        Listening(ServerState),
+        Returned(ReturnedState),
+    }
+
+    struct ClientInitState {
+        server_name: ActorName,
+    }
+
+    struct ClientState;
+
+    impl ClientInit for ClientInitState {
+        type AfterActivate = ClientState;
+        type HandleActivateFut = impl Future<Output = Result<Self::AfterActivate>>;
+        fn handle_activate(self, msg: Activate) -> Self::HandleActivateFut {
+            async move {
+                let from = msg.rt.actor_name(msg.act_id);
+                // TODO: This implementation should not know about PingProtocolMsg. It should be
+                // able to send Ping directly.
+                msg.rt
+                    .send(self.server_name, from, PingProtocolMsg::Ping(Ping))
+                    .await?;
+                Ok(ClientState)
+            }
+        }
+    }
+
+    impl SentPing for ClientState {
+        type AfterPong = ReturnedState;
+        type HandlePongFut = Ready<Result<Self::AfterPong>>;
+        fn handle_pong(self, _msg: Envelope<Pong>) -> Self::HandlePongFut {
+            ready(Ok(ReturnedState))
+        }
+    }
+
+    struct ServerInitState;
+
+    struct ServerState {
+        rt: &'static Runtime,
+        act_id: Uuid,
+    }
+
+    impl ServerInit for ServerInitState {
+        type AfterActivate = ServerState;
+        type HandleActivateFut = Ready<Result<Self::AfterActivate>>;
+        fn handle_activate(self, msg: Activate) -> Self::HandleActivateFut {
+            ready(Ok(ServerState {
+                rt: msg.rt,
+                act_id: msg.act_id,
+            }))
+        }
+    }
+
+    impl Listening for ServerState {
+        type AfterPing = ReturnedState;
+        type HandlePingFut = impl Future<Output = Result<Self::AfterPing>>;
+        fn handle_ping(self, msg: Envelope<Ping>) -> Self::HandlePingFut {
+            async move {
+                let to = msg.from;
+                let from = self.rt.actor_name(self.act_id);
+                // TODO: This implementation should not know about PingProtocolMsg. It should be
+                // able to send Pong directly.
+                self.rt.send(to, from, PingProtocolMsg::Pong(Pong)).await?;
+                Ok(ReturnedState)
+            }
+        }
+    }
+
+    async fn ping_server(
+        counter: Arc<AtomicU8>,
+        rt: &'static Runtime,
+        mut mailbox: mpsc::Receiver<Envelope<PingProtocolMsg>>,
+        act_id: Uuid,
+    ) {
+        let mut state = {
+            let init = ServerInitState;
+            let state = init.handle_activate(Activate { rt, act_id }).await.unwrap();
+            PingServerState::Listening(state)
+        };
+        while let Some(envelope) = mailbox.recv().await {
+            let (msg, replier, from) = envelope.split();
+            match (state, msg) {
+                (PingServerState::Listening(listening_state), PingProtocolMsg::Ping(msg)) => {
+                    let envelope = Envelope::new(msg, replier, from);
+                    state = PingServerState::Returned(
+                        listening_state.handle_ping(envelope).await.unwrap(),
+                    );
+                }
+                _ => {
+                    log::error!("Ping protocol violation.");
+                    break;
+                }
+            }
+            if let PingServerState::Returned(_) = state {
+                break;
+            }
+        }
+        counter.fetch_sub(1, Ordering::SeqCst);
+    }
+
+    async fn ping_client(
+        counter: Arc<AtomicU8>,
+        server_name: ActorName,
+        rt: &'static Runtime,
+        mut mailbox: mpsc::Receiver<Envelope<PingProtocolMsg>>,
+        act_id: Uuid,
+    ) {
+        let mut state = {
+            let init = ClientInitState { server_name };
+            let state = init.handle_activate(Activate { rt, act_id }).await.unwrap();
+            PingClientState::SentPing(state)
+        };
+        while let Some(envelope) = mailbox.recv().await {
+            let (msg, replier, from) = envelope.split();
+            match (state, msg) {
+                (PingClientState::SentPing(curr_state), PingProtocolMsg::Pong(msg)) => {
+                    let envelope = Envelope::new(msg, replier, from);
+                    state =
+                        PingClientState::Returned(curr_state.handle_pong(envelope).await.unwrap());
+                }
+                _ => {
+                    log::error!("Ping protocol violation.");
+                    break;
+                }
+            }
+            if let PingClientState::Returned(_) = state {
+                break;
+            }
+        }
+        counter.fetch_sub(1, Ordering::SeqCst);
+    }
+
+    #[test]
+    fn ping_pong_test() {
+        ASYNC_RT.block_on(async {
+            let counter = Arc::new(AtomicU8::new(2));
+            let server_name = {
+                let counter = counter.clone();
+                RUNTIME
+                    .activate(move |rt, mailbox, act_id| ping_server(counter, rt, mailbox, act_id))
+                    .await
+            };
+            let client_name = {
+                let server_name = server_name.clone();
+                let counter = counter.clone();
+                RUNTIME
+                    .activate(move |rt, mailbox, act_id| {
+                        ping_client(counter, server_name, rt, mailbox, act_id)
+                    })
+                    .await
+            };
+
+            let deadline = Instant::now() + Duration::from_millis(500);
+            while counter.load(Ordering::SeqCst) > 0 && Instant::now() < deadline {
+                tokio::time::sleep(Duration::from_millis(20)).await;
+            }
+
+            // TODO: Should actor which return be removed from the runtime automatically?
+            RUNTIME.take(&server_name).await.unwrap();
+            RUNTIME.take(&client_name).await.unwrap();
+        });
+    }
 }