///|
/// Returns the Derived entry for `key`, creating and caching it on first access.
fn[K : Hash + Eq, V : Eq] DerivedMap::get_or_create_entry(
self : DerivedMap[K, V],
key : K,
) -> Derived[V] {
match self.entries.get(key) {
Some(entry) if !entry.is_disposed() => entry
_ => {
let entry = match self.label {
Some(label) => Derived(self.rt, () => (self.compute)(key), label~)
None => Derived(self.rt, () => (self.compute)(key))
}
self.entries.set(key, entry)
entry
}
}
}
///|
/// Package-private strict honest read. Requires an active tracked context;
/// reports cycles as `ReadError` after recreating GC-disposed private entries.
fn[K : Hash + Eq, V : Eq] DerivedMap::get_strict_honest(
self : DerivedMap[K, V],
key : K,
) -> Result[V, ReadError] {
guard self.rt.core.tracking.stack.length() > 0 else {
abort(
"DerivedMap::get() called outside tracked context. Use DerivedMap::read(key) or DerivedMap::read_or_abort(key) to read from outside the graph.",
)
}
self.read_honest(key)
}
///|
/// Package-private honest permissive read — the single source of truth for the
/// read channel. A per-key entry that gc disposed is recreated before reading;
/// a cycle returns `Err(Cycle(_))`; a compute `raise Failure` is a defect and
/// still aborts.
fn[K : Hash + Eq, V : Eq] DerivedMap::read_honest(
self : DerivedMap[K, V],
key : K,
) -> Result[V, ReadError] {
let entry = self.get_or_create_entry(key)
let result = entry.get_result_inner() catch {
e => {
self.rt.drain_pending_events_direct()
abort("DerivedMap compute raised: " + e.to_string())
}
}
if self.rt.has_pending_events() {
self.rt.drain_pending_events_if_idle()
}
match result {
Ok(v) => Ok(v)
Err(e) => Err(ReadError::cycle(e))
}
}
///|
/// Package-private strict Result read. Requires an active tracked context.
/// Compatibility projection of `get_strict_honest` onto `CycleError`.
fn[K : Hash + Eq, V : Eq] DerivedMap::get_strict_result(
self : DerivedMap[K, V],
key : K,
) -> Result[V, CycleError] {
guard self.rt.core.tracking.stack.length() > 0 else {
abort(
"DerivedMap::get() called outside tracked context. Use DerivedMap::read(key) or DerivedMap::read_or_abort(key) to read from outside the graph.",
)
}
self.read_result(key)
}
///|
/// Package-private permissive Result read. Records a dependency when tracked.
/// Compatibility projection of `read_honest` onto `CycleError`: a cycle is
/// `Err(CycleError)` and any lifecycle error aborts. GC-disposed private
/// per-key entries are recreated by `read_honest` before this projection.
fn[K : Hash + Eq, V : Eq] DerivedMap::read_result(
self : DerivedMap[K, V],
key : K,
) -> Result[V, CycleError] {
match self.read_honest(key) {
Ok(v) => Ok(v)
Err(ReadError::Cycle(e)) => Err(e)
Err(ReadError::Disposed(_)) =>
abort("DerivedMap::read called on a disposed entry")
}
}
///|
fn[K : Hash + Eq, V] DerivedMap::sweep_entries(self : DerivedMap[K, V]) -> Int {
let to_remove : Array[K] = []
for key, entry in self.entries {
if entry.is_disposed() {
to_remove.push(key)
}
}
for key in to_remove {
self.entries.remove(key)
}
to_remove.length()
}
///|
fn[K, V] DerivedMap::clear_entries(self : DerivedMap[K, V]) -> Unit {
for _, entry in self.entries {
entry.dispose()
}
self.entries.clear()
}