Andrew Moffat added the comment: Below I attached a script that reproduces the behavior of an assertion error on a specific fork condition for a process with a pty. Here are the results
Xubuntu x64: Python 2.7, parent_close_slave_before_child_write = False: WORKS Python 2.7, parent_close_slave_before_child_write = True: WORKS Python 3.2, parent_close_slave_before_child_write = False: WORKS Python 3.2, parent_close_slave_before_child_write = True: WORKS Mac OSX 10.7.2: Python 2.7, parent_close_slave_before_child_write = False: WORKS Python 2.7, parent_close_slave_before_child_write = True: WORKS Python 3.2, parent_close_slave_before_child_write = False: >> FAILS << Python 3.2, parent_close_slave_before_child_write = True: WORKS Here's dtruss -f for python 2.7 and 3.0 on Mac OSX: 2.7, parent_close_slave_before_child_write = False (WORKS) PID/THRD SYSCALL(args) = return 356/0xc4a: open_nocancel("/dev/ptmx\0", 0x20002, 0x0) = 3 0 356/0xc4a: ioctl(0x3, 0x20007454, 0xFFFFFFFF) = 0 0 356/0xc4a: ioctl(0x3, 0x20007452, 0x0) = 0 0 356/0xc4a: ioctl(0x3, 0x40807453, 0x10041AD20) = 0 0 356/0xc4a: stat64("/dev/ttys003\0", 0x7FFF5FBFF030, 0x0) = 0 0 356/0xc4a: open_nocancel("/dev/ttys003\0", 0x20002, 0x0) = 4 0 356/0xc4a: fork() = 362 0 362/0xc61: fork() = 0 0 362/0xc61: thread_selfid(0x100420BE0, 0x3, 0x1) = 3169 0 362/0xc61: getpid(0x100420BE0, 0x3, 0x0) = 362 0 362/0xc61: select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0) = 0 0 362/0xc61: setsid(0x0, 0x0, 0x1001A7B40) = 362 0 362/0xc61: close(0x3) = 0 0 362/0xc61: dup2(0x4, 0x0, 0x0) = 0 0 362/0xc61: dup2(0x4, 0x1, 0x0) = 1 0 362/0xc61: dup2(0x4, 0x2, 0x0) = 2 0 362/0xc61: getrlimit(0x1008, 0x7FFF5FBFF3D0, 0x0) = 0 0 ... close range ... 362/0xc61: ioctl(0x1, 0x40487413, 0x7FFF5FBFF360) = 0 0 362/0xc61: fstat64(0x1, 0x7FFF5FBFF2D0, 0x0) = 0 0 362/0xc61: open_nocancel("/dev/\0", 0x100004, 0x0) = 3 0 362/0xc61: fcntl_nocancel(0x3, 0x2, 0x1) = 0 0 362/0xc61: fstatfs64(0x3, 0x7FFF5FBFE7B0, 0x0) = 0 0 362/0xc61: getdirentries64(0x3, 0x1010AB600, 0x1000) = 3080 0 ... lots of lstat64 ... 362/0xc61: close_nocancel(0x3) = 0 0 362/0xc61: open("/dev/ttys003\0", 0x2, 0x1FF) = 3 0 362/0xc61: close(0x3) = 0 0 362/0xc61: write(0x1, "testing\0", 0x7) = 7 0 362/0xc61: close(0x1) = 0 0 356/0xc4a: select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0) = 0 0 356/0xc4a: close(0x4) = 0 0 356/0xc4a: read(0x3, "testing\0", 0x400) = 7 0 356/0xc4a: write_nocancel(0x1, "'testing'\n\0", 0xA) = 10 0 356/0xc4a: wait4(0x16A, 0x7FFF5FBFF3D4, 0x0) = 362 0 356/0xc4a: sigaction(0x2, 0x7FFF5FBFF800, 0x7FFF5FBFF830) = 0 0 3.0, parent_close_slave_before_child_write = False (FAILS) PID/THRD SYSCALL(args) = return 385/0xe53: open_nocancel("/dev/ptmx\0", 0x20002, 0xD15EB8) = 3 0 385/0xe53: ioctl(0x3, 0x20007454, 0xBFFFF1BC) = 0 0 385/0xe53: ioctl(0x3, 0x20007452, 0xBFFFF1BC) = 0 0 385/0xe53: ioctl(0x3, 0x40807453, 0x64F220) = 0 0 385/0xe53: stat64("/dev/ttys003\0", 0xBFFFF164, 0x64F220) = 0 0 385/0xe53: open_nocancel("/dev/ttys003\0", 0x20002, 0x0) = 4 0 385/0xe53: fork() = 389 0 389/0xe5c: fork() = 0 0 389/0xe5c: thread_selfid(0x0, 0x0, 0x0) = 3676 0 389/0xe5c: getpid(0x0, 0x0, 0x0) = 389 0 389/0xe5c: select_nocancel(0x0, 0x0, 0x0) = 0 0 389/0xe5c: setsid(0x0, 0x0, 0x0) = 389 0 389/0xe5c: close_nocancel(0x3) = 0 0 389/0xe5c: dup2(0x4, 0x0, 0x0) = 0 0 389/0xe5c: dup2(0x4, 0x1, 0x0) = 1 0 389/0xe5c: dup2(0x4, 0x2, 0x0) = 2 0 389/0xe5c: getrlimit(0x8, 0xBFFFF46C, 0x0) = 0 0 ... close range ... 389/0xe5c: ioctl(0x1, 0x402C7413, 0xBFFFF410) = 0 0 389/0xe5c: fstat64(0x1, 0xBFFFF3A4, 0xBFFFF410) = 0 0 389/0xe5c: open_nocancel("/dev/\0", 0x100004, 0xBFFFE968) = 3 0 389/0xe5c: fcntl_nocancel(0x3, 0x2, 0x1) = 0 0 389/0xe5c: fstatfs64(0x3, 0xBFFFE8D8, 0x1) = 0 0 389/0xe5c: getdirentries64(0x3, 0x10DFC00, 0x1000) = 3080 0 ... lots of lstat64 ... 389/0xe5c: close_nocancel(0x3) = 0 0 389/0xe5c: open_nocancel("/dev/ttys003\0", 0x20002, 0x0) = 3 0 389/0xe5c: close_nocancel(0x3) = 0 0 389/0xe5c: write_nocancel(0x1, "testing\0", 0x7) = 7 0 389/0xe5c: close_nocancel(0x1) = 0 0 385/0xe53: select_nocancel(0x0, 0x0, 0x0) = 0 0 385/0xe53: close_nocancel(0x4) = 0 0 385/0xe53: read_nocancel(0x3, "\0", 0x400) = 0 0 385/0xe53: write_nocancel(0x1, "b''\n\0", 0x4) = 4 0 385/0xe53: wait4_nocancel(0x185, 0xBFFFF474, 0x0) = 389 0 385/0xe53: sigaction(0x2, 0xBFFFF800, 0xBFFFF838) = 0 0 385/0xe53: madvise(0xDA0000, 0x20000, 0x9) = 0 0 385/0xe53: madvise(0xDC0000, 0x20000, 0x9) = 0 0 385/0xe53: madvise(0xD80000, 0x20000, 0x9) = 0 0 2.7, parent_close_slave_before_child_write = True (WORkS) PID/THRD SYSCALL(args) = return 363/0xcab: open_nocancel("/dev/ptmx\0", 0x20002, 0x0) = 3 0 363/0xcab: ioctl(0x3, 0x20007454, 0xFFFFFFFF) = 0 0 363/0xcab: ioctl(0x3, 0x20007452, 0x0) = 0 0 363/0xcab: ioctl(0x3, 0x40807453, 0x10041AD20) = 0 0 363/0xcab: stat64("/dev/ttys003\0", 0x7FFF5FBFF030, 0x0) = 0 0 363/0xcab: open_nocancel("/dev/ttys003\0", 0x20002, 0x0) = 4 0 363/0xcab: fork() = 368 0 368/0xcb5: fork() = 0 0 368/0xcb5: thread_selfid(0x100420BE0, 0x3, 0x1) = 3253 0 368/0xcb5: getpid(0x100420BE0, 0x3, 0x0) = 368 0 363/0xcab: select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0) = 0 0 363/0xcab: close(0x4) = 0 0 368/0xcb5: select(0x0, 0x0, 0x0, 0x0, 0x7FFF5FBFF3C0) = 0 0 368/0xcb5: setsid(0x0, 0x0, 0x1001A7B40) = 368 0 368/0xcb5: close(0x3) = 0 0 368/0xcb5: dup2(0x4, 0x0, 0x0) = 0 0 368/0xcb5: dup2(0x4, 0x1, 0x0) = 1 0 368/0xcb5: dup2(0x4, 0x2, 0x0) = 2 0 368/0xcb5: getrlimit(0x1008, 0x7FFF5FBFF3D0, 0x0) = 0 0 ... close range ... 368/0xcb5: ioctl(0x1, 0x40487413, 0x7FFF5FBFF360) = 0 0 368/0xcb5: fstat64(0x1, 0x7FFF5FBFF2D0, 0x0) = 0 0 368/0xcb5: open_nocancel("/dev/\0", 0x100004, 0x0) = 3 0 368/0xcb5: fcntl_nocancel(0x3, 0x2, 0x1) = 0 0 368/0xcb5: fstatfs64(0x3, 0x7FFF5FBFE7B0, 0x0) = 0 0 368/0xcb5: getdirentries64(0x3, 0x1010AB600, 0x1000) = 3080 0 ... lots of lstat64 ... 368/0xcb5: close_nocancel(0x3) = 0 0 368/0xcb5: open("/dev/ttys003\0", 0x2, 0x1FF) = 3 0 368/0xcb5: close(0x3) = 0 0 368/0xcb5: write(0x1, "testing\0", 0x7) = 7 0 368/0xcb5: close(0x1) = 0 0 363/0xcab: read(0x3, "testing\0", 0x400) = 7 0 363/0xcab: write_nocancel(0x1, "'testing'\n\0", 0xA) = 10 0 363/0xcab: wait4(0x170, 0x7FFF5FBFF3D4, 0x0) = 368 0 363/0xcab: sigaction(0x2, 0x7FFF5FBFF800, 0x7FFF5FBFF830) = 0 0 3.0, parent_close_slave_before_child_write = True (WORkS) PID/THRD SYSCALL(args) = return 393/0xed3: open_nocancel("/dev/ptmx\0", 0x20002, 0xD15EB8) = 3 0 393/0xed3: ioctl(0x3, 0x20007454, 0xBFFFF1BC) = 0 0 393/0xed3: ioctl(0x3, 0x20007452, 0xBFFFF1BC) = 0 0 393/0xed3: ioctl(0x3, 0x40807453, 0x64F220) = 0 0 393/0xed3: stat64("/dev/ttys003\0", 0xBFFFF164, 0x64F220) = 0 0 393/0xed3: open_nocancel("/dev/ttys003\0", 0x20002, 0x0) = 4 0 393/0xed3: fork() = 399 0 399/0xee3: fork() = 0 0 399/0xee3: thread_selfid(0x0, 0x0, 0x0) = 3811 0 399/0xee3: getpid(0x0, 0x0, 0x0) = 399 0 393/0xed3: select_nocancel(0x0, 0x0, 0x0) = 0 0 393/0xed3: close_nocancel(0x4) = 0 0 399/0xee3: select_nocancel(0x0, 0x0, 0x0) = 0 0 399/0xee3: setsid(0x0, 0x0, 0x0) = 399 0 399/0xee3: close_nocancel(0x3) = 0 0 399/0xee3: dup2(0x4, 0x0, 0x0) = 0 0 399/0xee3: dup2(0x4, 0x1, 0x0) = 1 0 399/0xee3: dup2(0x4, 0x2, 0x0) = 2 0 399/0xee3: getrlimit(0x8, 0xBFFFF46C, 0x0) = 0 0 ... close range ... 399/0xee3: ioctl(0x1, 0x402C7413, 0xBFFFF410) = 0 0 399/0xee3: fstat64(0x1, 0xBFFFF3A4, 0xBFFFF410) = 0 0 399/0xee3: open_nocancel("/dev/\0", 0x100004, 0xBFFFE968) = 3 0 399/0xee3: fcntl_nocancel(0x3, 0x2, 0x1) = 0 0 399/0xee3: fstatfs64(0x3, 0xBFFFE8D8, 0x1) = 0 0 399/0xee3: getdirentries64(0x3, 0x10DFC00, 0x1000) = 3080 0 ... lots of lstat64 ... 399/0xee3: close_nocancel(0x3) = 0 0 399/0xee3: open_nocancel("/dev/ttys003\0", 0x20002, 0x0) = 3 0 399/0xee3: close_nocancel(0x3) = 0 0 399/0xee3: write_nocancel(0x1, "testing\0", 0x7) = 7 0 399/0xee3: close_nocancel(0x1) = 0 0 393/0xed3: read_nocancel(0x3, "testing\0", 0x400) = 7 0 393/0xed3: write_nocancel(0x1, "b'testing'\n\0", 0xB) = 11 0 393/0xed3: wait4_nocancel(0x18F, 0xBFFFF474, 0x0) = 399 0 393/0xed3: sigaction(0x2, 0xBFFFF800, 0xBFFFF838) = 0 0 393/0xed3: madvise(0xDA0000, 0x20000, 0x9) = 0 0 393/0xed3: madvise(0xDC0000, 0x20000, 0x9) = 0 0 393/0xed3: madvise(0xD80000, 0x20000, 0x9) = 0 0 ----------- import os import pty import resource import signal import tty import time import sys PY3 = sys.version_info.major == 3 if PY3: raw_input = input # change this to False for failure on py3 parent_close_slave_before_child_write = True print("pid: %d" % os.getpid()) raw_input("hit enter when 'dtruss -f' is ready> ") send_from_child = "testing123".encode() master, slave = pty.openpty() pid = os.fork() # child process if pid == 0: if parent_close_slave_before_child_write: time.sleep(2) else: time.sleep(1) os.setsid() os.close(master) os.dup2(slave, 0) os.dup2(slave, 1) os.dup2(slave, 2) max_fd = resource.getrlimit(resource.RLIMIT_NOFILE)[0] os.closerange(3, max_fd) # make controlling terminal. taken from pty.fork tmp_fd = os.open(os.ttyname(1), os.O_RDWR) os.close(tmp_fd) os.write(1, send_from_child) os.close(1) os._exit(255) # parent process else: if parent_close_slave_before_child_write: time.sleep(1) else: time.sleep(2) os.close(slave) actual_output = os.read(master, 1024) os.waitpid(pid, 0) assert(actual_output == send_from_child) print("correct output") ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <http://bugs.python.org/issue15898> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com