Skip to content

termios.tcdrain hangs on MacOS #97001

Closed
Closed
@benthorner

Description

@benthorner

Bug report

The following Python program hangs forever on MacOS, but does not hang on Linux:

import os, termios, time
device_fd, tty_fd  = os.openpty()

def loop():
    while True:
        data = os.read(device_fd, 1)
        print(data)  # <--- never gets here
        time.sleep(.1)

from threading import Thread
thread = Thread(target=loop, daemon=True)
thread.start()

os.write(tty_fd, b'123')
termios.tcdrain(tty_fd)  # <--- gets stuck here
time.sleep(3)  # allow thread to read all data

Reading the data in a thread should mean there's absolutely no reason for tcdrain to hang. The docs say it only waits for the data to be "transmitted", which sounds different to "read". And there's the fact this works on Linux.

Any single-threaded call to tcdrain() using tty_fd also hangs, even if the os.write line is removed i.e. there's nothing for tcdrain to wait for write. When I try with a real file descriptor or a pipe I get an error: 'Inappropriate ioctl for device' - it's unclear if it's the combination of tcdrain and openpty or just tcdrain that's causing the issue.

When I sample the process using the MacOS Activity Monitor I get the following at the bottom of the call graph for the main thread (unchanging) and the secondary "read" thread (bottommost lines vary per sample) respectively:

...
+ 2553 termios_tcdrain  (in termios.cpython-39-darwin.so) + 56  [0x1048dedcc]
+   2553 tcdrain  (in libsystem_c.dylib) + 48  [0x19f27c454]
+     2553 ioctl  (in libsystem_kernel.dylib) + 36  [0x19f30b0c0]
+       2553 __ioctl  (in libsystem_kernel.dylib) + 8  [0x19f30b0d4]
...
  853 os_read  (in python3.9) + 320  [0x100d45ad8]
    853 _Py_read  (in python3.9) + 92  [0x100d35ca0]
      853 PyEval_RestoreThread  (in python3.9) + 24  [0x100cd2c7c]
        853 take_gil  (in python3.9) + 176  [0x100cd2550]
          852 _pthread_cond_wait  (in libsystem_pthread.dylib) + 1236  [0x19f34483c]
            ! 852 __psynch_cvwait  (in libsystem_kernel.dylib) + 8  [0x19f30a270]
              1 _pthread_cond_wait  (in libsystem_pthread.dylib) + 344  [0x19f3444c0]
                1 __gettimeofday  (in libsystem_kernel.dylib) + 12  [0x19f30aa0c]

The questions I'm struggling to answer:

  • What is the expected behaviour of tcdrain in this scenario?
  • If the Linux behaviour is expected, why does it not work on MacOS?

Your environment

  • CPython versions tested on: 3.9.13
  • Operating system and architecture: MacOS 12.5.1 (ARM CPU).

I've also tested on MacOS with an Intel CPU and another developer has tested on Linux (to prove it terminates there). Originally I thought the issue might be an OS issue and posted in the Apple dev forum about it.

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-mactype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions