serve: add and use portable spawnvp replacement
There is no standard python command to really detach a process under Windows.
Instead we use the low level API wrapped by subprocess module with all
necessary options to avoid any kind of context inheritance. Unfortunately, this
version still opens a new window for the child process.
The following have been tried:
- os.spawnv(os.P_NOWAIT): works but the child process is killed when parent
console terminates.
- os.spawnv(os.P_DETACH): works on python25, hang on python26 when writing to
the hgweb output socket.
- subprocess.CreateProcess() hack without shell mode: similar to
os.spawnv(os.P_DETACH).
Fix 1/3 for issue421
--- a/mercurial/cmdutil.py Tue Jan 12 15:12:53 2010 +0100
+++ b/mercurial/cmdutil.py Fri Apr 10 21:20:25 2009 +0200
@@ -580,8 +580,7 @@
elif runargs[i].startswith('--cwd'):
del runargs[i:i+2]
break
- pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
- runargs[0], runargs)
+ pid = util.spawndetached(runargs)
os.close(wfd)
os.read(rfd, 1)
if parentfn:
--- a/mercurial/posix.py Tue Jan 12 15:12:53 2010 +0100
+++ b/mercurial/posix.py Fri Apr 10 21:20:25 2009 +0200
@@ -257,3 +257,8 @@
return grp.getgrgid(gid)[0]
except KeyError:
return str(gid)
+
+def spawndetached(args):
+ return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
+ args[0], args)
+
--- a/mercurial/windows.py Tue Jan 12 15:12:53 2010 +0100
+++ b/mercurial/windows.py Fri Apr 10 21:20:25 2009 +0200
@@ -7,7 +7,7 @@
from i18n import _
import osutil, error
-import errno, msvcrt, os, re, sys, random
+import errno, msvcrt, os, re, sys, random, subprocess
nulldev = 'NUL:'
umask = 002
@@ -321,6 +321,37 @@
pass
os.rename(src, dst)
+def spawndetached(args):
+ # No standard library function really spawns a fully detached
+ # process under win32 because they allocate pipes or other objects
+ # to handle standard streams communications. Passing these objects
+ # to the child process requires handle inheritance to be enabled
+ # which makes really detached processes impossible.
+ class STARTUPINFO:
+ dwFlags = subprocess.STARTF_USESHOWWINDOW
+ hStdInput = None
+ hStdOutput = None
+ hStdError = None
+ wShowWindow = subprocess.SW_HIDE
+
+ args = subprocess.list2cmdline(args)
+ # Not running the command in shell mode makes python26 hang when
+ # writing to hgweb output socket.
+ comspec = os.environ.get("COMSPEC", "cmd.exe")
+ args = comspec + " /c " + args
+ hp, ht, pid, tid = subprocess.CreateProcess(
+ None, args,
+ # no special security
+ None, None,
+ # Do not inherit handles
+ 0,
+ # DETACHED_PROCESS
+ 0x00000008,
+ os.environ,
+ os.getcwd(),
+ STARTUPINFO())
+ return pid
+
try:
# override functions with win32 versions if possible
from win32 import *