#[test] fn parent_exit_adopts_two_children_and_init_reaps_publication_order() { let (mut table, _) = setup(); let before = driver(&mut table).inventory(); let init = spawn(&mut table); driver(&mut table).activate(init); let parent = spawn(&mut table); driver(&mut table).yield_current(); driver(&mut table).activate(parent); let first = spawn(&mut table); let second = spawn(&mut table); table.terminate_child(second, 0).unwrap(); table.terminate_child(first, 42).unwrap(); let live = driver(&mut table).inventory(); table.exit_current(7).unwrap(); assert_eq!(driver(&mut table).inventory(), live); assert_eq!(table.record(first).unwrap().parent, Some(init)); assert_eq!(table.record(first).unwrap().orphaned_from, Some(parent)); assert_eq!(table.record(second).unwrap().parent, Some(init)); assert_eq!(table.record(second).unwrap().orphaned_from, Some(parent)); driver(&mut table).activate(init); assert_eq!(table.next_orphan(init), Ok(first)); assert_eq!(table.wait_child_outcome(first), Ok(Outcome::Exited(42))); assert_eq!(table.next_orphan(init), Ok(second)); assert_eq!(table.wait_child_outcome(second), Ok(Outcome::Exited(0))); assert_eq!(table.next_orphan(init), Err(Error::NoChild)); assert_eq!(table.wait_child_outcome(parent), Ok(Outcome::Exited(7))); driver(&mut table).yield_current(); driver(&mut table).activate(init); table.exit_current(0).unwrap(); // The host bootstrap remains the custodian of the terminal INIT bag. let control = driver(&mut table).control.clone(); control.borrow_mut().execution.kernel_bootstrap = true; assert_eq!(table.wait_child_outcome(init), Ok(Outcome::Exited(0))); assert_eq!(driver(&mut table).inventory(), before); } #[test] fn reused_parent_slot_cannot_steal_adopted_children() { let (mut table, _) = setup(); let init = spawn(&mut table); driver(&mut table).activate(init); let parent = spawn(&mut table); driver(&mut table).yield_current(); driver(&mut table).activate(parent); let first = spawn(&mut table); let second = spawn(&mut table); table.terminate_child(first, 11).unwrap(); table.terminate_child(second, 12).unwrap(); table.exit_current(0).unwrap(); driver(&mut table).activate(init); assert_eq!(table.wait_child_outcome(parent), Ok(Outcome::Exited(0))); let replacement = spawn(&mut table); assert_eq!(replacement.slot(), parent.slot()); assert_ne!(replacement, parent); assert_eq!(table.record(first).unwrap().orphaned_from, Some(parent)); assert_eq!(table.record(parent), Err(Error::Stale)); let inventory = driver(&mut table).inventory(); driver(&mut table).yield_current(); driver(&mut table).activate(replacement); assert_eq!(table.wait_child_outcome(first), Err(Error::Foreign)); assert_eq!(table.next_orphan(init), Err(Error::Foreign)); assert_eq!(driver(&mut table).inventory(), inventory); driver(&mut table).yield_current(); driver(&mut table).activate(init); assert_eq!(table.next_orphan(init), Ok(first)); assert_eq!(table.wait_child_outcome(first), Ok(Outcome::Exited(11))); assert_eq!(table.next_orphan(init), Ok(second)); assert_eq!(table.wait_child_outcome(second), Ok(Outcome::Exited(12))); table.terminate_child(replacement, 0).unwrap(); assert_eq!(table.wait_child_outcome(replacement), Ok(Outcome::Exited(0))); } #[test] fn failed_parent_exit_keeps_original_owners_and_no_orphan_metadata() { let (mut table, control) = setup(); let init = spawn(&mut table); driver(&mut table).activate(init); let parent = spawn(&mut table); driver(&mut table).yield_current(); driver(&mut table).activate(parent); let first = spawn(&mut table); let second = spawn(&mut table); let inventory = driver(&mut table).inventory(); let snapshot = table.snapshot(); control.borrow_mut().exit_failure = Some(Boundary::Before); assert_eq!(table.exit_current(0), Err(Error::Backend)); assert_eq!(table.snapshot(), snapshot); assert_eq!(driver(&mut table).inventory(), inventory); for child in [first, second] { assert_eq!(table.record(child).unwrap().parent, Some(parent)); assert_eq!(table.record(child).unwrap().orphaned_from, None); } control.borrow_mut().exit_failure = None; table.exit_current(0).unwrap(); for child in [first, second] { assert_eq!(table.record(child).unwrap().parent, Some(init)); assert_eq!(table.record(child).unwrap().orphaned_from, Some(parent)); } } #[test] fn hidden_child_blocks_exit_before_any_owner_change() { let (mut table, control) = setup(); let init = spawn(&mut table); driver(&mut table).activate(init); let parent = spawn(&mut table); driver(&mut table).yield_current(); driver(&mut table).activate(parent); let hidden = table.begin(spec()).unwrap(); let exits = control.borrow().exits; let inventory = driver(&mut table).inventory(); assert_eq!(table.exit_current(0), Err(Error::Busy)); assert_eq!(control.borrow().exits, exits); assert_eq!(driver(&mut table).inventory(), inventory); assert_eq!(table.record(parent).unwrap().outcome, None); table.abort(hidden).unwrap(); table.exit_current(0).unwrap(); }