Skip to content

Commit 4ad77ae

Browse files
committed
feat(port_std): implement graceful shutdown
1 parent 2617b05 commit 4ad77ae

File tree

2 files changed

+31
-28
lines changed

2 files changed

+31
-28
lines changed

src/constance_port_std/src/lib.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ impl State {
175175
}
176176

177177
/// Initialize the user-mode scheduling system and boot the kernel.
178-
pub fn port_boot<System: Kernel>(&self) -> ! {
178+
///
179+
/// Returns when the shutdown initiated by [`shutdown`] completes.
180+
pub fn port_boot<System: Kernel>(&self) {
179181
// Create a UMS thread group.
180182
let (thread_group, join_handle) = ums::ThreadGroup::new(sched::SchedState::new::<System>());
181183

@@ -202,8 +204,6 @@ impl State {
202204
if let Err(e) = join_handle.join() {
203205
std::panic::resume_unwind(e);
204206
}
205-
206-
panic!("the system has been shut down")
207207
}
208208

209209
pub unsafe fn dispatch_first_task<System: PortInstance>(&'static self) -> !
@@ -299,14 +299,7 @@ impl State {
299299
Tsm::Uninit => unreachable!(),
300300
}
301301
} else {
302-
// Since we don't have timers or real interrupts yet, this means
303-
// we are in deadlock.
304-
//
305-
// The test code currently relies on this behavior (panic on
306-
// deadlock) to exit the dispatcher loop. When we have timers
307-
// and interrupts, we need to devise another way to exit the
308-
// dispatcher loop.
309-
panic!("No task to schedule");
302+
None
310303
};
311304
}
312305

@@ -537,6 +530,22 @@ impl State {
537530
}
538531
}
539532

533+
/// Initiate graceful shutdown.
534+
///
535+
/// The shutdown completes when all threads complete execution. Usually, the
536+
/// process will exit after this.
537+
///
538+
/// Note: There is no safe way to restart the simulated system without
539+
/// restarting an entire process.
540+
pub fn shutdown<System: PortInstance>() {
541+
System::port_state()
542+
.thread_group
543+
.get()
544+
.unwrap()
545+
.lock()
546+
.shutdown();
547+
}
548+
540549
#[macro_export]
541550
macro_rules! use_port {
542551
(unsafe $vis:vis struct $sys:ident) => {

src/constance_port_std/tests/test_suite.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(never_type)]
55
#![feature(const_mut_refs)]
66

7+
use constance_port_std::PortInstance;
78
use std::sync::atomic::{AtomicBool, Ordering};
89

910
struct KernelTestUtil {
@@ -17,34 +18,27 @@ impl KernelTestUtil {
1718
}
1819
}
1920

20-
fn success(&self) {
21+
fn success<System: PortInstance>(&self) {
2122
self.is_successful.store(true, Ordering::Relaxed);
23+
constance_port_std::shutdown::<System>();
2224
}
2325

2426
fn fail(&self) {
2527
panic!("test failed");
2628
}
2729

28-
fn run(&self, func: impl FnOnce() -> !) {
30+
fn run(&self, func: impl FnOnce()) {
2931
let _ = env_logger::try_init();
3032

31-
let panic_info = std::panic::catch_unwind(std::panic::AssertUnwindSafe(func))
32-
.err()
33-
.unwrap();
34-
35-
// "No task to schedule" is not a failure - it's the only way to stop
36-
// the dispatcher loop
37-
if let Some(msg) = panic_info.downcast_ref::<&'static str>() {
38-
if msg.contains("No task to schedule") {
39-
if self.is_successful.load(Ordering::Relaxed) {
40-
return;
41-
}
33+
if let Err(panic_info) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(func)) {
34+
std::panic::resume_unwind(panic_info);
35+
}
4236

43-
panic!("The program deadlocked without calling `success`");
44-
}
37+
if self.is_successful.load(Ordering::Relaxed) {
38+
return;
4539
}
4640

47-
std::panic::resume_unwind(panic_info);
41+
panic!("The program deadlocked without calling `success`");
4842
}
4943
}
5044

@@ -67,7 +61,7 @@ macro_rules! instantiate_kernel_tests {
6761
&COTTAGE
6862
}
6963
fn success() {
70-
TEST_UTIL.success();
64+
TEST_UTIL.success::<System>();
7165
}
7266
fn fail() {
7367
TEST_UTIL.fail();

0 commit comments

Comments
 (0)