///|
// Each domain retains its concrete timestamp type. Boundary closures carry only
// antichains, so no timestamp erasure or synthetic capability is necessary.
priv struct ProgressDomain {
prepare : () -> Bool
seed : () -> Unit
publish : () -> Unit
reclaim : () -> Bool
}
///|
priv struct ProgressNetwork {
domains : Array[ProgressDomain]
links : Array[() -> Bool]
topology : Array[(Int32, Int32, Int32, Int32)]
mut workers : Int32
}
///|
fn ProgressNetwork::ProgressNetwork() -> ProgressNetwork {
{ domains: [], links: [], topology: [], workers: 0, }
}
///|
fn ProgressNetwork::pump(self : ProgressNetwork) -> Unit {
let mut changed = false
for domain in self.domains {
// Do not short-circuit: all count transactions precede propagation.
let prepared = (domain.prepare)()
changed = changed || prepared
}
if !changed {
return
}
for domain in self.domains {
(domain.seed)()
}
let mut steps = 0
for ;; {
let mut advanced = false
for transfer in self.links {
let moved = transfer()
advanced = advanced || moved
}
if !advanced {
break
}
steps += 1
guard steps < 1000000 else {
abort("cross-scope reachability did not converge")
}
}
// Observers see only the global fixed point, never an intermediate frontier.
for domain in self.domains {
(domain.publish)()
}
}
///|
fn[A, B : Lattice, S, U] ProgressNetwork::link(
self : ProgressNetwork,
source : ScopeProgress[A, S],
input : Int32,
target : ScopeProgress[B, U],
output : Int32,
translate : (A) -> B,
) -> Unit {
self.topology.push((source.id, input, target.id, output))
self.links.push(fn() {
if source.disposed || target.disposed {
return false
}
let times = source.transient[input].elements.map(translate)
target.include_frontier(output, times)
})
}
///|
// Stable IDs remain valid for external handles and manifests, but completed
// local domains no longer retain reachability matrices, observers or boundary
// closures. Coordinated domains require a separate group retirement decision.
fn ProgressNetwork::reclaim_completed(self : ProgressNetwork) -> Unit {
let retired = []
for id, domain in self.domains {
if (domain.reclaim)() {
retired.push(id)
self.domains[id] = {
prepare: fn() { false },
seed: fn() { () },
publish: fn() { () },
reclaim: fn() { false },
}
}
}
if !retired.is_empty() {
for index, (source, _, target, _) in self.topology {
if retired.contains(source) || retired.contains(target) {
self.links[index] = fn() { false }
}
}
}
}