Repository: actix/actix (Rust)

Issue:
Feature Request: Way to get a `WeakAddr<A>` or something which can later be converted to `Addr<A>`
I'm writing a simple chat server and trying to represent every chat room as a corresponding `Actor`.
I need a mechanism to address these chat rooms (`fn query(key: K) -> Addr<A>`), which eventually will be something like `HashMap<K, Addr<A>>`.
But `Addr<A>` is a 'strong reference' to an `Actor`, so this registry prevents actors from dying.

Here's an example:
```rust
use std::collections::HashMap;
use std::mem::drop;

use actix::*;

#[derive(Debug)]
struct ChatRoom(u32);

impl Actor for ChatRoom {
    type Context = Context<ChatRoom>;

    fn started(&mut self, _ctx: &mut Self::Context) {
        println!("ChatRoom {} started", self.0);
    }

    fn stopped(&mut self, _ctx: &mut Self::Context) {
        println!("ChatRoom {} stopped", self.0);
    }
}

#[derive(Message)]
struct Msg(u32);

impl Handler<Msg> for ChatRoom {
    type Result = ();
    fn handle(&mut self, msg: Msg, _ctx: &mut Self::Context) {
        println!("ChatRooms receives {}", msg.0);
    }
}

struct User {
    rooms: Vec<Addr<ChatRoom>>,
    id: u32,
}

impl Actor for User {
    type Context = Context<User>;

    fn started(&mut self, _ctx: &mut Self::Context) {
        self.rooms.len(); // make rustc happy
        println!("Actor {} started", self.id);
    }

    fn stopped(&mut self, _ctx: &mut Self::Context) {
        println!("Actor {} stopped", self.id);
    }
}

fn main() {
    let sys = System::new("issue");
    let room1 = ChatRoom(1).start();
    let room2 = ChatRoom(2).start();

    let user1 = User {
        rooms: vec![room1.clone()],
        id: 1,
    }
    .start();
    let user2 = User {
        rooms: vec![room2.clone()],
        id: 2,
    }
    .start();

    // XXX: registry holds an `Addr<ChatRoom>` of ChatRoom(1)
    let mut registry: HashMap<u32, Addr<ChatRoom>> = HashMap::new();
    registry.insert(1, room1.clone());

    drop(room1);
    drop(room2);
    drop(user1);
    drop(user2);

    sys.run();
}
```
which gives output:
```
ChatRoom 1 started
ChatRoom 2 started
Actor 1 started
Actor 1 stopped
Actor 2 started
Actor 2 stopped
ChatRoom 2 stopped

```
`ChatRoom 1 stopped` is missing as expected.

I can't think of a way to accomplish this (let ChatRoom 1 die) without some serious cooperation between `ChatRoom` and `User` actors and `ChatRoom`'s registry(maybe another `Actor`).

What I propose is something like `std::rc::Weak` equivalent for `Addr<A>`.
If such thing exists the code above can be easily implemented as:
```rust
let mut registry: HashMap<u32, WeakAddr<ChatRoom>> = HashMap::new();
registry.insert(1, room1.clone().downgrade());
```


Discussion:
@fafhrd91 I need your opinion on this.
It should be possible to have weak address similar to RC pointers, but I'm somewhat 50/50 on this since weak pointers are in general very specialized thingies
I don’t have opinion on weak, I am fine with adding support for weak addr
follow up question here @fafhrd91, `WeakAddr` was in version `0.7.10`, but it was yanked.
I'd be interested in the feature, are you planning to release another `0.7.x` or should I wait for 0.8.0 instead?
We had to yank it since I removed `async` keyword and it broke actix-web.
Well I could possibly release one more `0.7` version



Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 121e81cde..a585f07a1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,11 @@
 # CHANGES
 
+## [0.7.10] (2018-12-18)
+
+### Changed
+
+- Added `WeakAddr<A>` to weak reference an actor
+
 ## [0.7.9] (2018-12-11)
 
 ### Changed
diff --git a/Cargo.toml b/Cargo.toml
index 0f052902d..b6394a1f1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "actix"
-version = "0.7.9"
+version = "0.7.10"
 authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
 description = "Actor framework for Rust"
 readme = "README.md"
diff --git a/src/address/channel.rs b/src/address/channel.rs
index d636a872c..a30500761 100644
--- a/src/address/channel.rs
+++ b/src/address/channel.rs
@@ -1,9 +1,9 @@
 //! This is copy of [sync/mpsc/](https://github.com/alexcrichton/futures-rs)
+use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::sync::atomic::Ordering::{Relaxed, SeqCst};
 use std::sync::atomic::{AtomicBool, AtomicUsize};
-use std::sync::Arc;
-use std::fmt;
+use std::sync::{Arc, Weak};
 use std::{thread, usize};
 
 use futures::sync::oneshot::{channel as sync_channel, Receiver};
@@ -61,6 +61,19 @@ impl<A: Actor> fmt::Debug for AddressSender<A> {
     }
 }
 
