cycle_ptr 0.1.0

Smart pointers, with cycles
//! Provide an implementation of identifiers for generations.
//!
//! These identifiers are unique sequential numbers,
//! and mostly correspond with the age of a generation
//! (bigger numbers indicate a generation is younger).

#[cfg(not(feature = "single_generation"))]
use std::cell::RefCell;
#[cfg(not(all(
    feature = "single_generation",
    any(feature = "single_generation_mt", not(feature = "multi_thread"))
)))]
use std::cmp::Ordering;
#[cfg(not(feature = "single_generation"))]
use std::marker::PhantomData;
#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
use std::sync::atomic::AtomicUsize;
#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
use std::sync::atomic::Ordering as AtomicOrdering;

/// Sequence type used for identifiers.
#[cfg(not(all(
    feature = "single_generation",
    any(feature = "single_generation_mt", not(feature = "multi_thread"))
)))]
type SeqType = usize;

#[cfg(not(feature = "single_generation"))]
thread_local! {
    static TLS_SEQ: RefCell<SeqType> = const{RefCell::new(0)};
}

/// Sequence generator for [MTGenerationId].
#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
static MT_TLS_SEQ: AtomicUsize = const { AtomicUsize::new(0) };

/// An ID for generations that are only used on the current thread.
///
/// Doesn't need to be unique, but mostly will be.
#[derive(Clone, Debug)]
#[cfg(not(feature = "single_generation"))]
pub(crate) struct GenerationId {
    /// Sequence number of the generation.
    seq: SeqType,

    /// Prevent this thing from crossing threads.
    no_send_sync: PhantomData<*mut u8>,
}

#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
#[derive(Clone, Debug)]
/// An ID for generations that are used for [Send] types.
///
/// Doesn't need to be unique, but mostly will be.
pub(crate) struct MTGenerationId {
    /// Sequence number of the generation.
    seq: SeqType,
}

#[cfg(not(feature = "single_generation"))]
impl Default for GenerationId {
    fn default() -> Self {
        GenerationId {
            seq: TLS_SEQ.with_borrow_mut(|v: &mut SeqType| {
                let seq = *v;
                *v = v.wrapping_add(1);
                seq
            }),
            no_send_sync: PhantomData,
        }
    }
}

#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
impl Default for MTGenerationId {
    fn default() -> Self {
        MTGenerationId {
            seq: MT_TLS_SEQ.fetch_add(1, AtomicOrdering::Relaxed),
        }
    }
}

#[cfg(not(feature = "single_generation"))]
impl PartialEq for GenerationId {
    fn eq(&self, other: &Self) -> bool {
        self.seq == other.seq
    }
}

#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
impl PartialEq for MTGenerationId {
    fn eq(&self, other: &Self) -> bool {
        self.seq == other.seq
    }
}

#[cfg(not(feature = "single_generation"))]
impl Eq for GenerationId {}

#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
impl Eq for MTGenerationId {}

#[cfg(not(feature = "single_generation"))]
impl PartialOrd for GenerationId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
impl PartialOrd for MTGenerationId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[cfg(not(feature = "single_generation"))]
impl Ord for GenerationId {
    fn cmp(&self, other: &Self) -> Ordering {
        self.seq.cmp(&other.seq)
    }
}

#[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
impl Ord for MTGenerationId {
    fn cmp(&self, other: &Self) -> Ordering {
        self.seq.cmp(&other.seq)
    }
}

#[cfg(test)]
mod tests {
    #[cfg(not(feature = "single_generation"))]
    use super::GenerationId;
    #[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
    use super::MTGenerationId;

    #[test]
    #[cfg(not(feature = "single_generation"))]
    fn dfl() {
        let gid = GenerationId::default();
        let next_gid = GenerationId::default();

        assert_ne!(gid, next_gid);
    }

    #[test]
    #[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
    fn dfl_mt() {
        let gid = MTGenerationId::default();
        let next_gid = MTGenerationId::default();

        assert_ne!(gid, next_gid);
    }

    #[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
    fn is_sendable<T>(_: &T) -> bool
    where
        T: Send,
    {
        true
    }

    #[test]
    #[cfg(all(feature = "multi_thread", not(feature = "single_generation_mt")))]
    fn mt_sendable() {
        let gid = MTGenerationId::default();

        assert!(is_sendable(&gid));
    }

    // XXX We would like to have a test that assert GenerationId doesn't implement Send or Sync.
}