Skip to content

Commit 746059e

Browse files
authored
unix-ffi: Fixing behavior to avoid zombie threads
This commit is fixing this issue: #780 Now the pid of child thread will be stored and closed properly. Even with context manager. Signed-off-by: Kirill Lukonin (Evil Wireless Man) <[email protected]>
1 parent bdc4706 commit 746059e

File tree

1 file changed

+50
-16
lines changed

1 file changed

+50
-16
lines changed

unix-ffi/os/os/__init__.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
write_ = libc.func("i", "write", "iPi")
4646
close_ = libc.func("i", "close", "i")
4747
dup_ = libc.func("i", "dup", "i")
48+
dup2_ = libc.func("i", "dup2", "ii")
4849
access_ = libc.func("i", "access", "si")
4950
fork_ = libc.func("i", "fork", "")
5051
pipe_ = libc.func("i", "pipe", "p")
@@ -207,6 +208,10 @@ def dup(fd):
207208
check_error(r)
208209
return r
209210

211+
def dup2(oldfd, newfd):
212+
r = dup2_(oldfd, newfd)
213+
check_error(r)
214+
return r
210215

211216
def access(path, mode):
212217
return access_(path, mode) == 0
@@ -293,24 +298,53 @@ def urandom(n):
293298
with builtins.open("/dev/urandom", "rb") as f:
294299
return f.read(n)
295300

301+
class _PopenStream:
302+
def __init__(self, fd, pid, mode):
303+
self._fd = open(f"/dev/fd/{fd}", mode)
304+
self._pid = pid
305+
self._closed = False
306+
self._exitcode = None
296307

297-
def popen(cmd, mode="r"):
298-
import builtins
308+
def __enter__(self):
309+
return self._fd
310+
311+
def __exit__(self, exc_type, exc_val, exc_tb):
312+
self.close()
299313

300-
i, o = pipe()
301-
if mode[0] == "w":
302-
i, o = o, i
314+
def close(self):
315+
if not self._closed:
316+
try:
317+
self._fd.close()
318+
finally:
319+
pid, status = waitpid(self._pid, 0)
320+
self._exitcode = status
321+
self._closed = True
322+
return self._exitcode
323+
324+
325+
def popen(cmd, mode='r'):
326+
rfd, wfd = pipe()
303327
pid = fork()
304-
if not pid:
305-
if mode[0] == "r":
306-
close(1)
328+
329+
if pid == 0:
330+
# --- CHILD ---
331+
if mode[0] == 'r':
332+
close(rfd)
333+
dup2(wfd, 1) # connect pipe to stdout
334+
close(wfd)
307335
else:
308-
close(0)
309-
close(i)
310-
dup(o)
311-
close(o)
312-
s = system(cmd)
313-
_exit(s)
336+
close(wfd)
337+
dup2(rfd, 0) # connect pipe to stdin
338+
close(rfd)
339+
340+
execvp("sh", ["sh", "-c", cmd])
341+
# If execvp SUCCEEDS, the code below is NEVER executed.
342+
_exit(127)
343+
344+
# --- PARENT ---
345+
if mode[0] == 'r':
346+
close(wfd)
347+
return _PopenStream(rfd, pid, mode)
314348
else:
315-
close(o)
316-
return builtins.open(i, mode)
349+
close(rfd)
350+
return _PopenStream(wfd, pid, mode)

0 commit comments

Comments
 (0)