// Policy-free activation lifecycle ownership for the dispatcher shell. This
// module records one accepted-or-rejected entry outcome and exactly one release
// for each accepted guest activation. Logical-depth policy remains in #617.
///|
priv suberror InvalidActivationObservationTransition {
InvalidActivationObservationTransition(String)
}
///|
#warnings("-unused_constructor")
priv enum ActivationObservationPhase {
ActivationEntryAttempted
ActivationEntryAccepted
ActivationEntryRejected
ActivationReleased
}
///|
priv enum ActivationObservationEvent {
AcceptActivationEntry
RejectActivationEntry
ReleaseObservedActivation
ReleaseRejectedActivation
}
///|
priv enum ActivationObservationDecision {
EmitActivationAccepted
EmitActivationRejected
EmitActivationReleased
}
///|
fn[T] invalid_activation_observation_transition(
message : String,
) -> T raise InvalidActivationObservationTransition {
raise InvalidActivationObservationTransition(message)
}
///|
fn reduce_activation_observation(
phase : ActivationObservationPhase,
event : ActivationObservationEvent,
) -> (ActivationObservationPhase, ActivationObservationDecision) raise InvalidActivationObservationTransition {
match (phase, event) {
(ActivationEntryAttempted, AcceptActivationEntry) =>
(ActivationEntryAccepted, EmitActivationAccepted)
(ActivationEntryAttempted, RejectActivationEntry) =>
(ActivationEntryRejected, EmitActivationRejected)
(ActivationEntryAccepted, ReleaseObservedActivation) =>
(ActivationReleased, EmitActivationReleased)
(ActivationEntryRejected, ReleaseRejectedActivation) =>
(ActivationReleased, EmitActivationReleased)
_ =>
invalid_activation_observation_transition(
"activation observation capability is stale or out of order",
)
}
}
///|
priv struct ActivationObservationAttemptHooks {
acquired : () -> Unit
accepted : () -> Unit
rejected : () -> Unit
released : () -> Unit
}
///|
fn ActivationObservationAttemptHooks::ActivationObservationAttemptHooks(
acquired? : () -> Unit = () => (),
accepted~ : () -> Unit,
rejected~ : () -> Unit,
released~ : () -> Unit,
) -> ActivationObservationAttemptHooks {
{ acquired, accepted, rejected, released }
}
///|
priv struct ActivationObservationPort {
begin_entry : () -> ActivationObservationAttemptHooks raise Error
denied : () -> Unit
}
///|
fn ActivationObservationPort::ActivationObservationPort(
begin_entry~ : () -> ActivationObservationAttemptHooks raise Error,
denied~ : () -> Unit,
) -> ActivationObservationPort {
{ begin_entry, denied }
}
///|
priv struct ActivationEntryAttempt {
phase : Ref[ActivationObservationPhase]
hooks : ActivationObservationAttemptHooks
}
///|
fn ActivationEntryAttempt::ActivationEntryAttempt(
hooks : ActivationObservationAttemptHooks,
) -> ActivationEntryAttempt {
{ phase: { val: ActivationEntryAttempted }, hooks }
}
///|
priv struct ActivationObservationToken {
phase : Ref[ActivationObservationPhase]
released : () -> Unit
}
///|
fn ActivationObservationToken::ActivationObservationToken(
phase~ : Ref[ActivationObservationPhase],
released~ : () -> Unit,
) -> ActivationObservationToken {
{ phase, released }
}
///|
#warnings("-unused_value")
fn observe_activation_entry(
port : ActivationObservationPort,
) -> ActivationEntryAttempt raise Error {
let hooks = (port.begin_entry)() catch {
error => {
(port.denied)()
raise error
}
}
// A successful begin_entry owns one unit even when later setup rejects the
// entry. Outcome hooks remain reserved for setup acceptance or rejection;
// release pairs with this acquisition after owned cleanup.
(hooks.acquired)()
ActivationEntryAttempt(hooks)
}
///|
#warnings("-unused_value")
fn ActivationEntryAttempt::accept(
self : ActivationEntryAttempt,
) -> ActivationObservationToken raise InvalidActivationObservationTransition {
let (next_phase, decision) = reduce_activation_observation(
self.phase.val,
AcceptActivationEntry,
)
self.phase.val = next_phase
match decision {
EmitActivationAccepted => (self.hooks.accepted)()
_ =>
invalid_activation_observation_transition(
"accept entry produced an invalid observation decision",
)
}
ActivationObservationToken(phase=self.phase, released=self.hooks.released)
}
///|
#warnings("-unused_value")
fn ActivationEntryAttempt::reject(
self : ActivationEntryAttempt,
) -> Unit raise InvalidActivationObservationTransition {
let (next_phase, decision) = reduce_activation_observation(
self.phase.val,
RejectActivationEntry,
)
self.phase.val = next_phase
match decision {
EmitActivationRejected => (self.hooks.rejected)()
_ =>
invalid_activation_observation_transition(
"reject entry produced an invalid observation decision",
)
}
}
///|
// Setup rejection is distinct from denial: the entry observation may already
// have acquired an activation unit. The shell records the rejected attempt,
// restores its owned runtime state, and calls this method exactly once after
// restoration so the release hook observes the restored state.
fn ActivationEntryAttempt::release_after_rejection(
self : ActivationEntryAttempt,
) -> Unit raise InvalidActivationObservationTransition {
let (next_phase, decision) = reduce_activation_observation(
self.phase.val,
ReleaseRejectedActivation,
)
self.phase.val = next_phase
match decision {
EmitActivationReleased => (self.hooks.released)()
_ =>
invalid_activation_observation_transition(
"rejected activation release produced an invalid observation decision",
)
}
}
///|
#warnings("-unused_value")
fn ActivationObservationToken::release(
self : ActivationObservationToken,
) -> Unit raise InvalidActivationObservationTransition {
let (next_phase, decision) = reduce_activation_observation(
self.phase.val,
ReleaseObservedActivation,
)
self.phase.val = next_phase
match decision {
EmitActivationReleased => (self.released)()
_ =>
invalid_activation_observation_transition(
"release produced an invalid observation decision",
)
}
}
///|
#warnings("-unused_value")
fn allow_all_activation_observation_port() -> ActivationObservationPort {
ActivationObservationPort(
begin_entry=() => {
ActivationObservationAttemptHooks(accepted=() => (), rejected=() => (), released=() => {
()
})
},
denied=() => (),
)
}