Skip to content

Commit f25a731

Browse files
committed
feat(Lib/os): wait* func, W* consts
1 parent b1b22cf commit f25a731

File tree

2 files changed

+159
-1
lines changed

2 files changed

+159
-1
lines changed

src/pylib/Lib/n_os.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
import ../private/trans_imp
33
impExp os_impl,
4-
consts, posix_like, subp, utils, path, walkImpl, listdirx, randoms
4+
consts, posix_like, subp, utils, path, walkImpl, listdirx, randoms, waits
55

66
when not defined(js):
77
import ./os_impl/[

src/pylib/Lib/os_impl/waits.nim

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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

Comments
 (0)