///|
let global_scopes : @slotmap.SlotMap[Scope] = SlotMap()
///|
let root_scope : @slotmap.Id = global_scopes.allocate_with(id => {
id,
parent: None,
sub_scopes: Set([]),
cleanups: [],
})
///|
let current_scope : Ref[@slotmap.Id] = Ref(root_scope)
///|
fn get_scope() -> Scope {
global_scopes[current_scope.val]
}
///|
priv struct Scope {
id : @slotmap.Id
parent : @slotmap.Id?
sub_scopes : Set[@slotmap.Id]
cleanups : Array[() -> Unit]
}
///|
fn[A] with_scope(f : (@slotmap.Id) -> A) -> A {
let id = global_scopes.allocate_with(id => {
id,
parent: Some(current_scope.val),
sub_scopes: Set([]),
cleanups: [],
})
let result = current_scope.protect(id, () => f(id))
if global_scopes.get(current_scope.val) is Some(scope) {
scope.sub_scopes.add(id)
}
result
}
///|
pub fn on_cleanup(f : () -> Unit) -> Unit {
get_scope().cleanups.push(f)
}
///|
pub fn cleanup() -> Unit {
global_scopes[root_scope].dispose()
}
///|
fn Scope::dispose(self : Self) -> Unit {
for id in self.sub_scopes {
if global_scopes.get(id) is Some(scope) {
scope.dispose()
}
}
for cleanup in self.cleanups {
cleanup()
}
if self.parent is Some(p) {
global_scopes[p].sub_scopes.remove(self.id)
}
global_scopes.free(self.id)
}