// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn signal_from_native(sig : Int) -> Signal? {
match sig {
1 => Some(Signal::Hangup)
2 => Some(Signal::Interrupt)
3 => Some(Signal::Quit)
4 => Some(Signal::Illegal)
5 => Some(Signal::Trap)
6 => Some(Signal::Abort)
8 => Some(Signal::FloatingPointException)
9 => Some(Signal::Kill)
10 => Some(Signal::User1)
11 => Some(Signal::Segv)
12 => Some(Signal::User2)
13 => Some(Signal::Pipe)
14 => Some(Signal::Alarm)
15 => Some(Signal::Term)
17 => Some(Signal::Child)
18 => Some(Signal::Continue)
19 => Some(Signal::Stop)
20 => Some(Signal::TSTP)
21 => Some(Signal::TTIN)
22 => Some(Signal::TTOU)
23 => Some(Signal::Urgent)
24 => Some(Signal::XCPU)
25 => Some(Signal::XFSZ)
26 => Some(Signal::VirtualAlarm)
27 => Some(Signal::Profiling)
28 => Some(Signal::Winch)
29 => Some(Signal::IO)
30 => Some(Signal::Power)
31 => Some(Signal::Sys)
_ => None
}
}
///|
fn decode_wait_status(status : Int) -> ExitStatus {
let term_signal = status & 0x7f
if term_signal == 0 {
let code = (status >> 8) & 0xff
ExitStatus::from_code(code)
} else {
match signal_from_native(term_signal) {
Some(signal) => ExitStatus::from_signal(signal)
None => { code: None, signal: None, success: false }
}
}
}
///|
pub fn Process::kill(self : Process) -> Bool {
self.kill_with(Signal::Term) == Some(true)
}
///|
pub fn Process::kill_with(self : Process, signal : Signal) -> Bool? {
if !is_supported_system {
return None
}
let rc = kill_pid_ffi(self.pid.to_int(), map_signal_to_native(signal))
Some(rc == 0)
}
///|
fn wait_until_process_exits(pid : Pid, max_attempts : Int) -> Bool {
for i = 0; i < max_attempts; i = i + 1 {
if !process_exists(pid) {
return true
}
sleep_millis_native(50)
}
!process_exists(pid)
}
///|
pub fn Process::wait(self : Process) -> ExitStatus? {
if !is_supported_system {
return None
}
let status = @ref.new(0)
let rc = wait_pid_ffi(self.pid.to_int(), 0, status)
if rc == self.pid.to_int() {
Some(decode_wait_status(status.val))
} else if rc < 0 && wait_until_process_exits(self.pid, 1200) {
if get_os_tag() == "linux" {
None
} else {
Some(ExitStatus::from_code(0))
}
} else {
None
}
}
///|
pub fn Process::kill_and_wait(self : Process) -> Result[ExitStatus?, KillError] {
self.kill_with_and_wait(Signal::Term)
}
///|
pub fn Process::kill_with_and_wait(
self : Process,
signal : Signal,
) -> Result[ExitStatus?, KillError] {
let wnohang = 1
match self.kill_with(signal) {
None => Err(KillError::Unsupported)
Some(false) => Err(KillError::PermissionDenied)
Some(true) => {
let max_attempts = 80
for i = 0; i < max_attempts; i = i + 1 {
let status = @ref.new(0)
let rc = wait_pid_ffi(self.pid.to_int(), wnohang, status)
if rc == self.pid.to_int() {
return Ok(Some(decode_wait_status(status.val)))
}
if rc < 0 {
if wait_until_process_exits(self.pid, max_attempts - i) {
return Ok(None)
}
return Err(KillError::WaitFailed)
}
// keep polling for child process completion
sleep_millis_native(50)
}
Err(KillError::Timeout)
}
}
}