Skip to content

Windows: SystemError during os.kill(..., signal.CTRL_C_EVENT) #87128

Closed

Description

@wkschwartz
BPO 42962
Nosy @terryjreedy, @pfmoore, @ncoghlan, @tjguk, @zware, @wkschwartz, @eryksun, @zooba
Superseder
  • bpo-14484: missing return in win32_kill?
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2021-12-11.21:00:33.304>
    created_at = <Date 2021-01-18.22:12:34.571>
    labels = ['type-bug', '3.8', '3.9', '3.10', 'library', 'OS-windows']
    title = 'Windows: SystemError during os.kill(..., signal.CTRL_C_EVENT)'
    updated_at = <Date 2021-12-11.21:00:33.302>
    user = 'https://github.com/wkschwartz'

    bugs.python.org fields:

    activity = <Date 2021-12-11.21:00:33.302>
    actor = 'eryksun'
    assignee = 'none'
    closed = True
    closed_date = <Date 2021-12-11.21:00:33.304>
    closer = 'eryksun'
    components = ['Library (Lib)', 'Windows']
    creation = <Date 2021-01-18.22:12:34.571>
    creator = 'William.Schwartz'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 42962
    keywords = []
    message_count = 14.0
    messages = ['385235', '385238', '385239', '385248', '385277', '385291', '385296', '385305', '385551', '385552', '387732', '387734', '387738', '387739']
    nosy_count = 9.0
    nosy_names = ['terry.reedy', 'paul.moore', 'ncoghlan', 'tim.golden', 'zach.ware', 'William.Schwartz', 'eryksun', 'steve.dower', 'elsamuko']
    pr_nums = []
    priority = 'normal'
    resolution = 'duplicate'
    stage = 'resolved'
    status = 'closed'
    superseder = '14484'
    type = 'behavior'
    url = 'https://bugs.python.org/issue42962'
    versions = ['Python 3.8', 'Python 3.9', 'Python 3.10']

    Activity

    1. wkschwartz commented on Jan 18, 2021

      wkschwartzmannequin
      MannequinAuthor

      I don't have an automated test at this time, but here's how to get os.kill to raise SystemError. I did this on Windows 10 version 20H2 (build 19042.746) with Pythons 3.7.7, 3.8.5, and 3.9.1. os_kill_impl at Modules/posixmodule.c:7833 does not appear to have been modified recently (but I haven't checked its transitive callees), so I imagine you can get the same behavior from os.kill on Python 3.10.

      1. Open two consoles, A and B. (I'm using tabs in Windows Terminal, if that matters.)
      2. In console A, type but DO NOT EXECUTE the following command:

      python -c"import os, signal; os.kill(, signal.CTRL_C_EVENT)"

      Move your cursor back before the comma.

      1. In console B, create a process that does nothing but print its process identifier and wait long enough for you to type it in console A:

      python -c"import os, time; print(os.getpid()); time.sleep(60); print('exiting cleanly')"

      Copy or remember the printed PID. Hurry to step 4 before your 60 seconds expires!

      1. In console A, type the PID from console B and execute the command.
      2. In console B, confirm that the Python exited without printing "exiting cleanly". Oddly, echo %errorlevel% will print 0 rather than -1073741510 (which is 2**32 - 0xc000013a, the CTRL_C_EVENT exit code), which is what it prints after python -c"raise KeyboardInturrupt".
      3. In console A, you should see the following traceback.

      OSError: [WinError 87] The parameter is incorrect

      The above exception was the direct cause of the following exception:
      
      Traceback (most recent call last):
        File "<string>", line 1, in <module>
      SystemError: <built-in function kill> returned a result with an error set
    2. 8 remaining items

    3. wkschwartz commented on Jan 19, 2021

      wkschwartzmannequin
      MannequinAuthor

      For a new process group, the cancel event is initially ignored, but the break event is always handled. To enable the cancel event, the process must call SetConsoleCtrlHandler(NULL, FALSE), such as via ctypes with kernel32.SetConsoleCtrlHandler(None, False). I think the signal module should provide a function to enable/disable Ctrl+C handling without ctypes, and implicitly enable it when setting a new SIGINT handler.

      That's what Lib/test/win_console_handler.py:39 does. What I don't understand is why that's necessary. Isn't that what PyConfig.install_signal_handlers is supposed to do?

      Which brings me to how I ended up here in the first place: I wanted to write a test that PyConfig.install_signal_handlers is set in an embedded instance of Python I'm working with. In outline, the following test works on both Windows and macOS *except on Windows running under Tox*.

      @unittest.removeHandler
      def test_signal_handlers_installed(self):
          SIG = signal.SIGINT
          if sys.platform == 'win32':
              SIG = signal.CTRL_C_EVENT
          with self.assertRaises(KeyboardInterrupt):
              os.kill(os.getpid(), SIG)
              if sys.platform == 'win32':
                  time.sleep(.1)  # Give handler's thread time to join

      Using SetConsoleCtrlHandler if I detect that I'm running on Windows under Tox would, if I understand correctly, hide whether PyConfig.install_signal_handlers was set before the Python I'm running in started, right? (I know this isn't the right venue for discussing my embedding/testing problem. But maybe the use case informs the pending discussion of what to do about os.kill's semantics.)

    4. eryksun commented on Jan 19, 2021

      @eryksun
      Contributor

      That's what Lib/test/win_console_handler.py:39 does.

      No, that script is currently broken.

      SetConsoleCtrlHandler(None, False) clears the Ctrl+C ignore flag in the PEB ProcessParameters, which is normally inherited by child processes. But using the CREATE_NEW_PROCESS_GROUP creation flag always causes Ctrl+C to be initially ignored in the process. The intent is to make it a 'background' process by default, to support CMD's start /b option.

      Win32KillTests in Lib/test/test_os.py incorrectly assumes it only has to enable Ctrl+C in the parent process. win_console_handler.py incorrectly assumes it does not have to call SetConsoleCtrlHandler(None, False). This is why test_CTRL_C_EVENT() is currently being skipped. To fix it, win_console_handler.py needs to call SetConsoleCtrlHandler(None, False).

      Isn't that what PyConfig.install_signal_handlers is supposed to do?

      The C runtime's signal() function does not clear the Ctrl+C ignore flag when a SIGINT handler is set. I think Python should. It's expected behavior for POSIX compatibility.

      with self.assertRaises(KeyboardInterrupt):
      os.kill(os.getpid(), SIG)

      Unless you know the current process was created in a new process group, it is not correct to call os.kill(os.getpid(), signal.CTRL_C_EVENT). You'd be better off using os.kill(0, signal.CTRL_C_EVENT) and ensuring that Ctrl+C is temporarily ignored in the parent process, if that's in your control. Also, Python 3.8 added raise_signal(), which calls C raise(), so a more reliable way to test just the signal handler is to call signal.raise_signal(signal.SIGINT). This also eliminates needing to synchronize with the console control thread.

    5. terryjreedy commented on Jan 23, 2021

      @terryjreedy
      Member

      If I understand correctly, this is open for someone to evaluate Eryk's suggestion in msg385291 that a 'missing' return be added to win32_kill.

    6. eryksun commented on Jan 23, 2021

      @eryksun
      Contributor

      Thanks, Terry. I should have followed up with a short message asking for a core developer to sign off on that suggestion, in which case this is an "easy (C)" issue.

    7. elsamuko commented on Feb 26, 2021

      elsamukomannequin
      Mannequin

      FWIW, I could send CTRL-C to ffmpeg (else it won't write a valid mp4 header) under Windows by using the WINPID and not the PID returned from ps .

      WINPID=$(ps aux | grep ffmpeg | awk '{print $4}')
      python -c "import os, signal; os.kill($WINPID, signal.CTRL_C_EVENT)"
      

      I put an example here:
      https://gist.github.com/elsamuko/9c3fe69f00a0f847251ffa3ef1d080a2

    8. eryksun commented on Feb 26, 2021

      @eryksun
      Contributor

      El Samuko, is that a Windows (Win32), Cygwin, MSYS2, WSL, or some other version of bash? The same for Python, and which version of Python is it. With these details, I can tell you what os.kill($WINPID, signal.CTRL_C_EVENT)" is probably doing.

    9. elsamuko commented on Feb 26, 2021

      elsamukomannequin
      Mannequin

      @eryksun

      I can run the script successfully under cygwin and mingw(git bash).
      The python version is 3.9.2

    10. elsamuko commented on Feb 26, 2021

      elsamukomannequin
      Mannequin

      I'm getting [WinError 87], too, when using the PID.

    11. transferred this issue fromon Apr 10, 2022
    12. davetapley commented on Jan 20, 2023

      @davetapley

      @eryksun do you remember how/why this was closed?
      I just ran in to it on 3.10.9.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.10 (EOL)end of life3.8 (EOL)end of life3.9 (EOL)end of lifeOS-windowsstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions