cljrs-value 0.2.32

Runtime Value type and persistent collections for clojurust
Documentation
use super::Snapshot;
use crate::hash::hash_combine_unordered;
use crate::{ClojureHash, PersistentHashSet, Value, ValueError, ValueResult};
use std::sync::{
    Mutex,
    atomic::{AtomicBool, AtomicU64, Ordering},
};

#[derive(Debug)]
pub struct TransientSet {
    set: Mutex<PersistentHashSet>,
    persisted: Mutex<bool>,
    revision: AtomicU64,
    keys_may_realize: AtomicBool,
}

impl TransientSet {
    pub fn new() -> Self {
        TransientSet {
            set: Mutex::new(PersistentHashSet::empty()),
            persisted: Mutex::new(false),
            revision: AtomicU64::new(0),
            keys_may_realize: AtomicBool::new(false),
        }
    }

    pub fn new_from_set(set: &PersistentHashSet) -> TransientSet {
        TransientSet {
            set: Mutex::new(set.clone()),
            persisted: Mutex::new(false),
            revision: AtomicU64::new(0),
            keys_may_realize: AtomicBool::new(!(set.iter().all(crate::realization::is_inert))),
        }
    }

    fn update(
        &self,
        key: &Value,
        fast: impl FnOnce(&mut PersistentHashSet),
        operation: impl Fn(&PersistentHashSet) -> PersistentHashSet,
    ) -> ValueResult<()> {
        {
            let mut collection = self.set.lock().unwrap();
            if *self.persisted.lock().unwrap() {
                return Err(ValueError::TransientAlreadyPersisted);
            }
            if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(key) {
                fast(&mut collection);
                self.revision.fetch_add(1, Ordering::Relaxed);
                return Ok(());
            }
        }
        loop {
            let (snapshot, revision) = {
                let set = self.set.lock().unwrap();
                if *self.persisted.lock().unwrap() {
                    return Err(ValueError::TransientAlreadyPersisted);
                }
                (
                    Snapshot::new(set.clone()),
                    self.revision.load(Ordering::Relaxed),
                )
            };
            let candidate = operation(snapshot.get());
            let mut set = self.set.lock().unwrap();
            if *self.persisted.lock().unwrap() {
                return Err(ValueError::TransientAlreadyPersisted);
            }
            if self.revision.load(Ordering::Relaxed) != revision {
                continue;
            }
            *set = candidate;
            if !crate::realization::is_inert(key) {
                self.keys_may_realize.store(true, Ordering::Relaxed);
            }
            self.revision.fetch_add(1, Ordering::Relaxed);
            return Ok(());
        }
    }
    pub fn conj(&self, value: Value) -> ValueResult<()> {
        self.update(
            &value,
            |set| {
                set.conj_mut(value.clone());
            },
            |set| set.conj(value.clone()),
        )
    }
    pub fn disj(&self, value: &Value) -> ValueResult<()> {
        self.update(value, |set| set.disj_mut(value), |set| set.disj(value))
    }

    pub fn persistent(&self) -> ValueResult<PersistentHashSet> {
        let set = self.set.lock().unwrap();
        let mut persisted = self.persisted.lock().unwrap();
        if *persisted {
            return Err(ValueError::TransientAlreadyPersisted);
        }
        *persisted = true;
        Ok(set.clone())
    }

    pub fn count(&self) -> usize {
        let set = self.set.lock().unwrap();
        set.count()
    }

    pub fn contains(&self, value: &Value) -> bool {
        let set = self.set.lock().unwrap();
        if !self.keys_may_realize.load(Ordering::Relaxed) && crate::realization::is_inert(value) {
            return set.contains(value);
        }
        let snapshot = Snapshot::new(set.clone());
        drop(set);
        snapshot.get().contains(value)
    }
}

impl Clone for TransientSet {
    fn clone(&self) -> Self {
        Self {
            set: Mutex::new(self.set.lock().unwrap().clone()),
            persisted: Mutex::new(*self.persisted.lock().unwrap()),
            revision: AtomicU64::new(0),
            keys_may_realize: AtomicBool::new(self.keys_may_realize.load(Ordering::Relaxed)),
        }
    }
}

impl ClojureHash for TransientSet {
    fn clojure_hash(&self) -> u32 {
        let mut hash: u32 = 0;
        let snapshot = Snapshot::new(self.set.lock().unwrap().clone());
        for v in snapshot.get().iter() {
            hash = hash_combine_unordered(hash, v.clojure_hash())
        }
        hash
    }
}

impl cljrs_gc::Trace for TransientSet {
    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
        {
            let set = self.set.lock().unwrap();
            for v in set.iter() {
                v.trace(visitor);
            }
        }
    }

    fn gc_size_extra(&self) -> usize {
        let set = self.set.lock().unwrap();
        set.full_size_extra()
    }
}

impl Default for TransientSet {
    fn default() -> Self {
        Self::new()
    }
}