mirror of
https://github.com/LibreELEC/LibreELEC.tv.git
synced 2025-07-29 13:46:49 +00:00
scripts/pkgbuilder.py: assign each subprocess a process group
When pkgbuilder.py is terminated with SIGINT (ie. ctrl-c), or exits immediately due to a failed job, it is sometimes possible for child subprocesses (ie. build tasks) to remain alive and continue running in the background. To fix this, assign each subprocess a new process group identifier, and capture the pid of each child subprocess so that on shutdown we can kill the entire child process group (ie. kill the child subprocess, and all subprocesses the child subprocess may have created) for any builder processes that are still running.
This commit is contained in:
parent
00e68d580f
commit
8e2c02f464
@ -14,6 +14,7 @@ import threading
|
|||||||
import queue
|
import queue
|
||||||
import subprocess
|
import subprocess
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
import signal
|
||||||
|
|
||||||
# Ensure we can output any old crap to stdout and stderr
|
# Ensure we can output any old crap to stdout and stderr
|
||||||
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
|
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
|
||||||
@ -33,9 +34,10 @@ class RusagePopen(subprocess.Popen):
|
|||||||
self.rusage = ru
|
self.rusage = ru
|
||||||
return (pid, sts)
|
return (pid, sts)
|
||||||
|
|
||||||
def rusage_run(*popenargs, timeout=None, **kwargs):
|
def rusage_run(*popenargs, parent=None, timeout=None, **kwargs):
|
||||||
with RusagePopen(*popenargs, **kwargs) as process:
|
with RusagePopen(*popenargs, **kwargs) as process:
|
||||||
try:
|
try:
|
||||||
|
parent.child = process
|
||||||
stdout, stderr = process.communicate(None, timeout=timeout)
|
stdout, stderr = process.communicate(None, timeout=timeout)
|
||||||
except subprocess.TimeoutExpired as exc:
|
except subprocess.TimeoutExpired as exc:
|
||||||
process.kill()
|
process.kill()
|
||||||
@ -47,6 +49,7 @@ def rusage_run(*popenargs, timeout=None, **kwargs):
|
|||||||
retcode = process.poll()
|
retcode = process.poll()
|
||||||
res = subprocess.CompletedProcess(process.args, retcode, stdout, stderr)
|
res = subprocess.CompletedProcess(process.args, retcode, stdout, stderr)
|
||||||
res.rusage = process.rusage
|
res.rusage = process.rusage
|
||||||
|
parent.child = None
|
||||||
return res
|
return res
|
||||||
|
|
||||||
class GeneratorEmpty(Exception):
|
class GeneratorEmpty(Exception):
|
||||||
@ -255,11 +258,19 @@ class BuildProcess(threading.Thread):
|
|||||||
|
|
||||||
self.active = False
|
self.active = False
|
||||||
|
|
||||||
|
self.child = None
|
||||||
|
|
||||||
self.stopping = False
|
self.stopping = False
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.stopping = True
|
self.stopping = True
|
||||||
self.work.put(None)
|
self.work.put(None)
|
||||||
|
if self.child:
|
||||||
|
try:
|
||||||
|
os.killpg(os.getpgid(self.child.pid), signal.SIGTERM)
|
||||||
|
self.child.wait()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def isActive(self):
|
def isActive(self):
|
||||||
return self.active == True
|
return self.active == True
|
||||||
@ -305,14 +316,14 @@ class BuildProcess(threading.Thread):
|
|||||||
with open(job["logfile"], "w") as logfile:
|
with open(job["logfile"], "w") as logfile:
|
||||||
cmd = rusage_run(job["args"], cwd=ROOT,
|
cmd = rusage_run(job["args"], cwd=ROOT,
|
||||||
stdin=subprocess.PIPE, stdout=logfile, stderr=subprocess.STDOUT,
|
stdin=subprocess.PIPE, stdout=logfile, stderr=subprocess.STDOUT,
|
||||||
universal_newlines=True, shell=False)
|
universal_newlines=True, shell=False, parent=self, start_new_session=True)
|
||||||
returncode = cmd.returncode
|
returncode = cmd.returncode
|
||||||
job["cmdproc"] = cmd
|
job["cmdproc"] = cmd
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
cmd = rusage_run(job["args"], cwd=ROOT,
|
cmd = rusage_run(job["args"], cwd=ROOT,
|
||||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||||
universal_newlines=True, shell=False,
|
universal_newlines=True, shell=False, parent=self, start_new_session=True,
|
||||||
encoding="utf-8", errors="replace")
|
encoding="utf-8", errors="replace")
|
||||||
returncode = cmd.returncode
|
returncode = cmd.returncode
|
||||||
job["cmdproc"] = cmd
|
job["cmdproc"] = cmd
|
||||||
@ -434,7 +445,6 @@ class Builder:
|
|||||||
job = None
|
job = None
|
||||||
|
|
||||||
self.captureStats(finished=True)
|
self.captureStats(finished=True)
|
||||||
self.stopProcesses()
|
|
||||||
|
|
||||||
if self.joblogfile:
|
if self.joblogfile:
|
||||||
self.joblogfile.close()
|
self.joblogfile.close()
|
||||||
@ -678,8 +688,13 @@ class Builder:
|
|||||||
process.start()
|
process.start()
|
||||||
|
|
||||||
def stopProcesses(self):
|
def stopProcesses(self):
|
||||||
for process in self.processes:
|
if self.processes:
|
||||||
process.stop()
|
for process in self.processes:
|
||||||
|
process.stop()
|
||||||
|
self.processes = None
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.stopProcesses()
|
||||||
|
|
||||||
def vprint(self, status, task, data, p1=None, p2=None):
|
def vprint(self, status, task, data, p1=None, p2=None):
|
||||||
p1 = (self.threadcount - self.generator.activeJobCount()) if p1 == None else p1
|
p1 = (self.threadcount - self.generator.activeJobCount()) if p1 == None else p1
|
||||||
@ -777,17 +792,22 @@ with open("%s/parallel.pid" % THREAD_CONTROL, "w") as pid:
|
|||||||
print("%d" % os.getpid(), file=pid)
|
print("%d" % os.getpid(), file=pid)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = Builder(args.max_procs, args.plan, args.joblog, args.loadstats, args.stats_interval, \
|
builder = Builder(args.max_procs, args.plan, args.joblog, args.loadstats, args.stats_interval, \
|
||||||
haltonerror=args.halt_on_error, failimmediately=args.fail_immediately, \
|
haltonerror=args.halt_on_error, failimmediately=args.fail_immediately, \
|
||||||
log_burst=args.log_burst, log_combine=args.log_combine, bookends=args.with_bookends, \
|
log_burst=args.log_burst, log_combine=args.log_combine, bookends=args.with_bookends, \
|
||||||
autoremove=args.auto_remove, colors=args.colors, \
|
autoremove=args.auto_remove, colors=args.colors, progress=args.progress, \
|
||||||
debug=args.debug, verbose=args.verbose).build()
|
debug=args.debug, verbose=args.verbose)
|
||||||
|
|
||||||
|
result = builder.build()
|
||||||
|
|
||||||
if DEBUG_LOG:
|
if DEBUG_LOG:
|
||||||
DEBUG_LOG.close()
|
DEBUG_LOG.close()
|
||||||
|
|
||||||
sys.exit(0 if result else 1)
|
sys.exit(0 if result else 1)
|
||||||
except (KeyboardInterrupt, SystemExit) as e:
|
except (KeyboardInterrupt, SystemExit) as e:
|
||||||
|
if builder:
|
||||||
|
builder.cleanup()
|
||||||
|
|
||||||
if type(e) == SystemExit:
|
if type(e) == SystemExit:
|
||||||
sys.exit(int(str(e)))
|
sys.exit(int(str(e)))
|
||||||
else:
|
else:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user