|
| 1 | + |
| 2 | +import ../n_errno |
| 3 | +import ../signal_impl/c_api |
| 4 | +import ../resource_impl/types |
| 5 | +from ./common import raiseErrno |
| 6 | + |
| 7 | +const DWin = defined(windows) |
| 8 | +when DWin: |
| 9 | + import ../../private/iph_utils # with_Py_SUPPRESS_IPH |
| 10 | +else: |
| 11 | + from std/posix import Pid |
| 12 | + |
| 13 | +when DWin: |
| 14 | + type Res = int |
| 15 | + type WAIT_TYPE = cint |
| 16 | + proc cwait(termstat: var cint, pid: cint, options: cint): Res{.importc: "_cwait", header: "<process.h>".} |
| 17 | + proc c_waitpid(pid: cint, status: var WAIT_TYPE, options: cint): Res = |
| 18 | + with_Py_SUPPRESS_IPH: |
| 19 | + result = cwait(status, pid, options) |
| 20 | + template handle_status(res: Res): int = status shl 8 |
| 21 | + template decl_WAIT_STATUS(status; val: cint = 0) = |
| 22 | + var status: WAIT_TYPE = val |
| 23 | + |
| 24 | +elif defined(unix): |
| 25 | + type Res = cint |
| 26 | + type WAIT_TYPE{.importc.} = cint # also may be union |
| 27 | + proc c_waitpid(pid: cint, status: var WAIT_TYPE, options: cint): Res{.importc: "waitpid", header: "<sys/wait.h>".} |
| 28 | + template handle_status(res: Res): int = status.int |
| 29 | + |
| 30 | + template decl_STATUS_INIT(status; val: cint = 0) = |
| 31 | + var status{.noInit.}: WAIT_TYPE |
| 32 | + {.emit: ["WAIT_STATUS_INIT(", status, ") = ", val, ");"].} |
| 33 | + |
| 34 | + proc WAIT_STATUS_INT(status: WAIT_TYPE): cint{.inline.} = |
| 35 | + {.emit: [result, "= WAIT_STATUS_INT(", status, ")"].} |
| 36 | + |
| 37 | +when not DWin: |
| 38 | + template genW(name){.dirty.} = |
| 39 | + proc name(status: WAIT_TYPE): cint{.importc, header: "<sys/wait.h>".} |
| 40 | + proc name*(status: int): bool = |
| 41 | + decl_STATUS_INIT(wait_status, status.cint) |
| 42 | + name(wait_status) != 0 |
| 43 | + |
| 44 | + genW WCOREDUMP |
| 45 | + genW WIFCONTINUED |
| 46 | + genW WIFSTOPPED |
| 47 | + genW WIFSIGNALED |
| 48 | + genW WIFEXITED |
| 49 | + genW WEXITSTATUS |
| 50 | + genW WTERMSIG |
| 51 | + genW WSTOPSIG |
| 52 | + |
| 53 | + template genWConst(name){.dirty.} = |
| 54 | + let `c name`{.importc: astToStr(name), header: "<sys/wait.h>".}: cint |
| 55 | + let name* = int `c name` |
| 56 | + |
| 57 | + genWConst WCONTINUED |
| 58 | + genWConst WEXITED |
| 59 | + genWConst WSTOPPED |
| 60 | + genWConst WUNTRACED |
| 61 | + genWConst WNOHANG |
| 62 | + genWConst WNOWAIT |
| 63 | + |
| 64 | + |
| 65 | +template waitX_impl[R](res, status: untyped; resExpr){.dirty.} = |
| 66 | + var async_err: int |
| 67 | + decl_STATUS_INIT(status) |
| 68 | +
|
| 69 | + while true: |
| 70 | + res = resExpr |
| 71 | + if res >= 0 or isErr(EINTR): |
| 72 | + break |
| 73 | + async_err = PyErr_CheckSignals() |
| 74 | + if async_err != 0: |
| 75 | + break |
| 76 | + if res < 0: |
| 77 | + raiseErrno() |
| 78 | +
|
| 79 | +when declared(c_waitpid): |
| 80 | + proc waitpid*(pid: int, options: int): tuple[pid: int, status: int] = |
| 81 | + let |
| 82 | + options = options.cint |
| 83 | + pid = pid.cint |
| 84 | + var res: Res |
| 85 | + waitX_impl[Res](res, status, c_waitpid(pid, status, options)) |
| 86 | + (res.int, handle_status(status)) |
| 87 | +
|
| 88 | +when not DWin: |
| 89 | + proc wait(status: var WAIT_TYPE): Res{.importc, header: "<sys/wait.h>".} |
| 90 | + proc wait*(): tuple[pid: int, status: int] = |
| 91 | + var res: Pid |
| 92 | + waitX_impl[Pid](res, status, wait(status)) |
| 93 | + (res.int, WAIT_STATUS_INT status) |
| 94 | +
|
| 95 | + proc wait_helper(pid: Pid, status: cint, rusage: var posix.RUsage): |
| 96 | + tuple[pid: int, status: int, rusage: struct_rusage] = |
| 97 | + if pid == -1: |
| 98 | + raiseErrno() |
| 99 | + |
| 100 | + #[If wait succeeded but no child was ready to report status, ru will not |
| 101 | + have been populated.]# |
| 102 | + if pid == 0: |
| 103 | + zeroMem(addr rusage, sizeof(rusage)) |
| 104 | + let res = rusage.toPyObject() |
| 105 | + (pid.int, status.int, res) |
| 106 | + |
| 107 | + proc wait3(status: var WAIT_TYPE, options: cint, rusage: var posix.RUsage): Res{.importc, header: "<sys/wait.h>".} |
| 108 | + proc wait3*(options: int): tuple[pid: int, status: int, rusage: struct_rusage] = |
| 109 | + var |
| 110 | + res: Pid |
| 111 | + ru: posix.RUsage |
| 112 | + waitX_impl[Pid](res, status, wait3(status, options.cint, ru)) |
| 113 | + wait_helper(res, WAIT_STATUS_INT(status), ru) |
| 114 | +
|
| 115 | + proc wait4(pid: Pid, status: var WAIT_TYPE, options: cint, rusage: var posix.RUsage): Res{.importc, header: "<sys/wait.h>".} |
| 116 | + proc wait4*(pid: int, options: int): tuple[pid: int, status: int, rusage: struct_rusage] = |
| 117 | + var |
| 118 | + res: Pid |
| 119 | + ru: posix.RUsage |
| 120 | + let pid = pid.Pid |
| 121 | + waitX_impl[Pid](res, status, wait4(pid, status, options.cint, ru)) |
| 122 | + wait_helper(res, WAIT_STATUS_INT(status), ru) |
| 123 | +
|
| 124 | +proc waitstatus_to_exitcode*(status: int): int = |
| 125 | + when not defined(windows): |
| 126 | + decl_STATUS_INIT(wait_status, status.cint) |
| 127 | + var exitcode: cint |
| 128 | + if bool WIFEXITED(wait_status): |
| 129 | + exitcode = WEXITSTATUS(wait_status) |
| 130 | + # Sanity check to provide warranty on the function behavior. |
| 131 | + # It should not occur in practice |
| 132 | + if exitcode < 0: |
| 133 | + raise newException(ValueError, "invalid WEXITSTATUS: " & $exitcode) |
| 134 | + elif bool WIFSIGNALED(wait_status): |
| 135 | + let signum = WTERMSIG(wait_status) |
| 136 | + # Sanity check to provide warranty on the function behavior. |
| 137 | + # It should not occur in practice |
| 138 | + if signum <= 0: |
| 139 | + raise newException(ValueError, "invalid WTERMSIG: " & $signum) |
| 140 | + exitcode = -signum |
| 141 | + elif bool WIFSTOPPED(wait_status): |
| 142 | + # Status only received if the process is being traced |
| 143 | + # or if waitpid() was called with WUNTRACED option. |
| 144 | + let signum = WSTOPSIG(wait_status) |
| 145 | + raise newException(ValueError, "process stopped by delivery of signal " & $signum) |
| 146 | + else: |
| 147 | + raise newException(ValueError, "invalid wait status: " & $status) |
| 148 | + result = int exitcode |
| 149 | + else: |
| 150 | + # Windows implementation: see os.waitpid() implementation |
| 151 | + # which uses _cwait(). |
| 152 | + let exitcode = (status.uint64 shr 8) |
| 153 | + # ExitProcess() accepts an UINT type: |
| 154 | + # reject exit code which doesn't fit in an UINT |
| 155 | + if exitcode > cuint.high.uint64: |
| 156 | + raise newException(ValueError, "invalid exit code: " & $exitcode) |
| 157 | + result = exitcode.int |
| 158 | +
|
0 commit comments