On Wed, 28 Mar 2018 22:45:30 -0400, Matt Harbison wrote: > # HG changeset patch > # User Matt Harbison <matt_harbi...@yahoo.com> > # Date 1522210269 14400 > # Wed Mar 28 00:11:09 2018 -0400 > # Node ID 7a6eccc3b43dd8f6090b23aa38b2f71d33f5bbdc > # Parent 8bac14ce57781d2a45b6d036b3614b1a21917f0d > server: add an error feedback mechanism for when the daemon fails to launch > > There's a recurring problem on Windows where `hg serve -d` will randomly fail > to > spawn a detached process. The reason for the failure is completely hidden, > and > it takes hours to get a single failure on my laptop. All this does is > redirect > stdout/stderr of the child to a file until the lock file is freed, and then > the > parent dumps it out if it fails to spawn.
Abusing the lockfile is fine, but ... > diff --git a/mercurial/server.py b/mercurial/server.py > --- a/mercurial/server.py > +++ b/mercurial/server.py > @@ -30,6 +30,20 @@ def runservice(opts, parentfn=None, init > runargs=None, appendpid=False): > '''Run a command as a service.''' > > + # When daemonized, redirect stdout/stderr to the lockfile (which gets > + # cleaned up after the child is up and running), so that the parent can > read > + # and print the error if this child dies early. > + if opts['daemon_postexec']: > + for inst in opts['daemon_postexec']: > + if inst.startswith('unlink:'): > + lockpath = inst[7:] > + procutil.stdout.flush() > + procutil.stderr.flush() > + logfilefp = open(lockpath, 'w+') Nit: os.open(lockpath, os.O_WRONLY | os.O_APPEND | os.O_BINARY) is probably better as it enforces that the lock was created by the parent process. > + os.dup2(logfilefp.fileno(), 1) > + os.dup2(logfilefp.fileno(), 2) > + logfilefp.close() > @@ -47,6 +61,8 @@ def runservice(opts, parentfn=None, init > try: > if not runargs: > runargs = procutil.hgcmd() + pycompat.sysargv[1:] > + # Only hg processes accept --traceback (e.g., not > dumbhttp.py) > + runargs.append('--traceback') Doesn't it enable the traceback permanently? The --traceback option should be specified explicitly only when needed. > runargs.append('--daemon-postexec=unlink:%s' % lockpath) > # Don't pass --cwd to the child process, because we've already > # changed directory. > @@ -61,6 +77,12 @@ def runservice(opts, parentfn=None, init > return not os.path.exists(lockpath) > pid = procutil.rundetached(runargs, condfn) > if pid < 0: > + # If the daemonized process managed to write out an error > msg, > + # report it. > + if os.path.exists(lockpath): > + with open(lockpath) as log: > + for line in log: > + procutil.stderr.write('server: %s' % line) Event without this hack, stdout/stderr of the child should be visible (at least on Unix.) They aren't redirected until the server starts successfully. > - procutil.hidewindow() > - procutil.stdout.flush() > - procutil.stderr.flush() > + > + if lockpath: > + procutil.hidewindow() > + procutil.stdout.flush() > + procutil.stderr.flush() > > - nullfd = os.open(os.devnull, os.O_RDWR) > - logfilefd = nullfd > - if logfile: > - logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | > os.O_APPEND, > - 0o666) > - os.dup2(nullfd, 0) > - os.dup2(logfilefd, 1) > - os.dup2(logfilefd, 2) > - if nullfd not in (0, 1, 2): > - os.close(nullfd) > - if logfile and logfilefd not in (0, 1, 2): > - os.close(logfilefd) > + nullfd = os.open(os.devnull, os.O_RDWR) > + logfilefd = nullfd > + if logfile: > + logfilefd = os.open(logfile, > + os.O_RDWR | os.O_CREAT | os.O_APPEND, > + 0o666) > + os.dup2(nullfd, 0) > + os.dup2(logfilefd, 1) > + os.dup2(logfilefd, 2) > + if nullfd not in (0, 1, 2): > + os.close(nullfd) > + if logfile and logfilefd not in (0, 1, 2): > + os.close(logfilefd) > + > + # Only unlink after redirecting stdout/stderr, so Windows doesn't > + # complain about a sharing violation. > + os.unlink(lockpath) Wheter lockpath is specified or not, stdout/stderr of the child must be redirected to /dev/null. _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel