diff --git a/examples/echo-server.rs b/examples/echo-server.rs index 2596ad8..f4a4185 100644 --- a/examples/echo-server.rs +++ b/examples/echo-server.rs @@ -32,7 +32,7 @@ fn srv(c: Chan<(), Rec>) { type Cli = ::Dual; fn cli(c: Chan<(), Rec>) { - let mut stdin = std::io::stdin(); + let stdin = std::io::stdin(); let mut count = 0usize; let mut c = c.enter(); diff --git a/src/lib.rs b/src/lib.rs index 211e464..b9f1ff1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -161,10 +161,37 @@ pub enum Branch { Right(R) } +impl Drop for Chan { + fn drop(&mut self) { + panic!("Session channel prematurely dropped"); + } +} + impl Chan { /// Close a channel. Should always be used at the end of your program. - pub fn close(self) { - // Consume `c` + pub fn close(mut self) { + // This method cleans up the channel without running the panicky destructor + // In essence, it calls the drop glue bypassing the `Drop::drop` method + use std::mem; + + // Create some dummy values to place the real things inside + // This is safe because nobody will read these + // mem::swap uses a similar technique (also paired with `forget()`) + let mut sender = unsafe { mem::uninitialized() }; + let mut receiver = unsafe { mem::uninitialized() }; + + // Extract the internal sender/receiver so that we can drop them + // We cannot drop directly since moving out of a type + // that implements `Drop` is disallowed + mem::swap(&mut self.0, &mut sender); + mem::swap(&mut self.1, &mut receiver); + + drop(sender);drop(receiver); // drop them + + // Ensure Chan destructors don't run so that we don't panic + // This also ensures that the uninitialized values don't get + // read at any point + mem::forget(self); } } @@ -561,18 +588,16 @@ macro_rules! offer { /// spawn(move|| send_str(tcs)); /// spawn(move|| send_usize(tcu)); /// -/// loop { -/// chan_select! { -/// (c, s) = rcs.recv() => { -/// assert_eq!("Hello, World!".to_string(), s); -/// c.close(); -/// break -/// }, -/// (c, i) = rcu.recv() => { -/// assert_eq!(42, i); -/// c.close(); -/// break -/// } +/// chan_select! { +/// (c, s) = rcs.recv() => { +/// assert_eq!("Hello, World!".to_string(), s); +/// c.close(); +/// rcu.recv().0.close(); +/// }, +/// (c, i) = rcu.recv() => { +/// assert_eq!(42, i); +/// c.close(); +/// rcs.recv().0.close(); /// } /// } /// } @@ -598,17 +623,33 @@ macro_rules! offer { /// let (c, s) = chan_one.recv(); /// assert_eq!("Hello, World!".to_string(), s); /// c.close(); +/// match chan_two.offer() { +/// Left(c) => c.recv().0.close(), +/// Right(c) => c.recv().0.close(), +/// } /// }, /// Number => { -/// unreachable!() +/// chan_one.recv().0.close(); +/// match chan_two.offer() { +/// Left(c) => c.recv().0.close(), +/// Right(c) => c.recv().0.close(), +/// } /// } /// }, /// _ign = chan_two.offer() => { /// String => { -/// unreachable!() +/// chan_two.recv().0.close(); +/// match chan_one.offer() { +/// Left(c) => c.recv().0.close(), +/// Right(c) => c.recv().0.close(), +/// } /// }, /// Number => { -/// unreachable!() +/// chan_two.recv().0.close(); +/// match chan_one.offer() { +/// Left(c) => c.recv().0.close(), +/// Right(c) => c.recv().0.close(), +/// } /// } /// } /// } @@ -622,6 +663,8 @@ macro_rules! offer { /// let (ca1, ca2) = session_channel(); /// let (cb1, cb2) = session_channel(); /// +/// cb2.sel2().send(42).close(); +/// /// spawn(move|| cli(ca2)); /// /// srv(ca1, cb1);