From dbad23385daca3e492a88b2619c81bae74deee47 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 22 Sep 2024 14:02:32 +1100 Subject: [PATCH 1/8] [os2]: Implement pipe_has_data procedure --- core/os/os2/errors_windows.odin | 4 +++- core/os/os2/pipe.odin | 37 +++++++++++++++++++++++++++++++++ core/os/os2/pipe_linux.odin | 26 +++++++++++++++++++++++ core/os/os2/pipe_posix.odin | 25 ++++++++++++++++++++++ core/os/os2/pipe_windows.odin | 12 +++++++++++ core/sys/windows/kernel32.odin | 8 +++++++ 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin index 56acd503fe0..404560f987b 100644 --- a/core/os/os2/errors_windows.odin +++ b/core/os/os2/errors_windows.odin @@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error { case win32.ERROR_NEGATIVE_SEEK: return .Invalid_Offset + case win32.ERROR_BROKEN_PIPE: + return .Broken_Pipe + case win32.ERROR_BAD_ARGUMENTS, win32.ERROR_INVALID_PARAMETER, win32.ERROR_NOT_ENOUGH_MEMORY, win32.ERROR_NO_MORE_FILES, win32.ERROR_LOCK_VIOLATION, - win32.ERROR_BROKEN_PIPE, win32.ERROR_CALL_NOT_IMPLEMENTED, win32.ERROR_INSUFFICIENT_BUFFER, win32.ERROR_INVALID_NAME, diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin index 9254d6f8e03..5d3e8368eec 100644 --- a/core/os/os2/pipe.odin +++ b/core/os/os2/pipe.odin @@ -1,6 +1,43 @@ package os2 +/* +Create an anonymous pipe. + +This procedure creates an anonymous pipe, returning two ends of the pipe, `r` +and `w`. The file `r` is the readable end of the pipe. The file `w` is a +writeable end of the pipe. + +Pipes are used as an inter-process communication mechanism, to communicate +between a parent and a child process. The child uses one end of the pipe to +write data, and the parent uses the other end to read from the pipe +(or vice-versa). When a parent passes one of the ends of the pipe to the child +process, that end of the pipe needs to be closed by the parent, before any data +is attempted to be read. + +Although pipes look like files and is compatible with most file APIs in package +os2, the way it's meant to be read is different. Due to asynchronous nature of +the communication channel, the data may not be present at the time of a read +request. The other scenario is when a pipe has no data because the other end +of the pipe was closed by the child process. +*/ @(require_results) pipe :: proc() -> (r, w: ^File, err: Error) { return _pipe() } + +/* +Check if the pipe has any data. + +This procedure checks whether a read-end of the pipe has data that can be +read, and returns `true`, if the pipe has readable data, and `false` if the +pipe is empty. This procedure does not block the execution of the current +thread. + +**Note**: If the other end of the pipe was closed by the child process, the +`.Broken_Pipe` +can be returned by this procedure. Handle these errors accordingly. +*/ +@(require_results) +pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + return _pipe_has_data(r) +} diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin index ac3382bc315..852674c69f0 100644 --- a/core/os/os2/pipe_linux.odin +++ b/core/os/os2/pipe_linux.odin @@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) { return } + +@(require_results) +_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + if r == nil || r.impl == nil { + return false, nil + } + fd := linux.Fd((^File_Impl)(r.impl).fd) + poll_fds := []linux.Poll_Fd { + linux.Poll_Fd { + fd = fd, + events = {.IN, .HUP}, + }, + } + n, errno := linux.poll(poll_fds, 0) + if n != 1 || errno != nil { + return false, _get_platform_error(errno) + } + pipe_events := poll_fds[0].revents + if pipe_events >= {.IN} { + return true, nil + } + if pipe_events >= {.HUP} { + return false, .Broken_Pipe + } + return false, nil +} \ No newline at end of file diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin index 487e32aea25..463f29f011f 100644 --- a/core/os/os2/pipe_posix.odin +++ b/core/os/os2/pipe_posix.odin @@ -44,3 +44,28 @@ _pipe :: proc() -> (r, w: ^File, err: Error) { return } +@(require_results) +_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + if r == nil || r.impl == nil { + return false, nil + } + fd := posix.FD((^File_Impl)(r.impl).fd) + poll_fds := []posix.pollfd { + posix.pollfd { + fd = fd, + events = {.IN, .HUP}, + }, + } + n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0) + if n != 1 { + return false, _get_platform_error() + } + pipe_events := poll_fds[0].revents + if pipe_events >= {.IN} { + return true, nil + } + if pipe_events >= {.HUP} { + return false, .Broken_Pipe + } + return false, nil +} \ No newline at end of file diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index ee93fb6834d..5a6048f0651 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) { return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil } +@(require_results) +_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + if r == nil || r.impl == nil { + return false, nil + } + handle := win32.HANDLE((^File_Impl)(r.impl).fd) + bytes_available: u32 + if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) { + return false, _get_platform_error() + } + return bytes_available > 0, nil +} \ No newline at end of file diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 2771581e611..d9648ec45d7 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -381,6 +381,14 @@ foreign kernel32 { nDefaultTimeOut: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, ) -> HANDLE --- + PeekNamedPipe :: proc( + hNamedPipe: HANDLE, + lpBuffer: rawptr, + nBufferSize: u32, + lpBytesRead: ^u32, + lpTotalBytesAvail: ^u32, + lpBytesLeftThisMessage: ^u32, + ) -> BOOL --- CancelIo :: proc(handle: HANDLE) -> BOOL --- GetOverlappedResult :: proc( hFile: HANDLE, From 842f1ae304f2bd983a6acd4da3aafa4ae74912dc Mon Sep 17 00:00:00 2001 From: flysand7 Date: Thu, 26 Sep 2024 07:13:00 +1100 Subject: [PATCH 2/8] Fix indentation issues --- core/os/os2/pipe_windows.odin | 8 ++++---- core/sys/windows/kernel32.odin | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index 5a6048f0651..d6dc47c9cc0 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -22,8 +22,8 @@ _pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { } handle := win32.HANDLE((^File_Impl)(r.impl).fd) bytes_available: u32 - if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) { - return false, _get_platform_error() - } - return bytes_available > 0, nil + if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) { + return false, _get_platform_error() + } + return bytes_available > 0, nil } \ No newline at end of file diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index d9648ec45d7..8be50bceb5c 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -382,13 +382,13 @@ foreign kernel32 { lpSecurityAttributes: LPSECURITY_ATTRIBUTES, ) -> HANDLE --- PeekNamedPipe :: proc( - hNamedPipe: HANDLE, - lpBuffer: rawptr, - nBufferSize: u32, - lpBytesRead: ^u32, - lpTotalBytesAvail: ^u32, - lpBytesLeftThisMessage: ^u32, - ) -> BOOL --- + hNamedPipe: HANDLE, + lpBuffer: rawptr, + nBufferSize: u32, + lpBytesRead: ^u32, + lpTotalBytesAvail: ^u32, + lpBytesLeftThisMessage: ^u32, + ) -> BOOL --- CancelIo :: proc(handle: HANDLE) -> BOOL --- GetOverlappedResult :: proc( hFile: HANDLE, From acfac3cf2d14f6f27beae9e305b0dc0b57aa6321 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Thu, 26 Sep 2024 07:25:40 +1100 Subject: [PATCH 3/8] Add missing registers in User_Regs on AMD64 --- core/sys/linux/types.odin | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 0e5b8218b34..07c654749bd 100644 --- a/core/sys/linux/types.odin +++ b/core/sys/linux/types.odin @@ -1136,6 +1136,12 @@ when ODIN_ARCH == .arm32 { eflags: uint, rsp: uint, ss: uint, + fs_base: uint, + gs_base: uint, + ds: uint, + es: uint, + fs: uint, + gs: uint, } // All floating point state _Arch_User_FP_Regs :: struct { From abd52529a661e8ed6e307029fa390efd1c334b8d Mon Sep 17 00:00:00 2001 From: flysand7 Date: Thu, 26 Sep 2024 07:26:01 +1100 Subject: [PATCH 4/8] Revert "Add missing registers in User_Regs on AMD64" This reverts commit acfac3cf2d14f6f27beae9e305b0dc0b57aa6321. --- core/sys/linux/types.odin | 6 ------ 1 file changed, 6 deletions(-) diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 07c654749bd..0e5b8218b34 100644 --- a/core/sys/linux/types.odin +++ b/core/sys/linux/types.odin @@ -1136,12 +1136,6 @@ when ODIN_ARCH == .arm32 { eflags: uint, rsp: uint, ss: uint, - fs_base: uint, - gs_base: uint, - ds: uint, - es: uint, - fs: uint, - gs: uint, } // All floating point state _Arch_User_FP_Regs :: struct { From 7deb28c8101d2384b01fdd735bb8e649c553ebbc Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 28 Sep 2024 10:30:54 +1100 Subject: [PATCH 5/8] [os2/process]: Unindent doc comments --- core/os/os2/process.odin | 247 +++++++++++++++++++-------------------- misc/odin.res | Bin 0 -> 8592 bytes 2 files changed, 123 insertions(+), 124 deletions(-) create mode 100644 misc/odin.res diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index ce65987b062..7b8f22a9939 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -4,13 +4,13 @@ import "base:runtime" import "core:time" /* - In procedures that explicitly state this as one of the allowed values, - specifies an infinite timeout. +In procedures that explicitly state this as one of the allowed values, +specifies an infinite timeout. */ TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity /* - Arguments to the current process. +Arguments to the current process. */ args := get_args() @@ -24,17 +24,17 @@ get_args :: proc() -> []string { } /* - Exit the current process. +Exit the current process. */ exit :: proc "contextless" (code: int) -> ! { _exit(code) } /* - Obtain the UID of the current process. +Obtain the UID of the current process. - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_uid :: proc() -> int { @@ -42,15 +42,15 @@ get_uid :: proc() -> int { } /* - Obtain the effective UID of the current process. - - The effective UID is typically the same as the UID of the process. In case - the process was run by a user with elevated permissions, the process may - lower the privilege to perform some tasks without privilege. In these cases - the real UID of the process and the effective UID are different. - - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +Obtain the effective UID of the current process. + +The effective UID is typically the same as the UID of the process. In case +the process was run by a user with elevated permissions, the process may +lower the privilege to perform some tasks without privilege. In these cases +the real UID of the process and the effective UID are different. + +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_euid :: proc() -> int { @@ -58,10 +58,10 @@ get_euid :: proc() -> int { } /* - Obtain the GID of the current process. - - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +Obtain the GID of the current process. + +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_gid :: proc() -> int { @@ -69,15 +69,15 @@ get_gid :: proc() -> int { } /* - Obtain the effective GID of the current process. - - The effective GID is typically the same as the GID of the process. In case - the process was run by a user with elevated permissions, the process may - lower the privilege to perform some tasks without privilege. In these cases - the real GID of the process and the effective GID are different. - - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +Obtain the effective GID of the current process. + +The effective GID is typically the same as the GID of the process. In case +the process was run by a user with elevated permissions, the process may +lower the privilege to perform some tasks without privilege. In these cases +the real GID of the process and the effective GID are different. + +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_egid :: proc() -> int { @@ -85,7 +85,7 @@ get_egid :: proc() -> int { } /* - Obtain the ID of the current process. +Obtain the ID of the current process. */ @(require_results) get_pid :: proc() -> int { @@ -93,13 +93,13 @@ get_pid :: proc() -> int { } /* - Obtain the ID of the parent process. +Obtain the ID of the parent process. - **Note(windows)**: Windows does not mantain strong relationships between - parent and child processes. This function returns the ID of the process - that has created the current process. In case the parent has died, the ID - returned by this function can identify a non-existent or a different - process. +**Note(windows)**: Windows does not mantain strong relationships between +parent and child processes. This function returns the ID of the process +that has created the current process. In case the parent has died, the ID +returned by this function can identify a non-existent or a different +process. */ @(require_results) get_ppid :: proc() -> int { @@ -107,7 +107,7 @@ get_ppid :: proc() -> int { } /* - Obtain ID's of all processes running in the system. +Obtain ID's of all processes running in the system. */ @(require_results) process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) { @@ -115,9 +115,9 @@ process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) { } /* - Bit set specifying which fields of the `Process_Info` struct need to be - obtained by the `process_info()` procedure. Each bit corresponds to a - field in the `Process_Info` struct. +Bit set specifying which fields of the `Process_Info` struct need to be +obtained by the `process_info()` procedure. Each bit corresponds to a +field in the `Process_Info` struct. */ Process_Info_Fields :: bit_set[Process_Info_Field] Process_Info_Field :: enum { @@ -134,8 +134,8 @@ Process_Info_Field :: enum { ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir} /* - Contains information about the process as obtained by the `process_info()` - procedure. +Contains information about the process as obtained by the `process_info()` +procedure. */ Process_Info :: struct { // The information about a process the struct contains. `pid` is always @@ -162,19 +162,19 @@ Process_Info :: struct { } /* - Obtain information about a process. +Obtain information about a process. - This procedure obtains an information, specified by `selection` parameter of - a process given by `pid`. +This procedure obtains an information, specified by `selection` parameter of +a process given by `pid`. - Use `free_process_info` to free the memory allocated by this procedure. The - `free_process_info` procedure needs to be called, even if this procedure - returned an error, as some of the fields may have been allocated. +Use `free_process_info` to free the memory allocated by this procedure. The +`free_process_info` procedure needs to be called, even if this procedure +returned an error, as some of the fields may have been allocated. - **Note**: The resulting information may or may contain the fields specified - by the `selection` parameter. Always check whether the returned - `Process_Info` struct has the required fields before checking the error code - returned by this procedure. +**Note**: The resulting information may or may contain the fields specified +by the `selection` parameter. Always check whether the returned +`Process_Info` struct has the required fields before checking the error code +returned by this procedure. */ @(require_results) process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -182,20 +182,20 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: } /* - Obtain information about a process. +Obtain information about a process. - This procedure obtains information, specified by `selection` parameter - about a process that has been opened by the application, specified in - the `process` parameter. +This procedure obtains information, specified by `selection` parameter +about a process that has been opened by the application, specified in +the `process` parameter. - Use `free_process_info` to free the memory allocated by this procedure. The - `free_process_info` procedure needs to be called, even if this procedure - returned an error, as some of the fields may have been allocated. +Use `free_process_info` to free the memory allocated by this procedure. The +`free_process_info` procedure needs to be called, even if this procedure +returned an error, as some of the fields may have been allocated. - **Note**: The resulting information may or may contain the fields specified - by the `selection` parameter. Always check whether the returned - `Process_Info` struct has the required fields before checking the error code - returned by this procedure. +**Note**: The resulting information may or may contain the fields specified +by the `selection` parameter. Always check whether the returned +`Process_Info` struct has the required fields before checking the error code +returned by this procedure. */ @(require_results) process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -203,19 +203,19 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, } /* - Obtain information about the current process. +Obtain information about the current process. - This procedure obtains the information, specified by `selection` parameter - about the currently running process. +This procedure obtains the information, specified by `selection` parameter +about the currently running process. - Use `free_process_info` to free the memory allocated by this procedure. The - `free_process_info` procedure needs to be called, even if this procedure - returned an error, as some of the fields may have been allocated. +Use `free_process_info` to free the memory allocated by this procedure. The +`free_process_info` procedure needs to be called, even if this procedure +returned an error, as some of the fields may have been allocated. - **Note**: The resulting information may or may contain the fields specified - by the `selection` parameter. Always check whether the returned - `Process_Info` struct has the required fields before checking the error code - returned by this procedure. +**Note**: The resulting information may or may contain the fields specified +by the `selection` parameter. Always check whether the returned +`Process_Info` struct has the required fields before checking the error code +returned by this procedure. */ @(require_results) current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -223,7 +223,7 @@ current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime. } /* - Obtain information about the specified process. +Obtain information about the specified process. */ process_info :: proc { process_info_by_pid, @@ -232,11 +232,11 @@ process_info :: proc { } /* - Free the information about the process. +Free the information about the process. - This procedure frees the memory occupied by process info using the provided - allocator. The allocator needs to be the same allocator that was supplied - to the `process_info` function. +This procedure frees the memory occupied by process info using the provided +allocator. The allocator needs to be the same allocator that was supplied +to the `process_info` function. */ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) { delete(pi.executable_path, allocator) @@ -254,13 +254,13 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) { } /* - Represents a process handle. +Represents a process handle. - When a process dies, the OS is free to re-use the pid of that process. The - `Process` struct represents a handle to the process that will refer to a - specific process, even after it has died. +When a process dies, the OS is free to re-use the pid of that process. The +`Process` struct represents a handle to the process that will refer to a +specific process, even after it has died. - **Note(linux)**: The `handle` will be referring to pidfd. +**Note(linux)**: The `handle` will be referring to pidfd. */ Process :: struct { pid: int, @@ -276,13 +276,13 @@ Process_Open_Flag :: enum { } /* - Open a process handle using it's pid. +Open a process handle using it's pid. - 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`. +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 `process_close()` function to close the process handle. */ @(require_results) process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) { @@ -322,28 +322,28 @@ Process_Desc :: struct { } /* - Create a new process and obtain its handle. - - This procedure creates a new process, with a given command and environment - strings as parameters. Use `environ()` to inherit the environment of the - current process. - - The `desc` parameter specifies the description of how the process should - 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. - - This procedure is not thread-safe. It may alter the inheritance properties - of file handles in an unpredictable manner. In case multiple threads change - handle inheritance properties, make sure to serialize all those calls. +Create a new process and obtain its handle. + +This procedure creates a new process, with a given command and environment +strings as parameters. Use `environ()` to inherit the environment of the +current process. + +The `desc` parameter specifies the description of how the process should +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. + +This procedure is not thread-safe. It may alter the inheritance properties +of file handles in an unpredictable manner. In case multiple threads change +handle inheritance properties, make sure to serialize all those calls. */ @(require_results) -process_start :: proc(desc := Process_Desc {}) -> (Process, Error) { +process_start :: proc(desc: Process_Desc) -> (Process, Error) { return _process_start(desc) } @@ -371,17 +371,17 @@ Process_State :: struct { } /* - Wait for a process event. +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. +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. - If the timeout has expired, the `General_Error.Timeout` is returned as - the error. +If the timeout has expired, the `General_Error.Timeout` is returned as +the error. - If an error is returned for any other reason, other than timeout, the - process state is considered undetermined. +If an error is returned for any other reason, other than timeout, the +process state is considered undetermined. */ @(require_results) process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) { @@ -389,12 +389,12 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_ } /* - Close the handle to a process. +Close the handle to 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 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. */ @(require_results) process_close :: proc(process: Process) -> (Error) { @@ -402,10 +402,9 @@ process_close :: proc(process: Process) -> (Error) { } /* - Terminate a process. - - This procedure terminates a process, specified by it's handle, `process`. +Terminate a process. +This procedure terminates a process, specified by it's handle, `process`. */ @(require_results) process_kill :: proc(process: Process) -> (Error) { diff --git a/misc/odin.res b/misc/odin.res new file mode 100644 index 0000000000000000000000000000000000000000..f9cace9c5fafb49861f7af71e3f9f4b7eafb5576 GIT binary patch literal 8592 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 z zEvUrAJJk_|+bl_EDdZ2gqbxfCSv%XKT2+d-JG%1m*dG)Z9s9)|6{JU)pBAz=G=n_p z2p`jB?(G9f(lH3{hVn7u({qHCar=?2#~E zAL5j25WT&I(@WiWv-R3_#PO@yf`y9~FBz*J zywrNxa!*g~U#3l;F>}`JmUDtT&z(2lv#Dvo_z4pUEn4amTuk+BVlvW?@L}{%W5zmJ zjq~@^@@hVK$WQ_TpJ5CJJOxI!ZM&+Se#-#^2YG60+O+AyLqc~R4>Sx7$=$b8BY;b1 zSMu71Hsr5L?4SYIQ{2x3xo9jf!??|<-&WNh|okiyc`6Tnc zns6i*=7m}IsR0vm%&f!+YxJf1-}&{K8nsZY+doJLSx2H_68ck-n`B-tTrtPTVHk zUto%a4U}J$NChmbEZZ1@m--$w+LvnsRiYUKKS#65alpDxTJcDbqjd5?s zP$(60l+nIr5l)at4<-F&WFHkIXMTVv)E=s$R4DRZB5i{evQCV{+e;g8hOp>ed?XYK z1@iOqp}4o6e7Y6cwjF)l7>i;K&9Kp~eSFE0<$ z$liS3U8azt^iU7Nn@&*4iYX`g1F7^5AU}2mo<=XjfiYSrNPmGcqKQMBLw0j9olgjg zSx)=n7*sSrV>gGCtsS7079%6^G9rG|!0S6vP?cno?JOXF6o#TOXB0(x@jm_a3L`qB zNC-1t@%i=Biy_F#$)!7x4cUVo5M7>yoK5DCWhOu&%0}{r7D$O002Q5YKIy{p<-|+7 zXig5w6k-&Hv_#1t(;&}&g3OH;lw(gp*4Y_ITxtT*yLiZH&#&(sM{@j0fm69?h*7$~G7++EcHcUFD{xkLo1M2u^@f^hHn3P>apyt%lRe+QEo zIi1e46mppi=}9+mVqp(l51$N)xD@%X;&5fj*Yw^)3#Vtbz>To6coe+~2`5(}ZqHO4 z3+_zVsEIw4S3Zu}Msmf-$jCr)++JK?E2iY@6I- zT|7+;eHPo8em(BS&0Dwc)NeEX?!Ei5*BZ`0wiFn@P=#GbwT1p5yhtYK+8o4e0`c zV}XDGdFa@2{=~rJbp!bVPp`p41oaR-)WLyJdZb@Ny5O6Ry;=w;Qxj0CCZJqxP!E=g zd?*5eJtb?p0s`xLAb04|^8=B7DeqK6ck4E`Jg}1v#*OI$0iT6g^FYt;0|DvBLi1Un zjLyJNATVmelD3aTYN*K{zUlUAH=O+^`sd`&=KqQO8C|Qfa9W?wD?fgzGq;BN{qNiK z-j*-bKp(0t{tcDMT~4*G8X8muRJA`WTh}-zRsU)3^El>T%}H7O!u%YIU04jmFk6|I z2xab5K9*rI4v$NOO7Mm#Vt%c&ouPBoiLVUCz zV=*C% z_ZW`+UH-Pfe~1M}j`a2u{GjvWC|$L}W_E6Fggf5e^~j@Prp_3>z14J$L7rZ2ZY+~j z_xTLc&5giF-RD^LNHTi4tNWdJG_s>tjq1KSr8Kkqx}&;ZpH7-Nw^sKx*jqpzf9m@L zHtH<1zOSJ{uMIWmz2e6{bNK?bQ~U@5U-tg&e;_io|A& Date: Sat, 28 Sep 2024 11:25:32 +1100 Subject: [PATCH 6/8] [os2/process]: Implement process_exec --- core/os/os2/process.odin | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 7b8f22a9939..25026dc7efe 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -1,6 +1,7 @@ package os2 import "base:runtime" +import "core:strings" import "core:time" /* @@ -347,6 +348,95 @@ process_start :: proc(desc: Process_Desc) -> (Process, Error) { return _process_start(desc) } +/* +Execute the process and capture stdout and stderr streams. + +This procedure creates a new process, with a given command and environment +strings as parameters, and waits until the process finishes execution. While +the process is running, this procedure accumulates the output of its stdout +and stderr streams and returns byte slices containing the captured data from +the streams. + +Use this function when the target process doesn't require any input from stdin, +in order to complete. + +This procedure does not free `stdout` and `stderr` slices before an error is +returned. Make sure to call `delete` on these slices. + +This procedure is not thread-safe. It may alter the inheritance properties +of file handles in an unpredictable manner. In case multiple threads change +handle inheritance properties, make sure to serialize all those calls. +*/ +@(require_results) +process_exec :: proc( + desc: Process_Desc, + allocator: runtime.Allocator, + loc := #caller_location, +) -> ( + state: Process_State, + stdout: []u8, + stderr: []u8, + err: Error, +) { + assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc) + assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc) + stdout_r, stdout_w := pipe() or_return + defer close(stdout_r) + stderr_r, stderr_w := pipe() or_return + defer close(stdout_w) + process: Process + { + // NOTE(flysand): Make sure the write-ends are closed, regardless + // of the outcome. This makes read-ends readable on our side. + defer close(stdout_w) + defer close(stderr_w) + desc := desc + desc.stdout = stdout_w + desc.stderr = stderr_w + process = process_start(desc) or_return + } + stdout_builder := strings.builder_make(allocator) or_return + stderr_builder := strings.builder_make(allocator) or_return + read_data: for { + buf: [1024]u8 + n: int + has_data: bool + hangup := false + has_data, err = pipe_has_data(stdout_r) + if has_data { + n, err = read(stdout_r, buf[:]) + strings.write_bytes(&stdout_builder, buf[:n]) + } + switch err { + case nil: // nothing + case .Broken_Pipe: + hangup = true + case: + return + } + has_data, err = pipe_has_data(stderr_r) + if has_data { + n, err = read(stderr_r, buf[:]) + strings.write_bytes(&stderr_builder, buf[:n]) + } + switch err { + case nil: // nothing + case .Broken_Pipe: + hangup = true + case: + return + } + if hangup { + break read_data + } + } + err = nil + stdout = transmute([]u8) strings.to_string(stdout_builder) + stderr = transmute([]u8) strings.to_string(stderr_builder) + state = process_wait(process) or_return + return +} + /* The state of the process after it has finished execution. */ From 0e446e1d68eb80213b1a539da0d1d03212b941f8 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 29 Sep 2024 07:54:29 +1100 Subject: [PATCH 7/8] adjust docs --- core/os/os2/process.odin | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 25026dc7efe..fd06dca7433 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -357,15 +357,12 @@ the process is running, this procedure accumulates the output of its stdout and stderr streams and returns byte slices containing the captured data from the streams. -Use this function when the target process doesn't require any input from stdin, -in order to complete. +This procedure expects that `stdout` and `stderr` fields of the `desc` parameter +are left at default, i.e. a `nil` value. You can not capture stdout/stderr and +redirect it to a file at the same time. This procedure does not free `stdout` and `stderr` slices before an error is returned. Make sure to call `delete` on these slices. - -This procedure is not thread-safe. It may alter the inheritance properties -of file handles in an unpredictable manner. In case multiple threads change -handle inheritance properties, make sure to serialize all those calls. */ @(require_results) process_exec :: proc( From ca9cfc71678d93abc3f9d1aa17ffdb05fbe01f15 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 29 Sep 2024 08:01:47 +1100 Subject: [PATCH 8/8] remove extra binary --- misc/odin.res | Bin 8592 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 misc/odin.res diff --git a/misc/odin.res b/misc/odin.res deleted file mode 100644 index f9cace9c5fafb49861f7af71e3f9f4b7eafb5576..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8592 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 z zEvUrAJJk_|+bl_EDdZ2gqbxfCSv%XKT2+d-JG%1m*dG)Z9s9)|6{JU)pBAz=G=n_p z2p`jB?(G9f(lH3{hVn7u({qHCar=?2#~E zAL5j25WT&I(@WiWv-R3_#PO@yf`y9~FBz*J zywrNxa!*g~U#3l;F>}`JmUDtT&z(2lv#Dvo_z4pUEn4amTuk+BVlvW?@L}{%W5zmJ zjq~@^@@hVK$WQ_TpJ5CJJOxI!ZM&+Se#-#^2YG60+O+AyLqc~R4>Sx7$=$b8BY;b1 zSMu71Hsr5L?4SYIQ{2x3xo9jf!??|<-&WNh|okiyc`6Tnc zns6i*=7m}IsR0vm%&f!+YxJf1-}&{K8nsZY+doJLSx2H_68ck-n`B-tTrtPTVHk zUto%a4U}J$NChmbEZZ1@m--$w+LvnsRiYUKKS#65alpDxTJcDbqjd5?s zP$(60l+nIr5l)at4<-F&WFHkIXMTVv)E=s$R4DRZB5i{evQCV{+e;g8hOp>ed?XYK z1@iOqp}4o6e7Y6cwjF)l7>i;K&9Kp~eSFE0<$ z$liS3U8azt^iU7Nn@&*4iYX`g1F7^5AU}2mo<=XjfiYSrNPmGcqKQMBLw0j9olgjg zSx)=n7*sSrV>gGCtsS7079%6^G9rG|!0S6vP?cno?JOXF6o#TOXB0(x@jm_a3L`qB zNC-1t@%i=Biy_F#$)!7x4cUVo5M7>yoK5DCWhOu&%0}{r7D$O002Q5YKIy{p<-|+7 zXig5w6k-&Hv_#1t(;&}&g3OH;lw(gp*4Y_ITxtT*yLiZH&#&(sM{@j0fm69?h*7$~G7++EcHcUFD{xkLo1M2u^@f^hHn3P>apyt%lRe+QEo zIi1e46mppi=}9+mVqp(l51$N)xD@%X;&5fj*Yw^)3#Vtbz>To6coe+~2`5(}ZqHO4 z3+_zVsEIw4S3Zu}Msmf-$jCr)++JK?E2iY@6I- zT|7+;eHPo8em(BS&0Dwc)NeEX?!Ei5*BZ`0wiFn@P=#GbwT1p5yhtYK+8o4e0`c zV}XDGdFa@2{=~rJbp!bVPp`p41oaR-)WLyJdZb@Ny5O6Ry;=w;Qxj0CCZJqxP!E=g zd?*5eJtb?p0s`xLAb04|^8=B7DeqK6ck4E`Jg}1v#*OI$0iT6g^FYt;0|DvBLi1Un zjLyJNATVmelD3aTYN*K{zUlUAH=O+^`sd`&=KqQO8C|Qfa9W?wD?fgzGq;BN{qNiK z-j*-bKp(0t{tcDMT~4*G8X8muRJA`WTh}-zRsU)3^El>T%}H7O!u%YIU04jmFk6|I z2xab5K9*rI4v$NOO7Mm#Vt%c&ouPBoiLVUCz zV=*C% z_ZW`+UH-Pfe~1M}j`a2u{GjvWC|$L}W_E6Fggf5e^~j@Prp_3>z14J$L7rZ2ZY+~j z_xTLc&5giF-RD^LNHTi4tNWdJG_s>tjq1KSr8Kkqx}&;ZpH7-Nw^sKx*jqpzf9m@L zHtH<1zOSJ{uMIWmz2e6{bNK?bQ~U@5U-tg&e;_io|A&