Skip to content

Commit 0f83c20

Browse files
authored
Add ptr_eq and ptr_hash to Shared (#2673)
1 parent e196c16 commit 0f83c20

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

futures-util/src/future/future/shared.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use futures_core::task::{Context, Poll, Waker};
44
use slab::Slab;
55
use std::cell::UnsafeCell;
66
use std::fmt;
7+
use std::hash::Hasher;
78
use std::pin::Pin;
9+
use std::ptr;
810
use std::sync::atomic::AtomicUsize;
911
use std::sync::atomic::Ordering::{Acquire, SeqCst};
1012
use std::sync::{Arc, Mutex, Weak};
@@ -156,6 +158,35 @@ where
156158
pub fn weak_count(&self) -> Option<usize> {
157159
self.inner.as_ref().map(Arc::weak_count)
158160
}
161+
162+
/// Hashes the internal state of this `Shared` in a way that's compatible with `ptr_eq`.
163+
pub fn ptr_hash<H: Hasher>(&self, state: &mut H) {
164+
match self.inner.as_ref() {
165+
Some(arc) => {
166+
state.write_u8(1);
167+
ptr::hash(Arc::as_ptr(arc), state);
168+
}
169+
None => {
170+
state.write_u8(0);
171+
}
172+
}
173+
}
174+
175+
/// Returns `true` if the two `Shared`s point to the same future (in a vein similar to
176+
/// `Arc::ptr_eq`).
177+
///
178+
/// Returns `false` if either `Shared` has terminated.
179+
pub fn ptr_eq(&self, rhs: &Self) -> bool {
180+
let lhs = match self.inner.as_ref() {
181+
Some(lhs) => lhs,
182+
None => return false,
183+
};
184+
let rhs = match rhs.inner.as_ref() {
185+
Some(rhs) => rhs,
186+
None => return false,
187+
};
188+
Arc::ptr_eq(lhs, rhs)
189+
}
159190
}
160191

161192
impl<Fut> Inner<Fut>

futures/tests/future_shared.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,52 @@ fn downgrade() {
151151
assert!(shared2.downgrade().is_none());
152152
}
153153

154+
#[test]
155+
fn ptr_eq() {
156+
use future::FusedFuture;
157+
use std::collections::hash_map::DefaultHasher;
158+
use std::hash::Hasher;
159+
160+
let (tx, rx) = oneshot::channel::<i32>();
161+
let shared = rx.shared();
162+
let mut shared2 = shared.clone();
163+
let mut hasher = DefaultHasher::new();
164+
let mut hasher2 = DefaultHasher::new();
165+
166+
// Because these two futures share the same underlying future,
167+
// `ptr_eq` should return true.
168+
assert!(shared.ptr_eq(&shared2));
169+
// Equivalence relations are symmetric
170+
assert!(shared2.ptr_eq(&shared));
171+
172+
// If `ptr_eq` returns true, they should hash to the same value.
173+
shared.ptr_hash(&mut hasher);
174+
shared2.ptr_hash(&mut hasher2);
175+
assert_eq!(hasher.finish(), hasher2.finish());
176+
177+
tx.send(42).unwrap();
178+
assert_eq!(block_on(&mut shared2).unwrap(), 42);
179+
180+
// Now that `shared2` has completed, `ptr_eq` should return false.
181+
assert!(shared2.is_terminated());
182+
assert!(!shared.ptr_eq(&shared2));
183+
184+
// `ptr_eq` should continue to work for the other `Shared`.
185+
let shared3 = shared.clone();
186+
let mut hasher3 = DefaultHasher::new();
187+
assert!(shared.ptr_eq(&shared3));
188+
189+
shared3.ptr_hash(&mut hasher3);
190+
assert_eq!(hasher.finish(), hasher3.finish());
191+
192+
let (_tx, rx) = oneshot::channel::<i32>();
193+
let shared4 = rx.shared();
194+
195+
// And `ptr_eq` should return false for two futures that don't share
196+
// the underlying future.
197+
assert!(!shared.ptr_eq(&shared4));
198+
}
199+
154200
#[test]
155201
fn dont_clone_in_single_owner_shared_future() {
156202
let counter = CountClone(Rc::new(Cell::new(0)));

0 commit comments

Comments
 (0)