tests/killdaemons.py
author Matt Harbison <matt_harbison@yahoo.com>
Mon, 01 Jun 2015 14:42:55 -0400
branchstable
changeset 25396 78e8890cfb4b
parent 21194 476069509e72
child 25031 0adc22a0b6b3
permissions -rwxr-xr-x
hgwebdir: avoid redundant repo and directory entries when 'web.name' is set Previously, when 'web.name' was set on a subrepo parent and 'web.collapse=True', the parent repo would show in the list with the configured 'web.name', and a directory with the parent repo's filesystem name (with a trailing slash) would also appear. The subrepo(s) would unexpectedly be excluded from the list of repositories. Clicking the directory entry would go right to the repo page. Now both the parent and the subrepos show up, without the additional directory entry. The configured hgweb paths used '**' for finding the repos in this scenario. A couple of notes about the tests: - The area where the subrepo was added has a comment that it tests subrepos, though none previously existed there. One now does. - The 'web.descend' option is required for collapse to work. I'm not sure what the previous expectations were for the test. Nothing changed with it set, prior to adding the code in this patch. It is however required for this test. - The only output changes are for the hyperlinks, obviously because of the 'web.name' parameter. - Without this code change, there would be an additional diff: --- /usr/local/mercurial/tests/test-hgwebdir.t +++ /usr/local/mercurial/tests/test-hgwebdir.t.err @@ -951,7 +951,7 @@ /rcoll/notrepo/e/ /rcoll/notrepo/e/e2/ /rcoll/notrepo/f/ - /rcoll/notrepo/f/f2/ + /rcoll/notrepo/f/ Test repositories inside intermediate directories I'm not sure why the fancy name doesn't come out, but it is enough to demonstrate that the parent is not listed redundantly, and the subrepo isn't skipped.

#!/usr/bin/env python

import os, sys, time, errno, signal

if os.name =='nt':
    import ctypes

    def _check(ret, expectederr=None):
        if ret == 0:
            winerrno = ctypes.GetLastError()
            if winerrno == expectederr:
                return True
            raise ctypes.WinError(winerrno)

    def kill(pid, logfn, tryhard=True):
        logfn('# Killing daemon process %d' % pid)
        PROCESS_TERMINATE = 1
        PROCESS_QUERY_INFORMATION = 0x400
        SYNCHRONIZE = 0x00100000
        WAIT_OBJECT_0 = 0
        WAIT_TIMEOUT = 258
        handle = ctypes.windll.kernel32.OpenProcess(
                PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
                False, pid)
        if handle == 0:
            _check(0, 87) # err 87 when process not found
            return # process not found, already finished
        try:
            r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
            if r == WAIT_OBJECT_0:
                pass # terminated, but process handle still available
            elif r == WAIT_TIMEOUT:
                _check(ctypes.windll.kernel32.TerminateProcess(handle, -1))
            else:
                _check(r)

            # TODO?: forcefully kill when timeout
            #        and ?shorter waiting time? when tryhard==True
            r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
                                                       # timeout = 100 ms
            if r == WAIT_OBJECT_0:
                pass # process is terminated
            elif r == WAIT_TIMEOUT:
                logfn('# Daemon process %d is stuck')
            else:
                _check(r) # any error
        except: #re-raises
            ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error
            raise
        _check(ctypes.windll.kernel32.CloseHandle(handle))

else:
    def kill(pid, logfn, tryhard=True):
        try:
            os.kill(pid, 0)
            logfn('# Killing daemon process %d' % pid)
            os.kill(pid, signal.SIGTERM)
            if tryhard:
                for i in range(10):
                    time.sleep(0.05)
                    os.kill(pid, 0)
            else:
                time.sleep(0.1)
                os.kill(pid, 0)
            logfn('# Daemon process %d is stuck - really killing it' % pid)
            os.kill(pid, signal.SIGKILL)
        except OSError, err:
            if err.errno != errno.ESRCH:
                raise

def killdaemons(pidfile, tryhard=True, remove=False, logfn=None):
    if not logfn:
        logfn = lambda s: s
    # Kill off any leftover daemon processes
    try:
        fp = open(pidfile)
        for line in fp:
            try:
                pid = int(line)
            except ValueError:
                continue
            kill(pid, logfn, tryhard)
        fp.close()
        if remove:
            os.unlink(pidfile)
    except IOError:
        pass

if __name__ == '__main__':
    path, = sys.argv[1:]
    killdaemons(path)