use core::sync::atomic::Ordering;
use std::sync::Arc;
#[cfg(ring)]
use super::Driver;
use super::Shard;
use crate::io::Source;
impl Shard {
pub(crate) fn block_on_then_leave<F: core::future::Future>(&self, future: F) -> F::Output {
let mut future = core::pin::pin!(future);
crate::rt::context::deeper(self.limits.max_nesting, || {
let signal = Arc::new(super::signal::Signal::new(&self.shared));
self.run_then_leave(future.as_mut(), &signal)
})
}
pub(crate) fn leave(&self) {
let gone: Vec<(u64, Arc<Source>)> = self.sources.borrow_mut().drain_tokens().collect();
evict_all(gone, self.shared_addr());
self.let_go_of_children();
#[cfg(ring)]
if let Driver::Ring(ring) = &self.driver {
ring.settle(|| self.apply_io_ops());
}
#[cfg(port)]
if !self.hand_off_port() {
self.settle_port();
}
}
}
fn evict_all(gone: Vec<(u64, Arc<Source>)>, home: usize) {
for (_, source) in &gone {
source.evict();
}
wait_for_evictions(&gone, home);
}
fn wait_for_evictions(gone: &[(u64, Arc<Source>)], home: usize) {
for (token, source) in gone {
while source.registered_with(home, *token) {
std::thread::yield_now();
}
}
}
#[cfg(all(test, unix, not(loom)))]
mod tests {
use core::future::{Future, poll_fn};
use core::task::Poll;
use std::sync::Arc;
use std::time::Duration;
#[test]
fn a_shard_that_goes_does_not_wait_for_a_registration_elsewhere_under_its_token() {
let (mine, _peer) = std::os::unix::net::UnixStream::pair().unwrap();
let handle = crate::io::Handle::new(mine).unwrap();
let src = Arc::clone(handle.source());
crate::block_on(async {
let mut buf = [0u8; 1];
{
let mut read = Box::pin(handle.recv(&mut buf));
let polled = poll_fn(|cx| Poll::Ready(read.as_mut().poll(cx))).await;
assert!(polled.is_pending());
}
let (home, token) = src.registration();
assert_ne!(home, 0, "the receive did not register the source");
let (done_tx, done_rx) = std::sync::mpsc::channel();
let other = {
let src = Arc::clone(&src);
std::thread::spawn(move || {
crate::block_on(async move {
let there =
crate::rt::context::current(|s| s.map(super::Shard::shared_addr))
.expect("a block_on runs a shard");
assert_ne!(there, home);
super::wait_for_evictions(&[(token, src)], there);
});
let _ = done_tx.send(());
})
};
assert!(
done_rx.recv_timeout(Duration::from_secs(10)).is_ok(),
"the other shard waited for this shard's registration"
);
other.join().unwrap();
assert_eq!(
src.registration(),
(home, token),
"the registration here moved"
);
});
drop(handle);
}
}
impl Drop for Shard {
fn drop(&mut self) {
self.shared.counters.went();
self.shared.draining.store(true, Ordering::SeqCst);
self.shared.stopped.store(true, Ordering::SeqCst);
drop(self.shared.stop_waker.take());
let gone: Vec<(u64, Arc<Source>)> = self.sources.get_mut().drain_tokens().collect();
evict_all(gone, self.shared_addr());
self.let_go_of_children();
#[cfg(ring)]
if let Driver::Ring(ring) = &self.driver {
ring.settle(|| self.apply_io_ops());
ring.shut_down();
}
#[cfg(port)]
if !self.port_kept() && !self.hand_off_port() {
self.settle_port();
}
loop {
let lifo = self.lifo.take();
let queue = core::mem::take(&mut *self.queue.borrow_mut());
let timers = self.wheel.borrow_mut().take_wakers();
let fired = core::mem::take(&mut *self.fired.borrow_mut());
let mut quiet =
lifo.is_none() && queue.is_empty() && timers.is_empty() && fired.is_empty();
drop((lifo, queue, timers, fired));
while let Some(runnable) = self.pop_inbox() {
drop(runnable);
quiet = false;
}
if quiet {
break;
}
}
self.shared.drop_stopped(true);
}
}