gled-core 2.31.2

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! Every thread keeps its own copy of the asset collections. Saving one copy
//! sends it to all others, which pick it up with `update`. The model's
//! `Collections` is reached through `Deref`, so model code taking
//! `&Collections` accepts this replica as well.

use crate::storage::collection::Collections as AssetCollections;
use kanal::{Receiver, Sender, bounded};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::{
    collections::HashMap,
    ops::{Deref, DerefMut},
    sync::atomic::AtomicUsize,
};

static CURRENT: Lazy<Mutex<AssetCollections>> =
    Lazy::new(|| Mutex::new(AssetCollections::default()));
static COLLECTION_ID: AtomicUsize = AtomicUsize::new(0);
static SENDERS: Lazy<Mutex<HashMap<usize, Sender<AssetCollections>>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

pub(crate) struct Collections {
    id: usize,
    receiver: Receiver<AssetCollections>,
    current: AssetCollections,
    /// Counts the changes this copy saw: its own saves and other copies'.
    generation: u64,
}

impl std::fmt::Debug for Collections {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Collections").finish_non_exhaustive()
    }
}

impl Default for Collections {
    fn default() -> Self {
        let (sender, receiver) = bounded(8);
        let id = COLLECTION_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
        SENDERS.lock().insert(id, sender);

        Self {
            id,
            receiver,
            current: CURRENT.lock().clone(),
            generation: 0,
        }
    }
}

impl Drop for Collections {
    fn drop(&mut self) {
        SENDERS.lock().remove(&self.id);
    }
}

impl Deref for Collections {
    type Target = AssetCollections;

    fn deref(&self) -> &Self::Target {
        &self.current
    }
}

impl DerefMut for Collections {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.current
    }
}

impl Collections {
    #[cfg_attr(feature = "profiling", profiling::function)]
    pub(crate) fn update(&mut self) {
        while let Ok(Some(new_collections)) = self.receiver.try_recv() {
            self.current = new_collections;
            self.generation += 1;
        }
    }

    /// Changes whenever the assets changed, through `update` or `save`.
    pub(crate) fn generation(&self) -> u64 {
        self.generation
    }

    /// Sends this copy to every other one.
    pub(crate) fn save(&mut self) {
        self.generation += 1;
        for (id, sender) in SENDERS.lock().iter() {
            if id == &self.id {
                continue;
            }

            sender
                .send(self.current.clone())
                .expect("Could not send collections");
        }

        CURRENT.lock().clone_from(&self.current);
    }
}