+/// Weak referenced version of `AddressSender`
+///
+/// This is created by the `AddressSender::downgrade` method.
+pub struct WeakAddressSender<A: Actor> {
+    inner: Weak<Inner<A>>,
+}
+
+impl<A: Actor> fmt::Debug for WeakAddressSender<A> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt.debug_struct("WeakAddressSender").finish()
+    }
+}
+
 trait AssertKinds: Send + Sync + Clone {}
 
 /// The receiving end of a channel which implements the `Stream` trait.
@@ -299,6 +312,13 @@ impl<A: Actor> AddressSender<A> {
         }
     }
 
+    /// Downgrade to `WeakAddressSender` which can later be upgraded
+    pub fn downgrade(&self) -> WeakAddressSender<A> {
+        WeakAddressSender {
+            inner: Arc::downgrade(&self.inner),
+        }
+    }
+
     // Push message to the queue and signal to the receiver
     fn queue_push_and_signal(&self, msg: Envelope<A>) {
         // Push the message onto the message queue
@@ -498,6 +518,23 @@ impl<A: Actor> Hash for AddressSender<A> {
     }
 }
 
+//
+//
+// ===== impl WeakSender =====
+//
+//
+impl<A: Actor> WeakAddressSender<A> {
+    /// Attempts to upgrade the `WeakAddressSender<A>` pointer to an [`AddressSender<A>`]
+    ///
+    /// Returns [`None`] if the actor has since been dropped.
+    pub fn upgrade(&self) -> Option<AddressSender<A>> {
+        match Weak::upgrade(&self.inner) {
+            Some(inner) => Some(AddressSenderProducer { inner }.sender()),
+            None => None,
+        }
+    }
+}
+
 //
 //
 // ===== impl SenderProducer =====
diff --git a/src/address/mod.rs b/src/address/mod.rs
index 5341f7836..c84fd056e 100644
--- a/src/address/mod.rs
+++ b/src/address/mod.rs
@@ -1,6 +1,6 @@
+use failure;
 use std::fmt;
 use std::hash::{Hash, Hasher};
-use failure;
 
 pub(crate) mod channel;
 mod envelope;
@@ -14,7 +14,7 @@ pub use self::envelope::{Envelope, EnvelopeProxy, ToEnvelope};
 pub use self::message::{RecipientRequest, Request};
 
 pub(crate) use self::channel::{AddressReceiver, AddressSenderProducer};
-use self::channel::{AddressSender, Sender};
+use self::channel::{AddressSender, Sender, WeakAddressSender};
 
 pub enum SendError<T> {
     Full(T),
@@ -56,9 +56,7 @@ impl<T> fmt::Display for SendError<T> {
     }
 }
 
