mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-18 20:40:28 +00:00
os: remove process_close and add process_terminate
`process_wait` (optionally prefaced with a `process_kill`) can be used to properly close and free resources of the process. `process_terminate` was added because `process_kill` is a forceful exit, we were missing a way to request the process to terminate.
This commit is contained in:
@@ -311,7 +311,8 @@ This procedure obtains a process handle of a process specified by `pid`.
|
||||
This procedure can be subject to race conditions. See the description of
|
||||
`Process`.
|
||||
|
||||
Use `process_close()` function to close the process handle.
|
||||
Use the `process_wait()` procedure (optionally prefaced with a `process_kill()`)
|
||||
to close and free the process handle.
|
||||
*/
|
||||
@(require_results)
|
||||
process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
|
||||
@@ -360,10 +361,8 @@ be created. It contains information such as the command line, the
|
||||
environment of the process, the starting directory and many other options.
|
||||
Most of the fields in the struct can be set to `nil` or an empty value.
|
||||
|
||||
Use `process_close` to close the handle to the process. Note, that this
|
||||
is not the same as terminating the process. One can terminate the process
|
||||
and not close the handle, in which case the handle would be leaked. In case
|
||||
the function returns an error, an invalid handle is returned.
|
||||
Use the `process_wait()` procedure (optionally prefaced with a `process_kill()`)
|
||||
to close and free the process handle.
|
||||
|
||||
This procedure is not thread-safe. It may alter the inheritance properties
|
||||
of file handles in an unpredictable manner. In case multiple threads change
|
||||
@@ -495,7 +494,7 @@ Process_State :: struct {
|
||||
// Will also store the number of the exception or signal that has crashed the
|
||||
// process.
|
||||
exit_code: int,
|
||||
// Specifies whether the termination of the process was successfull or not,
|
||||
// Specifies whether the termination of the process was successful or not,
|
||||
// i.e. whether it has crashed or not.
|
||||
// **Note(windows)**: On windows `true` is always returned, as there is no
|
||||
// reliable way to obtain information about whether the process has crashed.
|
||||
@@ -511,7 +510,9 @@ Wait for a process event.
|
||||
|
||||
This procedure blocks the execution until the process has exited or the
|
||||
timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
|
||||
no timeout restriction is imposed and the procedure can block indefinately.
|
||||
no timeout restriction is imposed and the procedure can block indefinitely.
|
||||
|
||||
If the timeout is 0, no blocking will be done and the current state is returned.
|
||||
|
||||
If the timeout has expired, the `General_Error.Timeout` is returned as
|
||||
the error.
|
||||
@@ -525,24 +526,25 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_
|
||||
}
|
||||
|
||||
/*
|
||||
Close the handle to a process.
|
||||
Kill a process.
|
||||
|
||||
This procedure closes the handle associated with a process. It **does not**
|
||||
terminate a process, in case it was running. In case a termination is
|
||||
desired, kill the process first, wait for the process to finish,
|
||||
then close the handle.
|
||||
This procedure kills a process, specified by it's handle, `process`.
|
||||
|
||||
The process is forced to exit and can't ignore the request.
|
||||
*/
|
||||
@(require_results)
|
||||
process_close :: proc(process: Process) -> (Error) {
|
||||
return _process_close(process)
|
||||
process_kill :: proc(process: Process) -> (Error) {
|
||||
return _process_kill(process)
|
||||
}
|
||||
|
||||
/*
|
||||
Terminate a process.
|
||||
|
||||
This procedure terminates a process, specified by it's handle, `process`.
|
||||
|
||||
The process is requested to exit and can ignore the request.
|
||||
*/
|
||||
@(require_results)
|
||||
process_kill :: proc(process: Process) -> (Error) {
|
||||
return _process_kill(process)
|
||||
process_terminate :: proc(process: Process) -> (Error) {
|
||||
return _process_terminate(process)
|
||||
}
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
#+build freebsd
|
||||
package os
|
||||
|
||||
import "core:c"
|
||||
|
||||
foreign import libc "system:c"
|
||||
foreign import dl "system:dl"
|
||||
|
||||
foreign libc {
|
||||
@(link_name="sysctlbyname")
|
||||
_sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
|
||||
_sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> i32 ---
|
||||
}
|
||||
|
||||
foreign dl {
|
||||
@(link_name="pthread_getthreadid_np")
|
||||
pthread_getthreadid_np :: proc() -> c.int ---
|
||||
pthread_getthreadid_np :: proc() -> i32 ---
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -33,4 +31,4 @@ _get_processor_core_count :: proc() -> int {
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,11 +66,11 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_stat
|
||||
return
|
||||
}
|
||||
|
||||
_process_close :: proc(process: Process) -> Error {
|
||||
_process_kill :: proc(process: Process) -> (err: Error) {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_process_kill :: proc(process: Process) -> (err: Error) {
|
||||
_process_terminate :: proc(process: Process) -> (err: Error) {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ package os
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
@@ -13,12 +12,6 @@ import "core:strconv"
|
||||
import "core:sys/unix"
|
||||
import "core:sys/linux"
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
@(link_name="get_nprocs") _unix_get_nprocs :: proc() -> c.int ---
|
||||
}
|
||||
|
||||
PIDFD_UNASSIGNED :: ~uintptr(0)
|
||||
|
||||
@(private="package")
|
||||
@@ -682,6 +675,8 @@ _reap_terminated :: proc(process: Process) -> (state: Process_State, err: Error)
|
||||
state.exit_code = int(info.status)
|
||||
state.success = false
|
||||
}
|
||||
|
||||
_process_close(process)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -723,6 +718,8 @@ _timed_wait_on_handle :: proc(process: Process, timeout: time.Duration) -> (proc
|
||||
start_tick = time.tick_now()
|
||||
continue
|
||||
}
|
||||
|
||||
_process_close(process)
|
||||
return process_state, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
@@ -733,6 +730,7 @@ _timed_wait_on_handle :: proc(process: Process, timeout: time.Duration) -> (proc
|
||||
}
|
||||
|
||||
if errno = linux.waitid(.PIDFD, linux.Id(process.handle), &info, {.WEXITED, .WNOHANG, .WNOWAIT}, nil); errno != .NONE {
|
||||
_process_close(process)
|
||||
return process_state, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
@@ -767,6 +765,8 @@ _timed_wait_on_handle :: proc(process: Process, timeout: time.Duration) -> (proc
|
||||
process_state.success = false
|
||||
}
|
||||
}
|
||||
|
||||
_process_close(process)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -783,6 +783,7 @@ _timed_wait_on_pid :: proc(process: Process, timeout: time.Duration) -> (process
|
||||
org_sigset: linux.Sig_Set
|
||||
errno := linux.rt_sigprocmask(.SIG_BLOCK, &sigchld_set, &org_sigset)
|
||||
if errno != .NONE {
|
||||
_process_close(process)
|
||||
return process_state, _get_platform_error(errno)
|
||||
}
|
||||
defer linux.rt_sigprocmask(.SIG_SETMASK, &org_sigset, nil)
|
||||
@@ -814,6 +815,7 @@ _timed_wait_on_pid :: proc(process: Process, timeout: time.Duration) -> (process
|
||||
timeout -= time.tick_since(start_tick)
|
||||
start_tick = time.tick_now()
|
||||
case .EINVAL:
|
||||
_process_close(process)
|
||||
return process_state, _get_platform_error(errno)
|
||||
}
|
||||
}
|
||||
@@ -852,13 +854,13 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (Process_Stat
|
||||
return process_state, .Timeout
|
||||
}
|
||||
if errno != .NONE {
|
||||
_process_close(process)
|
||||
return process_state, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
return _reap_terminated(process)
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_process_close :: proc(process: Process) -> Error {
|
||||
if process.handle == 0 || process.handle == PIDFD_UNASSIGNED {
|
||||
return nil
|
||||
@@ -872,3 +874,7 @@ _process_kill :: proc(process: Process) -> Error {
|
||||
return _get_platform_error(linux.kill(linux.Pid(process.pid), .SIGKILL))
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_process_terminate :: proc(process: Process) -> Error {
|
||||
return _get_platform_error(linux.kill(linux.Pid(process.pid), .SIGTERM))
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#+build netbsd
|
||||
package os
|
||||
|
||||
import "core:c"
|
||||
foreign import libc "system:c"
|
||||
|
||||
@(private)
|
||||
@@ -10,7 +9,7 @@ foreign libc {
|
||||
_lwp_self :: proc() -> i32 ---
|
||||
|
||||
@(link_name="sysctlbyname")
|
||||
_sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
|
||||
_sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> i32 ---
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -28,4 +27,4 @@ _get_processor_core_count :: proc() -> int {
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ _SC_NPROCESSORS_ONLN :: 503
|
||||
@(private, require_results)
|
||||
_get_processor_core_count :: proc() -> int {
|
||||
return int(_sysconf(_SC_NPROCESSORS_ONLN))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,10 +329,6 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_stat
|
||||
return
|
||||
}
|
||||
|
||||
_process_close :: proc(process: Process) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_process_kill :: proc(process: Process) -> (err: Error) {
|
||||
_process_handle_still_valid(process) or_return
|
||||
|
||||
@@ -342,3 +338,13 @@ _process_kill :: proc(process: Process) -> (err: Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_process_terminate :: proc(process: Process) -> (err: Error) {
|
||||
_process_handle_still_valid(process) or_return
|
||||
|
||||
if posix.kill(posix.pid_t(process.pid), .SIGTERM) != .OK {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,19 +2,16 @@
|
||||
package os
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:bytes"
|
||||
import "core:c"
|
||||
import "core:sys/darwin"
|
||||
import "core:sys/posix"
|
||||
import "core:sys/unix"
|
||||
import "core:time"
|
||||
|
||||
foreign import libc "system:System"
|
||||
foreign import pthread "system:System"
|
||||
foreign import libsystem "system:System"
|
||||
|
||||
foreign libc {
|
||||
foreign libsystem {
|
||||
sysctl :: proc "c" (
|
||||
name: [^]i32, namelen: u32,
|
||||
oldp: rawptr, oldlenp: ^uint,
|
||||
@@ -22,23 +19,24 @@ foreign libc {
|
||||
) -> posix.result ---
|
||||
|
||||
@(link_name="sysctlbyname")
|
||||
_sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
|
||||
_sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> posix.result ---
|
||||
|
||||
// NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
|
||||
// For older versions there is `syscall(SYS_thread_selfid)`, but not really
|
||||
// the same thing apparently.
|
||||
pthread_threadid_np :: proc "c" (rawptr, ^u64) -> i32 ---
|
||||
}
|
||||
|
||||
_get_current_thread_id :: proc "contextless" () -> int {
|
||||
tid: u64
|
||||
// NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
|
||||
// For older versions there is `syscall(SYS_thread_selfid)`, but not really
|
||||
// the same thing apparently.
|
||||
foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
|
||||
pthread_threadid_np(nil, &tid)
|
||||
return int(tid)
|
||||
}
|
||||
|
||||
_get_processor_core_count :: proc() -> int {
|
||||
count : int = 0
|
||||
count: int = 0
|
||||
count_size := size_of(count)
|
||||
if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
|
||||
if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == .OK {
|
||||
if count > 0 {
|
||||
return count
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_stat
|
||||
return
|
||||
}
|
||||
|
||||
_process_close :: proc(process: Process) -> Error {
|
||||
_process_kill :: proc(process: Process) -> (err: Error) {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_process_kill :: proc(process: Process) -> (err: Error) {
|
||||
_process_terminate :: proc(process: Process) -> (err: Error) {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
|
||||
@@ -539,6 +539,7 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_stat
|
||||
exit_code: u32
|
||||
if !win32.GetExitCodeProcess(handle, &exit_code) {
|
||||
err =_get_platform_error()
|
||||
_process_close(process)
|
||||
return
|
||||
}
|
||||
time_created: win32.FILETIME
|
||||
@@ -547,6 +548,7 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_stat
|
||||
time_user: win32.FILETIME
|
||||
if !win32.GetProcessTimes(handle, &time_created, &time_exited, &time_kernel, &time_user) {
|
||||
err = _get_platform_error()
|
||||
_process_close(process)
|
||||
return
|
||||
}
|
||||
process_state = {
|
||||
@@ -557,17 +559,18 @@ _process_wait :: proc(process: Process, timeout: time.Duration) -> (process_stat
|
||||
system_time = _filetime_to_duration(time_kernel),
|
||||
user_time = _filetime_to_duration(time_user),
|
||||
}
|
||||
_process_close(process)
|
||||
return
|
||||
case win32.WAIT_TIMEOUT:
|
||||
err = General_Error.Timeout
|
||||
return
|
||||
case:
|
||||
err = _get_platform_error()
|
||||
_process_close(process)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_process_close :: proc(process: Process) -> Error {
|
||||
if !win32.CloseHandle(win32.HANDLE(process.handle)) {
|
||||
return _get_platform_error()
|
||||
@@ -587,6 +590,49 @@ _process_kill :: proc(process: Process) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
_process_terminate :: proc(process: Process) -> Error {
|
||||
Enum_Windows_State :: struct {
|
||||
has_windows: bool,
|
||||
pid: int,
|
||||
}
|
||||
state: Enum_Windows_State
|
||||
state.pid = process.pid
|
||||
ok := win32.EnumWindows(
|
||||
proc "system" (hwnd: win32.HWND, lParam: win32.LPARAM) -> win32.BOOL {
|
||||
#assert(size_of(win32.LPARAM) == size_of(^Enum_Windows_State))
|
||||
state := (^Enum_Windows_State)(rawptr(uintptr(lParam)))
|
||||
|
||||
dwPid: win32.DWORD
|
||||
win32.GetWindowThreadProcessId(hwnd, &dwPid)
|
||||
if (dwPid == win32.DWORD(state.pid)) {
|
||||
state.has_windows = true
|
||||
win32.PostMessageW(hwnd, win32.WM_CLOSE, 0, 0)
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
win32.LPARAM(uintptr(&state)),
|
||||
)
|
||||
if state.has_windows {
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := _get_platform_error()
|
||||
kill_err := _process_kill(process)
|
||||
return kill_err == nil ? nil : err
|
||||
}
|
||||
|
||||
if !win32.GenerateConsoleCtrlEvent(win32.CTRL_C_EVENT, win32.DWORD(process.pid)) {
|
||||
err := _get_platform_error()
|
||||
kill_err := _process_kill(process)
|
||||
return kill_err == nil ? nil : err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_filetime_to_duration :: proc(filetime: win32.FILETIME) -> time.Duration {
|
||||
ticks := u64(filetime.dwHighDateTime)<<32 | u64(filetime.dwLowDateTime)
|
||||
return time.Duration(ticks * 100)
|
||||
|
||||
Reference in New Issue
Block a user