diff --git a/src/runtime2/global_store.rs b/src/runtime2/global_store.rs new file mode 100644 index 0000000000000000000000000000000000000000..ca7b4ea548b598fca2e83d3555780fed381fbefb --- /dev/null +++ b/src/runtime2/global_store.rs @@ -0,0 +1,132 @@ +use crate::collections::{MpmcQueue, RawVec}; + +use super::connector::Connector; + +use std::ptr; +use std::sync::RwLock; + +/// A kind of token that, once obtained, allows access to a container. +struct ConnectorKey { + index: u32, // of connector +} + +struct ConnectorStore { + connectors: RawVec<*mut Connector>, + free: Vec, +} + +impl ConnectorStore { + fn with_capacity(capacity: usize) -> Self { + Self{ + connectors: RawVec::with_capacity(capacity), + free: Vec::with_capacity(capacity), + } + } + + fn get_mut(&self, key: &ConnectorKey) -> &'static mut Connector { + unsafe { + let connector = self.connectors.get_mut(key.index as usize); + debug_assert!(!connector.is_null()); + return *connector as &mut _; + } + } + + fn create(&mut self, connector: Connector) -> ConnectorKey { + let index; + if self.free.is_empty() { + let connector = Box::into_raw(Box::new(connector)); + + unsafe { + // Cheating a bit here. Anyway, move to heap, store in list + index = self.connectors.len(); + self.connectors.push(connector); + } + } else { + index = self.free.pop().unwrap(); + + unsafe { + let target = self.connectors.get_mut(index); + debug_assert!(!target.is_null()); + ptr::write(*target, connector); + } + } + + return ConnectorKey{ index: index as u32 }; + } + + fn destroy(&mut self, key: ConnectorKey) { + unsafe { + let connector = self.connectors.get_mut(key.index as usize); + ptr::drop_in_place(*connector); + // Note: but not deallocating! + } + + self.free.push(key.index as usize); + } +} + +impl Drop for ConnectorStore { + fn drop(&mut self) { + for idx in 0..self.connectors.len() { + unsafe { + let memory = *self.connectors.get_mut(idx); + let boxed = Box::from_raw(memory); // takes care of deallocation + } + } + } +} + +/// Global store of connectors, ports and queues that are used by the sceduler +/// threads. The global store has the appearance of a thread-safe datatype, but +/// one needs to be careful using it. +/// +/// The intention of this data structure is to enforce the rules: +/// TODO: @docs +pub struct GlobalStore { + connector_queue: MpmcQueue, + connectors: RwLock, +} + +impl GlobalStore { + pub fn new() -> Self { + Self{ + connector_queue: MpmcQueue::with_capacity(256), + connectors: RwLock::new(ConnectorStore::with_capacity(256)), + } + } + + // Taking connectors out of global queue + + pub fn pop_key(&self) -> Option { + return self.connector_queue.pop_front(); + } + + pub fn push_key(&self, key: ConnectorKey) { + self.connector_queue.push_back(key); + } + + // Creating, retrieving and destroying connectors + + /// Retrieves a connector using the provided key. Note that the returned + /// reference is not truly static, the `GlobalStore` needs to stay alive. + pub fn get_connector(&self, key: &ConnectorKey) -> &'static mut Connector { + let connectors = self.connectors.read().unwrap(); + return connectors.get_mut(key); + } + + /// Adds a connector to the global system. Will also queue it to run + pub fn add_connector(&self, connector: Connector) { + let key = { + let mut connectors = self.connectors.write().unwrap(); + connectors.create(connector) + }; + + self.connector_queue.push_back(key); + } + + /// Destroys a connector + pub fn destroy_connector(&self, key: ConnectorKey) { + let mut connectors = self.connectors.write().unwrap(); + connectors.destroy(key); + } +} \ No newline at end of file