implement process_release/1, process_wait/2, process_wait/3, and process_kill/1

This commit is contained in:
Bennet Bleßmann
2025-07-20 22:57:06 +02:00
parent 1ee4f7a55f
commit 97e2e5d7f0
5 changed files with 203 additions and 4 deletions

View File

@@ -1,4 +1,10 @@
:- module(process, [process_create/3]).
:- module(process, [
process_create/3,
process_release/1,
process_wait/2,
process_wait/3,
process_kill/1
]).
:- use_module(library(error)).
:- use_module(library(iso_ext)).
@@ -27,6 +33,29 @@ process_create(Exe, Args, Options) :-
simplify_env(Env, Env1),
'$process_create'(Exe, Args, Stdin1, Stdout1, Stderr1, Env1, Cwd, Pid).
process_wait(Pid, Status) :- process_wait(Pid, Status, []).
process_wait(Pid, Status, Options) :-
must_be(integer, Pid),
must_be_known_options([timeout], [], Options),check_options(
[
([timeout], valid_timeout, infinite, timeout(Timeout))
],
Options
),
'$process_wait'(Pid, Exit, Timeout),
Exit = Status.
valid_timeout(timeout(infinite)).
valid_timeout(timeout(0)).
process_kill(Pid) :-
must_be(integer, Pid),
'$process_kill'(Pid).
process_release(Pid) :- process_wait(Pid, _).
must_be_known_options(_, _, []).
must_be_known_options(Valid, Found, [X|XS]) :-
X =.. [Option|_],
@@ -73,9 +102,7 @@ valid_env(environment(E)) :- (
valid_env_([]).
valid_env_([E| ES]) :-
(
E =.. [=, N, V] -> true ;
),
E =.. [=, N, V],
must_be(chars, N),
must_be(chars, V),
valid_env_(ES).

View File

@@ -4795,6 +4795,22 @@ impl Machine {
try_or_throw!(self.machine_st, self.process_create());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallProcessWait => {
try_or_throw!(self.machine_st, self.process_wait());
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteProcessWait => {
try_or_throw!(self.machine_st, self.process_wait());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallProcessKill => {
try_or_throw!(self.machine_st, self.process_kill());
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteProcessKill => {
try_or_throw!(self.machine_st, self.process_kill());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallPid => {
self.pid();
step_or_fail!(self, self.machine_st.p += 1);

View File

@@ -405,6 +405,17 @@ impl MachineState {
[atom_as_cell((atom!("stream"))), cell(culprit)]
);
MachineError {
stub,
location: None,
}
}
ExistenceError::Process(culprit) => {
let stub = functor!(
atom!("existence_error"),
[atom_as_cell((atom!("process"))), cell(culprit)]
);
MachineError {
stub,
location: None,
@@ -1003,6 +1014,7 @@ pub enum ExistenceError {
},
SourceSink(HeapCellValue),
Stream(HeapCellValue),
Process(HeapCellValue),
}
#[derive(Debug)]

View File

@@ -8616,6 +8616,142 @@ impl Machine {
})
}
pub(crate) fn process_wait(&mut self) -> CallResult {
fn stub_gen() -> Vec<FunctorElement> {
functor_stub(atom!("process_wait"), 2)
}
// Pid
let pid_r = self.deref_register(1);
// Var | Status
let status_r = self.deref_register(2);
// timeout | 0
let timeout_r = self.deref_register(3);
let Some(pid) = pid_r
.to_fixnum()
.and_then(|elem| elem.get_num().try_into().ok())
else {
let err = self
.machine_st
.existence_error(ExistenceError::Process(pid_r));
return Err(self.machine_st.error_form(err, stub_gen()));
};
let Some(mut child) = self.machine_st.child_processes.remove(&pid) else {
let err = self
.machine_st
.existence_error(ExistenceError::Process(pid_r));
return Err(self.machine_st.error_form(err, stub_gen()));
};
let status = if let Some(atom) = timeout_r.to_atom() {
match atom {
atom!("infinite") => child.wait().map(Some),
_ => {
panic!("Invalid Timeout value")
}
}
} else if let Some(timeout) = timeout_r.to_fixnum() {
if timeout.get_num() == 0 {
child.try_wait()
} else {
panic!("Invalid Timeout value")
}
} else {
panic!("Invalid Timeout value")
};
match status {
Ok(None) => {
unify!(self.machine_st, status_r, atom_as_cell!(atom!("timeout")));
Ok(())
}
Ok(Some(exit_status)) => {
if let Some(exit_code) = exit_status.code() {
let mut writer =
Heap::functor_writer(functor!(atom!("exit"), [fixnum(exit_code)]));
match writer(&mut self.machine_st.heap) {
Ok(loc) => {
unify!(self.machine_st, status_r, loc);
}
Err(resource_err_loc) => {
self.machine_st.throw_resource_error(resource_err_loc);
}
}
Ok(())
} else {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
if let Some(signal) = ExitStatusExt::signal(&exit_status) {
let mut writer =
Heap::functor_writer(functor!(atom!("signal"), [fixnum(signal)]));
match writer(&mut self.machine_st.heap) {
Ok(loc) => {
unify!(self.machine_st, status_r, loc);
}
Err(resource_err_loc) => {
self.machine_st.throw_resource_error(resource_err_loc);
}
};
Ok(())
} else {
unify!(self.machine_st, status_r, atom_as_cell!(atom!("unknown")));
Ok(())
}
}
#[cfg(not(unix))]
{
unify!(self.machine_st, status_r, atom_as_cell!(atom!("unknown")));
Ok(())
}
}
}
Err(_) => {
let perm_error = self.machine_st.permission_error(
Permission::Modify,
atom!("process"),
stub_gen(),
);
Err(self.machine_st.error_form(perm_error, stub_gen()))
}
}
}
pub(crate) fn process_kill(&mut self) -> CallResult {
fn stub_gen() -> Vec<FunctorElement> {
functor_stub(atom!("process_kill"), 1)
}
// Pid
let pid_r = self.deref_register(1);
let Some(pid) = pid_r
.to_fixnum()
.and_then(|elem| elem.get_num().try_into().ok())
else {
let err = self
.machine_st
.existence_error(ExistenceError::Process(pid_r));
return Err(self.machine_st.error_form(err, stub_gen()));
};
let Some(mut child) = self.machine_st.child_processes.remove(&pid) else {
let err = self
.machine_st
.existence_error(ExistenceError::Process(pid_r));
return Err(self.machine_st.error_form(err, stub_gen()));
};
if child.kill().is_err() {
let perm_error =
self.machine_st
.permission_error(Permission::Modify, atom!("process"), stub_gen());
return Err(self.machine_st.error_form(perm_error, stub_gen()));
}
Ok(())
}
#[inline(always)]
pub(crate) fn chars_base64(&mut self) -> CallResult {
let padding = cell_as_atom!(self.deref_register(3));