So, ehm, I've had to learn a lot about process management in the last two days, most importantly that java is *really* bad at doing it properly. If you open
pygump/python/gump/plugins/java/builder.py and edit the AntBuilderPlugin to have no_cleanup=False instead of no_cleanup=True, then do a gump run using the vmgump.xml profile, the run will usually stall trying to invoke java_cup. The reason for this seems to be that java sometimes deadlocks when forked from java. I built a "trivial" testcase (basically rewrote the Execute.java from ant to manually run my demo program and wrote a simple python wrapper to fire that up) and the problem does not occur there, so I suspect (after stepping through both python and java debuggers for a whole lot) that something like multi-threading or garbage collection is in some way significant. To be clear, this isn't a bug in gump or a bug in ant, but a bug in the JDK in interaction with a very specific environment. It'll be interesting to see if, for instance, the same mess doesn't occur when using Kaffe. I suspect that using any JVM for which Ant's Execute takes a different approach (ie not using Runtime.exec) makes the problem "go away". We'll have to see if this becomes a problem or not (eg zombie processes). I'll try hard to, if we run into issues, produce big and scary stack traces. My hunch is that we'd have to implement a work around in Ant...I doubt sun is going to fix their jdk... cheers, Leo [EMAIL PROTECTED] wrote: > * disable process group management for running ant. See inside > gump.plugins.java.builder.AntPlugin for some details. This was a *huge* > pain to figure out. What triggered this is the invocation of java_cup > from the xalan build.xml file, which has a <java fork="true". ... > <project name="xalan"> > - <module name="xml"/> > + <module name="xml-xalan"/> > > <!-- commands --> > <ant basedir="java" target="unbundledjar"> > @@ -252,6 +263,8 @@ > </project> ... > + def _do_run_command(self, command, args, workdir, shell=False, > no_cleanup=False): > + # see gump.plugins.java.builder.AntPlugin for information on the > + # no_cleanup flag > + ... > - cmd = > Popen(myargs,shell=False,cwd=workdir,stdout=outputfile,stderr=STDOUT,env=command.env) > + cmd = > Popen(myargs,shell=False,cwd=workdir,stdout=outputfile,stderr=STDOUT,env=command.env, > no_cleanup=no_cleanup) ... > - command.build_log = outputfile.read() > + # we need to avoid Unicode errors when people put in 'fancy > characters' > + # into build outputs > + command.build_log = unicode(outputfile.read(), 'iso-8859-1') > + import tempfile ... > - def _get_new_process_group(): > - """Get us an unused (or so we hope) process group.""" > - pid = os.fork() > - gid = pid # that *should* be correct. However, let's actually > - # create something in that group. > - if pid == 0: > - # Child > - > - # ensure a process group is created > - os.setpgrp() > - > - # sleep for ten days to keep the process group around > - # for "a while" > - import time > - time.sleep(10*24*60*60) > - os._exit(0) > - else: > - # Parent > - > - # wait for child a little so it can set its group > - import time > - time.sleep(1) > - > - # get the gid for the child > - gid = os.getpgid(pid) > - > - return gid > - > - # This is the group we chuck our children in. We don't just want to > - # use our own group since we don't want to kill ourselves prematurely! > - _our_process_group = _get_new_process_group() > + temp_dir = tempfile.mkdtemp("gump_util_executor") > + process_list_filename = os.path.join(temp_dir, "processlist.pids") > > + def savepgid(filename): > + """Function called from Popen child process to create new process > groups.""" > + os.setpgrp() > + f = None > + try: > + grp = os.getpgrp() > + f = open(filename,'a+') > + f.write("%d" % grp) > + f.write('\n') > + finally: > + if f: > + try: f.close() > + except: pass > + > class Popen(subprocess.Popen): > """This is a thin wrapper around subprocess.Popen which handles > process group management. The gump.util.executor.clean_up_processes() > @@ -106,35 +109,67 @@ > stdin=None, stdout=None, stderr=None, > preexec_fn=None, close_fds=False, shell=False, > cwd=None, env=None, universal_newlines=False, > - startupinfo=None, creationflags=0): > - """Create a new Popen instance that delegates to the > - subprocess Popen.""" > - if not preexec_fn: > - # setpgid to the gump process group inside the child > - pre_exec_function = lambda: os.setpgid(0, _our_process_group) > - else: > - # The below has a "stupid lambda trick" that makes the lambda > - # evaluate a tuple of functions. This sticks our own function > - # call in there while still supporting the originally > provided > - # function > - pre_exec_function = lambda: (preexec_fn(),os.setpgid(0, > _our_process_group)) > - > + startupinfo=None, creationflags=0, no_cleanup=False): > + # see gump.plugins.java.builder.AntPlugin for information on the > + # no_cleanup flag > + > # a logger can be set for this module to make us log commands > if _log: > _log.info(" Executing command:\n %s'%s'%s\n > in directory '%s'" % (ansicolor.Blue, " ".join(args), ansicolor.Black, > os.path.abspath(cwd or os.curdir))) > - > - subprocess.Popen.__init__(self, args, bufsize=bufsize, > executable=executable, > - stdin=stdin, stdout=stdout, stderr=stderr, > - # note our custom function in there... > - preexec_fn=pre_exec_function, close_fds=close_fds, > shell=shell, > - cwd=cwd, env=env, universal_newlines=universal_newlines, > - startupinfo=startupinfo, creationflags=creationflags) > + > + if not no_cleanup: > + global process_list_filename > + """Create a new Popen instance that delegates to the > + subprocess Popen.""" > + if not preexec_fn: > + # setpgid to the gump process group inside the child > + pre_exec_function = lambda: > savepgid(process_list_filename) > + else: > + # The below has a "stupid lambda trick" that makes the > lambda > + # evaluate a tuple of functions. This sticks our own > function > + # call in there while still supporting the originally > provided > + # function > + pre_exec_function = lambda: > (preexec_fn(),savepgid(process_list_filename)) > + > + > + subprocess.Popen.__init__(self, args, bufsize=bufsize, > executable=executable, > + stdin=stdin, stdout=stdout, stderr=stderr, > + # note our custom function in there... > + preexec_fn=pre_exec_function, close_fds=close_fds, > shell=shell, > + cwd=cwd, env=env, > universal_newlines=universal_newlines, > + startupinfo=startupinfo, > creationflags=creationflags) > + else: > + subprocess.Popen.__init__(self, args, bufsize=bufsize, > executable=executable, > + stdin=stdin, stdout=stdout, stderr=stderr, > + # note our custom function is *not* in there... > + preexec_fn=preexec_fn, close_fds=close_fds, > shell=shell, > + cwd=cwd, env=env, > universal_newlines=universal_newlines, > + startupinfo=startupinfo, > creationflags=creationflags) > + > > def clean_up_processes(timeout=300): > """This function can be called prior to program exit to attempt to > kill all our running children that were created using this module.""" > > - pgrp_list = [_our_process_group] > + global process_list_filename > + global temp_dir > + > + pgrp_list = [] > + > + f = None > + try: > + f = open(process_list_filename, 'r') > + pgrp_list = [int(line) for line in f.read().splitlines()] > + except: > + if f: > + try: f.close() > + except: pass > + try: > + import shutil > + shutil.rmtree(temp_dir) > + except: > + pass > + --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]