From c4527cccb2f2d4278ee68385c5071b9a2f6a794b Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 10 Apr 2015 12:03:13 +0000 Subject: [PATCH] #669: track all sound processes with the childreaper so they should never end up as zombies, also try to reap / poll processes on exit (cheap) git-svn-id: https://xpra.org/svn/Xpra/trunk@8984 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/child_reaper.py | 10 +++++++++- src/xpra/client/ui_client_base.py | 2 ++ src/xpra/net/subprocess_wrapper.py | 5 ++++- src/xpra/server/server_base.py | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/xpra/child_reaper.py b/src/xpra/child_reaper.py index 5ce1ecdcad..fec9dca7ca 100644 --- a/src/xpra/child_reaper.py +++ b/src/xpra/child_reaper.py @@ -35,6 +35,14 @@ def getChildReaper(quit_cb=None): return singleton +def reaper_cleanup(): + global singleton + if not singleton: + return + singleton.reap() + singleton.poll() + + # Note that this class has async subtleties -- e.g., it is possible for a # child to exit and us to receive the SIGCHLD before our fork() returns (and # thus before we even know the pid of the child). So be careful: @@ -174,6 +182,6 @@ def get_info(self): "children.ignored" : len([x for x in iv if x.ignore])} pi = sorted(self._proc_info, key=lambda x: x.pid, reverse=True) for i, procinfo in enumerate(pi): - d = dict((k,getattr(procinfo,k)) for k in ("name", "command", "ignore", "forget", "returncode", "dead")) + d = dict((k,getattr(procinfo,k)) for k in ("name", "command", "ignore", "forget", "returncode", "dead", "pid")) updict(info, "child[%i]" % i, d) return info diff --git a/src/xpra/client/ui_client_base.py b/src/xpra/client/ui_client_base.py index 8130dd3fbc..6a302890b2 100644 --- a/src/xpra/client/ui_client_base.py +++ b/src/xpra/client/ui_client_base.py @@ -44,6 +44,7 @@ from xpra.scripts.config import parse_bool_or_int from xpra.simple_stats import std_unit from xpra.net import compression, packet_encoding +from xpra.child_reaper import reaper_cleanup from xpra.daemon_thread import make_daemon_thread from xpra.os_util import Queue, os_info, platform_name, get_machine_id, get_user_uuid, bytestostr from xpra.util import nonl, std, AtomicInteger, AdHocStruct, log_screen_sizes, typedict, CLIENT_EXIT @@ -372,6 +373,7 @@ def cleanup(self): self.stop_sending_sound() if self.sound_sink: self.stop_receiving_sound() + reaper_cleanup() log("UIXpraClient.cleanup() done") def destroy_all_windows(self): diff --git a/src/xpra/net/subprocess_wrapper.py b/src/xpra/net/subprocess_wrapper.py index 1b862a77fa..183eb21d69 100644 --- a/src/xpra/net/subprocess_wrapper.py +++ b/src/xpra/net/subprocess_wrapper.py @@ -16,6 +16,7 @@ from xpra.net.bytestreams import TwoFileConnection from xpra.net.protocol import Protocol from xpra.os_util import Queue, setbinarymode, SIGNAMES, bytestostr +from xpra.child_reaper import getChildReaper from xpra.log import Logger log = Logger("util") @@ -266,7 +267,9 @@ def make_protocol(self): def exec_subprocess(self): kwargs = self.exec_kwargs() log("exec_subprocess() command=%s, kwargs=%s", self.command, kwargs) - return subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr.fileno(), env=self.get_env(), **kwargs) + proc = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stderr.fileno(), env=self.get_env(), **kwargs) + getChildReaper().add_process(proc, self.description, self.command, True, True, callback=self.subprocess_exit) + return proc def get_env(self): env = os.environ.copy() diff --git a/src/xpra/server/server_base.py b/src/xpra/server/server_base.py index d0e89fa117..29c611f8b4 100644 --- a/src/xpra/server/server_base.py +++ b/src/xpra/server/server_base.py @@ -27,6 +27,7 @@ from xpra.os_util import thread, get_hex_uuid from xpra.util import typedict, updict, log_screen_sizes, SERVER_EXIT, SERVER_ERROR, SERVER_SHUTDOWN, CLIENT_REQUEST, DETACH_REQUEST, NEW_CLIENT, DONE, IDLE_TIMEOUT,\ repr_ellipsized +from xpra.child_reaper import reaper_cleanup from xpra.scripts.config import python_platform, parse_bool_or_int from xpra.scripts.main import sound_option from xpra.codecs.loader import PREFERED_ENCODING_ORDER, PROBLEMATIC_ENCODINGS, codec_versions, has_codec, get_codec @@ -571,6 +572,7 @@ def cleanup(self, *args): self.notifications_forwarder = None ServerCore.cleanup(self) getVideoHelper().cleanup() + reaper_cleanup() self.cleanup_pulseaudio() def add_listen_socket(self, socktype, socket):