[PATCH 1 of 3] killdaemons: fix error handling on Windows

Matt Harbison mharbison72 at gmail.com
Mon May 15 04:08:30 UTC 2017


# HG changeset patch
# User Matt Harbison <matt_harbison at yahoo.com>
# Date 1494779534 14400
#      Sun May 14 12:32:14 2017 -0400
# Node ID 024271e90987e5794dab6eb00844467065fae7e4
# Parent  db0b470547d5e21f042390a204f09ae7d7303757
killdaemons: fix error handling on Windows

After taking Adrian's suggestion[1] to use util.posixfile to avoid os.unlink()
errors, the error changed from:

  Errored test-hgweb-json.t: Traceback (most recent call last):
    File "./run-tests.py", line 724, in run
      self.tearDown()
    File "./run-tests.py", line 805, in tearDown
      killdaemons(entry)
    File "./run-tests.py", line 540, in killdaemons
      logfn=vlog)
    File "...\tests\killdaemons.py", line 94, in killdaemons
      os.unlink(pidfile)
  WindowsError: [Error 32] The process cannot access the file because it is
     being used by another process: '...\\hgtests.zmpqj3\\child80\\daemon.pids'

to:

  Errored test-largefiles.t: Traceback (most recent call last):
    File "./run-tests.py", line 724, in run
      self.tearDown()
    File "./run-tests.py", line 805, in tearDown
      killdaemons(entry)
    File "./run-tests.py", line 540, in killdaemons
      logfn=vlog)
    File "...\tests\killdaemons.py", line 91, in killdaemons
      kill(pid, logfn, tryhard)
    File "...\tests\killdaemons.py", line 55, in kill
      _check(ctypes.windll.kernel32.CloseHandle(handle))
    File "...\tests\killdaemons.py", line 18, in _check
      raise ctypes.WinError(winerrno)
  WindowsError: [Error 6] The handle is invalid.

The handle in question is for the process, not the file.  That made me wonder
why WaitForSingleObject() didn't raise an error, and it's because it isn't a
BOOL return.  (For this method, a return of 0 is WAIT_OBJECT_0, and an error is
signalled by returning (DWORD) 0xFFFFFFFF.)  When I printed out that value (as
returned by the function) with '0x%x', it printed '0x-1'.  There are some bugs
around DWORDs in this range[2], and since the code explictly tests for errors it
wants to handle, it seems safer to test for 'ret != TRUE', to avoid silent
breakage in the future if this bug is fixed.  The only case not handled is
WAIT_ABANDONED, which will now raise.  ctypes.windll.kernel32.GetLastError has a
'c_long' in its restype field, instead of 'c_ulong' for some reason.

With that fixed, WaitForSingleObject() started raising an error with the same
message, meaning OpenProcess() was returning an invalid handle.  Since HANDLE is
a pointer, the proper check is for None, not 0.  With this fixed, I confirmed
with a print statement that the err 87 case now properly bails out.

[1] https://www.mercurial-scm.org/pipermail/mercurial-devel/2017-May/097600.html
[2] https://bugs.python.org/issue28474

diff --git a/tests/killdaemons.py b/tests/killdaemons.py
--- a/tests/killdaemons.py
+++ b/tests/killdaemons.py
@@ -11,7 +11,10 @@
     import ctypes
 
     def _check(ret, expectederr=None):
-        if ret == 0:
+        # 0 is typically a failure, but NOT for WaitForSingleObject().  It is
+        # expected that the cases which can be handled for WaitForSingleObject()
+        # will be before calling this.
+        if ret != 1:
             winerrno = ctypes.GetLastError()
             if winerrno == expectederr:
                 return True
@@ -27,7 +30,7 @@
         handle = ctypes.windll.kernel32.OpenProcess(
                 PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
                 False, pid)
-        if handle == 0:
+        if handle is None:
             _check(0, 87) # err 87 when process not found
             return # process not found, already finished
         try:


More information about the Mercurial-devel mailing list