Skip to content

Commit

Permalink
Differentiate thread_exit from proc_exit, allow threads to call proc_…
Browse files Browse the repository at this point in the history
…exit with a success code
  • Loading branch information
Arshia001 committed Oct 21, 2024
1 parent b3189e0 commit f5a5e65
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 24 deletions.
1 change: 1 addition & 0 deletions lib/wasix/src/bin_factory/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ fn call_module(
if let Err(err) = call_ret {
match err.downcast::<WasiError>() {
Ok(WasiError::Exit(code)) if code.is_success() => Ok(Errno::Success),
Ok(WasiError::ThreadExit) => Ok(Errno::Success),
Ok(WasiError::Exit(code)) => {
runtime.on_taint(TaintReason::NonZeroExitCode(code));
Err(WasiError::Exit(code).into())
Expand Down
2 changes: 2 additions & 0 deletions lib/wasix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ pub use crate::{
pub enum WasiError {
#[error("WASI exited with code: {0}")]
Exit(ExitCode),
#[error("WASI thread exited")]
ThreadExit,
#[error("WASI deep sleep: {0:?}")]
DeepSleep(DeepSleepWork),
#[error("The WASI version could not be determined")]
Expand Down
3 changes: 3 additions & 0 deletions lib/wasix/src/runners/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,9 @@ impl crate::runners::Runner for WasiRunner {
WasiRuntimeError::Wasi(WasiError::Exit(a)) => {
WasiRuntimeError::Wasi(WasiError::Exit(*a))
}
WasiRuntimeError::Wasi(WasiError::ThreadExit) => {
WasiRuntimeError::Wasi(WasiError::ThreadExit)
}
WasiRuntimeError::Wasi(WasiError::UnknownWasiVersion) => {
WasiRuntimeError::Wasi(WasiError::UnknownWasiVersion)
}
Expand Down
18 changes: 9 additions & 9 deletions lib/wasix/src/state/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1252,32 +1252,33 @@ impl WasiEnv {

/// Cleans up all the open files (if this is the main thread)
#[allow(clippy::await_holding_lock)]
pub fn blocking_on_exit(&self, exit_code: Option<ExitCode>) {
let cleanup = self.on_exit(exit_code);
pub fn blocking_on_exit(&self, process_exit_code: Option<ExitCode>) {
let cleanup = self.on_exit(process_exit_code);
InlineWaker::block_on(cleanup);
}

/// Cleans up all the open files (if this is the main thread)
#[allow(clippy::await_holding_lock)]
pub fn on_exit(&self, exit_code: Option<ExitCode>) -> BoxFuture<'static, ()> {
pub fn on_exit(&self, process_exit_code: Option<ExitCode>) -> BoxFuture<'static, ()> {
const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10);

// If snap-shooting is enabled then we should record an event that the thread has exited.
#[cfg(feature = "journal")]
if self.should_journal() && self.has_active_journal() {
if let Err(err) = JournalEffector::save_thread_exit(self, self.tid(), exit_code) {
if let Err(err) = JournalEffector::save_thread_exit(self, self.tid(), process_exit_code)
{
tracing::warn!("failed to save snapshot event for thread exit - {}", err);
}

if self.thread.is_main() {
if let Err(err) = JournalEffector::save_process_exit(self, exit_code) {
if let Err(err) = JournalEffector::save_process_exit(self, process_exit_code) {
tracing::warn!("failed to save snapshot event for process exit - {}", err);
}
}
}

// If this is the main thread then also close all the files
if self.thread.is_main() || matches!(exit_code, Some(e) if !e.is_success()) {
// If the process wants to exit, also close all files and terminate it
if let Some(process_exit_code) = process_exit_code {
let process = self.process.clone();
let disable_fs_cleanup = self.disable_fs_cleanup;
let pid = self.pid();
Expand All @@ -1303,8 +1304,7 @@ impl WasiEnv {
}

// Terminate the process
let exit_code = exit_code.unwrap_or_else(|| Errno::Canceled.into());
process.terminate(exit_code);
process.terminate(process_exit_code);
})
} else {
Box::pin(async {})
Expand Down
4 changes: 2 additions & 2 deletions lib/wasix/src/state/func_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,15 +284,15 @@ impl WasiFunctionEnv {
/// This function should only be called from within a syscall
/// as it can potentially execute local thread variable cleanup
/// code
pub fn on_exit(&self, store: &mut impl AsStoreMut, exit_code: Option<ExitCode>) {
pub fn on_exit(&self, store: &mut impl AsStoreMut, process_exit_code: Option<ExitCode>) {
trace!(
"wasi[{}:{}]::on_exit",
self.data(store).pid(),
self.data(store).tid()
);

// Cleans up all the open files (if this is the main thread)
self.data(store).blocking_on_exit(exit_code);
self.data(store).blocking_on_exit(process_exit_code);
}

/// Bootstraps this main thread and context with any journals that
Expand Down
19 changes: 9 additions & 10 deletions lib/wasix/src/syscalls/wasix/thread_exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ use crate::syscalls::*;

/// ### `thread_exit()`
/// Terminates the current running thread, if this is the last thread then
/// the process will also exit with the specified exit code. An exit code
/// of 0 indicates successful termination of the thread. The meanings of
/// other values is dependent on the environment.
/// the process will also exit with code 0.
/// The exit code parameter is a left over from a previous version of this
/// syscall, maintained here to keep the syscall backwards-compatible, but
/// is otherwise unused.
///
/// ## Parameters
///
/// * `rval` - The exit code returned by the process.
#[instrument(level = "trace", skip_all, fields(%exitcode), ret)]
pub fn thread_exit(ctx: FunctionEnvMut<'_, WasiEnv>, exitcode: ExitCode) -> Result<(), WasiError> {
tracing::debug!(tid=%ctx.data().thread.id(), %exitcode);
Err(WasiError::Exit(exitcode))
/// This syscall does not return.
#[instrument(level = "trace", skip_all, fields(%_exitcode), ret)]
pub fn thread_exit(ctx: FunctionEnvMut<'_, WasiEnv>, _exitcode: ExitCode) -> Result<(), WasiError> {
tracing::debug!(tid=%ctx.data().thread.id(), "thread exit");
Err(WasiError::ThreadExit)
}
17 changes: 14 additions & 3 deletions lib/wasix/src/syscalls/wasix/thread_spawn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,19 @@ fn call_module<M: MemorySize>(
.map_err(|_| Errno::Overflow)
.unwrap(),
);
trace!("callback finished (ret={:?})", call_ret);

let mut ret = Errno::Success;
let mut exit_code = None;
if let Err(err) = call_ret {
match err.downcast::<WasiError>() {
Ok(WasiError::ThreadExit) => {
trace!("thread exited cleanly");
ret = Errno::Success;
}
Ok(WasiError::Exit(code)) => {
trace!(?code, "thread exited cleanly");
trace!(exit_code = ?code, "thread requested exit");
exit_code = Some(code);
ret = if code.is_success() {
Errno::Success
} else {
Expand All @@ -227,20 +235,23 @@ fn call_module<M: MemorySize>(
.runtime
.on_taint(TaintReason::UnknownWasiVersion);
ret = Errno::Noexec;
exit_code = Some(ExitCode::Other(128 + ret as i32));
}
Err(err) => {
debug!("failed with runtime error: {}", err);
env.data(&store)
.runtime
.on_taint(TaintReason::RuntimeError(err));
ret = Errno::Noexec;
exit_code = Some(ExitCode::Other(128 + ret as i32));
}
}
} else {
debug!("thread exited cleanly without calling thread_exit");
}
trace!("callback finished (ret={})", ret);

// Clean up the environment
env.on_exit(store, Some(ret.into()));
env.on_exit(store, exit_code);

// Return the result
Ok(ret as u32)
Expand Down

0 comments on commit f5a5e65

Please sign in to comment.