use std::mem;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{atomic, Arc};
use crate::core::utils::Sealed;
pub trait WillClose: Sealed {
fn is_closed(&self) -> bool {
true
}
fn to_closable(&self) -> Closable;
}
#[derive(Debug)]
#[must_use]
pub struct Closer(Arc<AtomicBool>);
impl Closer {
pub fn new() -> Self {
Self(Arc::new(AtomicBool::new(false)))
}
pub fn to_closable(&self) -> Closable {
Closable(self.0.clone())
}
pub fn to_shared(&self) -> SharedCloser {
SharedCloser {
flag: self.0.clone(),
owners: Arc::new(AtomicUsize::new(1)),
associated: true,
}
}
pub fn into_shared(mut self) -> SharedCloser {
let mut flag = Arc::new(AtomicBool::new(false));
mem::swap(&mut self.0, &mut flag);
SharedCloser {
flag,
owners: Arc::new(AtomicUsize::new(1)),
associated: false,
}
}
pub fn close(&mut self) {
self.0.store(true, atomic::Ordering::Release);
}
pub fn is_closed(&self) -> bool {
self.0.load(atomic::Ordering::Acquire)
}
}
impl Sealed for Closer {}
impl WillClose for Closer {
fn is_closed(&self) -> bool {
self.is_closed()
}
fn to_closable(&self) -> Closable {
self.to_closable()
}
}
impl Default for Closer {
fn default() -> Self {
Self::new()
}
}
impl Drop for Closer {
fn drop(&mut self) {
self.close()
}
}
#[derive(Debug)]
#[must_use]
pub struct SharedCloser {
flag: Arc<AtomicBool>,
owners: Arc<AtomicUsize>,
associated: bool,
}
impl SharedCloser {
pub fn new() -> Self {
Self {
flag: Arc::new(AtomicBool::new(false)),
owners: Arc::new(AtomicUsize::new(1)),
associated: false,
}
}
pub fn to_closable(&self) -> Closable {
Closable(self.flag.clone())
}
pub fn close(&mut self) {
self.flag.store(true, atomic::Ordering::Release);
}
pub fn discard(mut self) {
if !self.associated && self.owners.load(atomic::Ordering::Acquire) <= 1 {
self.close();
}
let mut empty = Arc::new(AtomicBool::new(false));
mem::swap(&mut empty, &mut self.flag);
}
pub fn is_closed(&self) -> bool {
self.flag.load(atomic::Ordering::Acquire)
}
}
impl Sealed for SharedCloser {}
impl WillClose for SharedCloser {
fn is_closed(&self) -> bool {
self.is_closed()
}
fn to_closable(&self) -> Closable {
self.to_closable()
}
}
impl Default for SharedCloser {
fn default() -> Self {
Self::new()
}
}
impl Clone for SharedCloser {
fn clone(&self) -> Self {
let owners = self.owners.clone();
owners.fetch_add(1, atomic::Ordering::Release);
Self {
flag: self.flag.clone(),
owners,
associated: self.associated,
}
}
}
impl Drop for SharedCloser {
fn drop(&mut self) {
if self.owners.fetch_sub(1, atomic::Ordering::Release) <= 1 {
self.flag.store(true, atomic::Ordering::Release);
}
}
}
#[derive(Clone, Debug)]
#[must_use]
pub struct Closable(Arc<AtomicBool>);
impl Closable {
pub fn is_closed(&self) -> bool {
self.0.load(atomic::Ordering::Acquire)
}
}
impl Sealed for Closable {}
impl WillClose for Closable {
#[inline(always)]
fn is_closed(&self) -> bool {
self.is_closed()
}
fn to_closable(&self) -> Closable {
self.clone()
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Closed;
impl Closed {
pub const fn is_closed(&self) -> bool {
true
}
pub fn to_closable(&self) -> Closable {
Closable(Arc::new(AtomicBool::new(false)))
}
}
impl Sealed for Closed {}
impl WillClose for Closed {
#[inline(always)]
fn is_closed(&self) -> bool {
self.is_closed()
}
fn to_closable(&self) -> Closable {
self.to_closable()
}
}
#[cfg(test)]
mod test_closable {
use super::*;
#[test]
fn closer_state_is_passing() {
let mut closer = Closer::new();
assert!(!closer.is_closed());
let closable_1 = closer.to_closable();
let closable_2 = closer.to_closable();
assert!(!closable_1.is_closed());
assert!(!closable_2.is_closed());
closer.close();
assert!(closer.is_closed());
assert!(closable_1.is_closed());
assert!(closable_2.is_closed());
}
#[test]
fn closer_drop_means_closed() {
let closer = Closer::new();
let closable_1 = closer.to_closable();
let closable_2 = closer.to_closable();
drop(closer);
assert!(closable_1.is_closed());
assert!(closable_2.is_closed());
}
#[test]
fn closer_arc_drop_mechanics() {
let closer_1 = Arc::new(Closer::new());
let closer_2 = closer_1.clone();
let closable_1 = closer_1.to_closable();
let closable_2 = closer_2.to_closable();
drop(closer_1);
assert!(!closable_2.is_closed());
assert!(!closable_1.is_closed());
assert!(!closable_2.is_closed());
drop(closer_2);
assert!(closable_1.is_closed());
assert!(closable_2.is_closed());
}
#[test]
fn dependent_shared_closers_may_trigger_close() {
let closer = Closer::new();
let shared_closer = closer.to_shared();
drop(shared_closer);
assert!(closer.is_closed());
}
#[test]
fn shared_closers_behave_as_arc_closers() {
let shared_closer = SharedCloser::new();
let mut other_shared_closers = Vec::new();
for _ in 0..100 {
other_shared_closers.push(shared_closer.clone())
}
let closable_1 = shared_closer.to_closable();
let closable_2 = closable_1.clone();
for _ in 0..100 {
other_shared_closers.pop();
assert!(!shared_closer.is_closed());
assert!(!closable_1.is_closed());
assert!(!closable_2.is_closed());
}
drop(shared_closer);
assert!(closable_1.is_closed());
assert!(closable_2.is_closed());
}
#[test]
fn dependent_shared_closers_can_be_discarded() {
let closer = Closer::new();
closer.to_shared().discard();
assert!(!closer.is_closed());
}
#[test]
fn drop_after_discard() {
let closer = Closer::new();
let shared_closer_1 = closer.to_shared();
let shared_closer_2 = shared_closer_1.clone();
shared_closer_1.discard();
drop(shared_closer_2);
assert!(closer.is_closed());
}
#[test]
fn discard_after_drop() {
let closer = Closer::new();
let shared_closer_1 = closer.to_shared();
let shared_closer_2 = shared_closer_1.clone();
drop(shared_closer_1);
shared_closer_2.discard();
assert!(!closer.is_closed());
}
#[test]
fn standalone_discard_closes() {
let shared_closer = SharedCloser::new();
let closable = shared_closer.to_closable();
shared_closer.discard();
assert!(closable.is_closed());
}
}