use core_types::WasmNotSend; use core_types::graphene_hash::CacheHash; use core_types::memo::*; use std::hash::DefaultHasher; use std::hash::Hasher; use std::sync::Arc; use std::sync::Mutex; /// Caches the output of a given node called with a specific input. /// /// A cache miss occurs when the Option is None. In this case, the node evaluates the inner node and memoizes (stores) the result. /// /// A cache hit occurs when the Option is Some and has a stored hash matching the hash of the call argument. In this case, the node returns the cached value without re-evaluating the inner node. /// /// Currently, only one input-output pair is cached. Subsequent calls with different inputs will overwrite the previous cache. #[node_macro::node(category(""), path(graphene_core::memo), skip_impl)] async fn memo(input: I, #[data] cache: Arc>>, node: impl Node) -> T { let mut hasher = DefaultHasher::new(); input.cache_hash(&mut hasher); let hash = hasher.finish(); if let Some(data) = cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) { return data; } let value = node.eval(input).await; *cache.lock().unwrap() = Some((hash, value.clone())); value } type MonitorValue = Arc>>>>; /// Caches the output of the last graph evaluation for introspection. #[node_macro::node(category(""), path(graphene_core::memo), serialize(serialize_monitor), skip_impl)] async fn monitor( input: I, #[allow(clippy::type_complexity)] #[data] io: MonitorValue, node: impl Node, ) -> T { let output = node.eval(input.clone()).await; *io.lock().unwrap() = Some(Arc::new(IORecord { input, output: output.clone() })); output } fn serialize_monitor(io: &MonitorValue) -> Option> { let io = io.lock().unwrap(); io.as_ref().map(|output| output.clone() as Arc) }