-impl<T> failure::Fail for SendError<T>
-where T: Send + Sync + 'static {
-}
+impl<T> failure::Fail for SendError<T> where T: Send + Sync + 'static {}
 
 impl fmt::Debug for MailboxError {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
@@ -126,7 +124,9 @@ impl<A: Actor> Addr<A> {
     {
         match self.tx.send(msg) {
             Ok(rx) => Request::new(Some(rx), None),
-            Err(SendError::Full(msg)) => Request::new(None, Some((self.tx.clone(), msg))),
+            Err(SendError::Full(msg)) => {
+                Request::new(None, Some((self.tx.clone(), msg)))
+            }
             Err(SendError::Closed(_)) => Request::new(None, None),
         }
     }
@@ -141,6 +141,13 @@ impl<A: Actor> Addr<A> {
     {
         self.into()
     }
+
+    /// Downgrade to WeakAddr
+    pub fn downgrade(&self) -> WeakAddr<A> {
+        WeakAddr {
+            wtx: self.tx.downgrade(),
+        }
+    }
 }
 
 impl<A: Actor> Clone for Addr<A> {
@@ -165,6 +172,31 @@ impl<A: Actor> Hash for Addr<A> {
     }
 }
 
+/// Weak referenced Addr<A>
+#[derive(Debug)]
+pub struct WeakAddr<A: Actor> {
+    wtx: WeakAddressSender<A>,
+}
+
+impl<A: Actor> WeakAddr<A> {
+    /// Attempts to upgrade the `WeakAddr<A>` pointer to an [`Addr<A>`]
+    ///
+    /// Returns [`None`] if the actor has since been dropped or the underlying
+    /// is disconnected.
+    pub fn upgrade(&self) -> Option<Addr<A>> {
+        match self.wtx.upgrade() {
+            Some(tx) => {
+                if tx.connected() {
+                    Some(Addr::new(tx))
+                } else {
+                    None
+                }
+            }
+            None => None,
+        }
+    }
+}
+
 /// `Recipient` type allows to send one specific message to an actor.
 ///
 /// You can get recipient with `Addr<_, _>::recipient()` method.
@@ -209,7 +241,9 @@ where
     pub fn send(&self, msg: M) -> RecipientRequest<M> {
         match self.tx.send(msg) {
             Ok(rx) => RecipientRequest::new(Some(rx), None),
-            Err(SendError::Full(msg)) => RecipientRequest::new(None, Some((self.tx.boxed(), msg))),
+            Err(SendError::Full(msg)) => {
+                RecipientRequest::new(None, Some((self.tx.boxed(), msg)))
+            }
             Err(SendError::Closed(_)) => RecipientRequest::new(None, None),
         }
     }
@@ -322,7 +356,8 @@ mod tests {
                 .join(send3)
                 .map(|_| {
                     System::current().stop();
-                }).map_err(|_| {
+                })
+                .map_err(|_| {
                     panic!("Message over limit should be delivered, but it is not!");
                 });
             Arbiter::spawn(send);
diff --git a/src/lib.rs b/src/lib.rs
index 56eec3381..8a2fc4a21 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -95,13 +95,16 @@ pub mod registry;
 pub mod sync;
 pub mod utils;
 
-pub use actor::{Actor, ActorContext, ActorState, AsyncContext, Running, SpawnHandle, Supervised};
-pub use address::{Addr, MailboxError, Recipient};
+pub use actor::{
+    Actor, ActorContext, ActorState, AsyncContext, Running, SpawnHandle, Supervised,
+};
+pub use address::{Addr, MailboxError, Recipient, WeakAddr};
 pub use arbiter::Arbiter;
 pub use context::Context;
 pub use fut::{ActorFuture, ActorStream, FinishStream, WrapFuture, WrapStream};
 pub use handler::{
-    ActorResponse, Handler, Message, MessageResult, Response, ResponseActFuture, ResponseFuture,
+    ActorResponse, Handler, Message, MessageResult, Response, ResponseActFuture,
+    ResponseFuture,
 };
 pub use registry::{ArbiterService, Registry, SystemRegistry, SystemService};
 pub use stream::StreamHandler;
@@ -129,12 +132,15 @@ pub mod prelude {
     pub use actor::{
         Actor, ActorContext, ActorState, AsyncContext, Running, SpawnHandle, Supervised,
     };
-    pub use address::{Addr, MailboxError, Recipient, RecipientRequest, Request, SendError};
+    pub use address::{
+        Addr, MailboxError, Recipient, RecipientRequest, Request, SendError,
+    };
     pub use arbiter::Arbiter;
     pub use context::{Context, ContextFutureSpawner};
     pub use fut::{ActorFuture, ActorStream, WrapFuture, WrapStream};
     pub use handler::{
-        ActorResponse, Handler, Message, MessageResult, Response, ResponseActFuture, ResponseFuture,
+        ActorResponse, Handler, Message, MessageResult, Response, ResponseActFuture,
+        ResponseFuture,
     };
     pub use registry::{ArbiterService, SystemService};
     pub use stream::StreamHandler;


Test patch:
diff --git a/tests/test_address.rs b/tests/test_address.rs
index aad0533b4..686b4f129 100644
--- a/tests/test_address.rs
+++ b/tests/test_address.rs
@@ -4,6 +4,7 @@ extern crate tokio;
 extern crate tokio_timer;
 
 use std::collections::HashSet;
+use std::mem::drop;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
 use std::time::{Duration, Instant};
@@ -59,8 +60,14 @@ fn test_address() {
         arbiter.do_send(actix::msgs::Execute::new(move || -> Result<(), ()> {
             addr3.do_send(Ping(2));
 
-            let send_ping = addr2.send(Ping(3)).map_err(|_| panic!("Unable to send ping 3"))
-                .then(move |_| addr2.send(Ping(4)).map_err(|_| panic!("Unable to send ping 4")))
+            let send_ping = addr2
+                .send(Ping(3))
+                .map_err(|_| panic!("Unable to send ping 3"))
+                .then(move |_| {
+                    addr2
+                        .send(Ping(4))
+                        .map_err(|_| panic!("Unable to send ping 4"))
+                })
                 .then(|_| {
                     System::current().stop();
                     Ok(())
@@ -69,12 +76,46 @@ fn test_address() {
 
             Ok(())
         }));
-
     });
 
     assert_eq!(count.load(Ordering::Relaxed), 4);
 }
 
+struct WeakRunner;
+
+impl Actor for WeakRunner {
+    type Context = Context<Self>;
+
+    fn started(&mut self, ctx: &mut Self::Context) {
+        let addr1 = MyActor3.start();
+        let addr2 = MyActor3.start();
+        let weak1 = addr1.downgrade();
+        let weak2 = addr2.downgrade();
+        drop(addr1);
+
+        ctx.run_later(Duration::new(0, 1000), move |_, _| {
+            assert!(
+                weak1.upgrade().is_none(),
+                "Should not be able to upgrade weak1!"
+            );
+            match weak2.upgrade() {
+                Some(addr) => {
+                    assert!(addr2 == addr);
+                }
+                None => panic!("Should be able to upgrade weak2!"),
+            }
+            System::current().stop();
+        });
+    }
+}
+
+#[test]
+fn test_weak_address() {
+    System::run(move || {
+        WeakRunner.start();
+    });
+}
+
 #[test]
 fn test_sync_recipient_call() {
     let count = Arc::new(AtomicUsize::new(0));