This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients Akos Kiss, davin, eryksun, paul.moore, pitrou, steve.dower, tim.golden, zach.ware
Date 2021-03-21.11:00:59
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1616324460.26.0.965592601291.issue31863@roundup.psfhosted.org>
In-reply-to
Content
> I'm not actually sure what the proposal here is. Are we suggesting 
> that all Python's means of terminating a process should use the 
> same exit code?

That's how I read it, but I don't agree if it means always pretending that a process was killed by a SIGTERM POSIX signal. That's fighting an uphill battle against the platform and its conventions.

I singled out the case where multiprocessing tries to pretend that Windows has Unix signals. It documents that `exitcode` will be negative if terminated by a signal. It doesn't qualify that this is only in Unix.

    exitcode
        The child’s exit code. This will be None if the process has not
        yet terminated. A negative value -N indicates that the child
        was terminated by signal N.

A negative exit code in Windows (i.e. 0x8000_0000 and above) has many possible meanings. Generally it indicates an abnormal error of some sort that terminated the process, and generally the code is an NTSTATUS or HRESULT value, such as STATUS_CONTROL_C_EXIT or E_FAIL (0x8000_4005). However, there isn't a convention to use an NTSTATUS or HRESULT error code when manually terminating a process. Instead, the convention is to use EXIT_FAILURE (1).

multiprocessing pretends that terminate() is implemented with a SIGTERM signal. To do so, it terminates with an exit code that's just above the system 16-bit range:

    TERMINATE = 0x10000

    def terminate(self):
        if self.returncode is None:
            try:
                _winapi.TerminateProcess(int(self._handle), TERMINATE)
            except OSError:
                if self.wait(timeout=1.0) is None:
                    raise

and maps this code to -signal.SIGTERM:

    def wait(self, timeout=None):
        if self.returncode is None:
            if timeout is None:
                msecs = _winapi.INFINITE
            else:
                msecs = max(0, int(timeout * 1000 + 0.5))

            res = _winapi.WaitForSingleObject(int(self._handle), msecs)
            if res == _winapi.WAIT_OBJECT_0:
                code = _winapi.GetExitCodeProcess(self._handle)
                if code == TERMINATE:
                    code = -signal.SIGTERM
                self.returncode = code

        return self.returncode

I don't know why it doesn't simply use -SIGTERM instead of involving the intermediate error code. 

It's apparently trying to close a loop for scripts that call terminate() and look for this case, or for logging, but the platform convention in Windows doesn't allow distinguishing when a process was forcibly terminated. We don't know whether an exit status of 1 means the process failed internally or was forcibly terminated by another tool. So we can't rely on the exit code in Windows in the same way that it can be relied on in POSIX. All we know is that the process failed. Terminating with an exit code of 15 or -15 in some cases does nothing to solve the problem in general.
History
Date User Action Args
2021-03-21 11:01:00eryksunsetrecipients: + eryksun, paul.moore, pitrou, tim.golden, zach.ware, steve.dower, davin, Akos Kiss
2021-03-21 11:01:00eryksunsetmessageid: <1616324460.26.0.965592601291.issue31863@roundup.psfhosted.org>
2021-03-21 11:01:00eryksunlinkissue31863 messages
2021-03-21 11:00:59eryksuncreate