implement process_release/1, process_wait/2, process_wait/3, and process_kill/1
This commit is contained in:
@@ -539,6 +539,10 @@ enum SystemClauseType {
|
|||||||
Shell,
|
Shell,
|
||||||
#[strum_discriminants(strum(props(Arity = "8", Name = "$process_create")))]
|
#[strum_discriminants(strum(props(Arity = "8", Name = "$process_create")))]
|
||||||
ProcessCreate,
|
ProcessCreate,
|
||||||
|
#[strum_discriminants(strum(props(Arity = "3", Name = "$process_wait")))]
|
||||||
|
ProcessWait,
|
||||||
|
#[strum_discriminants(strum(props(Arity = "1", Name = "$process_kill")))]
|
||||||
|
ProcessKill,
|
||||||
#[strum_discriminants(strum(props(Arity = "1", Name = "$pid")))]
|
#[strum_discriminants(strum(props(Arity = "1", Name = "$pid")))]
|
||||||
Pid,
|
Pid,
|
||||||
#[strum_discriminants(strum(props(Arity = "4", Name = "$chars_base64")))]
|
#[strum_discriminants(strum(props(Arity = "4", Name = "$chars_base64")))]
|
||||||
@@ -1828,6 +1832,8 @@ fn generate_instruction_preface() -> TokenStream {
|
|||||||
&Instruction::CallUnsetEnv |
|
&Instruction::CallUnsetEnv |
|
||||||
&Instruction::CallShell |
|
&Instruction::CallShell |
|
||||||
&Instruction::CallProcessCreate |
|
&Instruction::CallProcessCreate |
|
||||||
|
&Instruction::CallProcessWait |
|
||||||
|
&Instruction::CallProcessKill |
|
||||||
&Instruction::CallPid |
|
&Instruction::CallPid |
|
||||||
&Instruction::CallCharsBase64 |
|
&Instruction::CallCharsBase64 |
|
||||||
&Instruction::CallDevourWhitespace |
|
&Instruction::CallDevourWhitespace |
|
||||||
@@ -2067,6 +2073,8 @@ fn generate_instruction_preface() -> TokenStream {
|
|||||||
&Instruction::ExecuteUnsetEnv |
|
&Instruction::ExecuteUnsetEnv |
|
||||||
&Instruction::ExecuteShell |
|
&Instruction::ExecuteShell |
|
||||||
&Instruction::ExecuteProcessCreate |
|
&Instruction::ExecuteProcessCreate |
|
||||||
|
&Instruction::ExecuteProcessWait |
|
||||||
|
&Instruction::ExecuteProcessKill |
|
||||||
&Instruction::ExecutePid |
|
&Instruction::ExecutePid |
|
||||||
&Instruction::ExecuteCharsBase64 |
|
&Instruction::ExecuteCharsBase64 |
|
||||||
&Instruction::ExecuteDevourWhitespace |
|
&Instruction::ExecuteDevourWhitespace |
|
||||||
|
|||||||
@@ -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(error)).
|
||||||
:- use_module(library(iso_ext)).
|
:- use_module(library(iso_ext)).
|
||||||
@@ -27,6 +33,29 @@ process_create(Exe, Args, Options) :-
|
|||||||
simplify_env(Env, Env1),
|
simplify_env(Env, Env1),
|
||||||
'$process_create'(Exe, Args, Stdin1, Stdout1, Stderr1, Env1, Cwd, Pid).
|
'$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(_, _, []).
|
||||||
must_be_known_options(Valid, Found, [X|XS]) :-
|
must_be_known_options(Valid, Found, [X|XS]) :-
|
||||||
X =.. [Option|_],
|
X =.. [Option|_],
|
||||||
@@ -73,9 +102,7 @@ valid_env(environment(E)) :- (
|
|||||||
|
|
||||||
valid_env_([]).
|
valid_env_([]).
|
||||||
valid_env_([E| ES]) :-
|
valid_env_([E| ES]) :-
|
||||||
(
|
E =.. [=, N, V],
|
||||||
E =.. [=, N, V] -> true ;
|
|
||||||
),
|
|
||||||
must_be(chars, N),
|
must_be(chars, N),
|
||||||
must_be(chars, V),
|
must_be(chars, V),
|
||||||
valid_env_(ES).
|
valid_env_(ES).
|
||||||
|
|||||||
@@ -4795,6 +4795,22 @@ impl Machine {
|
|||||||
try_or_throw!(self.machine_st, self.process_create());
|
try_or_throw!(self.machine_st, self.process_create());
|
||||||
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
|
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 => {
|
&Instruction::CallPid => {
|
||||||
self.pid();
|
self.pid();
|
||||||
step_or_fail!(self, self.machine_st.p += 1);
|
step_or_fail!(self, self.machine_st.p += 1);
|
||||||
|
|||||||
@@ -405,6 +405,17 @@ impl MachineState {
|
|||||||
[atom_as_cell((atom!("stream"))), cell(culprit)]
|
[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 {
|
MachineError {
|
||||||
stub,
|
stub,
|
||||||
location: None,
|
location: None,
|
||||||
@@ -1003,6 +1014,7 @@ pub enum ExistenceError {
|
|||||||
},
|
},
|
||||||
SourceSink(HeapCellValue),
|
SourceSink(HeapCellValue),
|
||||||
Stream(HeapCellValue),
|
Stream(HeapCellValue),
|
||||||
|
Process(HeapCellValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -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)]
|
#[inline(always)]
|
||||||
pub(crate) fn chars_base64(&mut self) -> CallResult {
|
pub(crate) fn chars_base64(&mut self) -> CallResult {
|
||||||
let padding = cell_as_atom!(self.deref_register(3));
|
let padding = cell_as_atom!(self.deref_register(3));
|
||||||
|
|||||||
Reference in New Issue
Block a user