[PATCH] MergeProcess: replace os.fork with multiprocessing.Process (bug 730192)

Zac Medico <[email protected]>
Newsgroups gmane.linux.gentoo.portage.devel
Message-ID <[email protected]>
Fix the MergeProcess _spawn method to call the superclass _spawn
method, in order to replace os.fork with multiprocessing.Process,
promoting a healthy state for the forked interpreter.

Bug: https://bugs.gentoo.org/730192
Signed-off-by: Zac Medico <[email protected]>
---
 lib/portage/dbapi/_MergeProcess.py | 102 ++++++++---------------------
 1 file changed, 28 insertions(+), 74 deletions(-)

diff --git a/lib/portage/dbapi/_MergeProcess.py b/lib/portage/dbapi/_MergeProcess.py
index 274ef586f..adf078cb2 100644
--- a/lib/portage/dbapi/_MergeProcess.py
+++ b/lib/portage/dbapi/_MergeProcess.py
@@ -3,9 +3,6 @@
 
 import io
 import platform
-import signal
-import sys
-import traceback
 
 import fcntl
 import portage
@@ -24,7 +21,7 @@ class MergeProcess(ForkProcess):
 		'vartree', 'blockers', 'pkgloc', 'infloc', 'myebuild',
 		'mydbapi', 'postinst_failure', 'prev_mtimes', 'unmerge',
 		'_elog_reader_fd',
-		'_buf', '_elog_keys', '_locked_vdb')
+		'_buf', '_counter', '_dblink', '_elog_keys', '_locked_vdb')
 
 	def _start(self):
 		# Portage should always call setcpv prior to this
@@ -132,57 +129,31 @@ class MergeProcess(ForkProcess):
 		# FEATURES=parallel-install skips this lock in order to
 		# improve performance, and the risk is practically negligible.
 		self._lock_vdb()
-		counter = None
 		if not self.unmerge:
-			counter = self.vartree.dbapi.counter_tick()
-
-		parent_pid = os.getpid()
-		pid = None
-		try:
-			pid = os.fork()
-
-			if pid != 0:
-				if not isinstance(pid, int):
-					raise AssertionError(
-						"fork returned non-integer: %s" % (repr(pid),))
-
-				os.close(elog_writer_fd)
-				self._elog_reader_fd = elog_reader_fd
-				self._buf = ""
-				self._elog_keys = set()
-				# Discard messages which will be collected by the subprocess,
-				# in order to avoid duplicates (bug #446136).
-				portage.elog.messages.collect_messages(key=mylink.mycpv)
-
-				# invalidate relevant vardbapi caches
-				if self.vartree.dbapi._categories is not None:
-					self.vartree.dbapi._categories = None
-				self.vartree.dbapi._pkgs_changed = True
-				self.vartree.dbapi._clear_pkg_cache(mylink)
-
-				return [pid]
-
-			os.close(elog_reader_fd)
-
-			# Use default signal handlers in order to avoid problems
-			# killing subprocesses as reported in bug #353239.
-			signal.signal(signal.SIGINT, signal.SIG_DFL)
-			signal.signal(signal.SIGTERM, signal.SIG_DFL)
-
-			# Unregister SIGCHLD handler and wakeup_fd for the parent
-			# process's event loop (bug 655656).
-			signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-			try:
-				wakeup_fd = signal.set_wakeup_fd(-1)
-				if wakeup_fd > 0:
-					os.close(wakeup_fd)
-			except (ValueError, OSError):
-				pass
-
-			portage.locks._close_fds()
-			# We don't exec, so use close_fds=False
-			# (see _setup_pipes docstring).
-			portage.process._setup_pipes(fd_pipes, close_fds=False)
+			self._counter = self.vartree.dbapi.counter_tick()
+
+		self._dblink = mylink
+		self._elog_reader_fd = elog_reader_fd
+		pids = super(MergeProcess, self)._spawn(args, fd_pipes, **kwargs)
+		os.close(elog_writer_fd)
+		self._buf = ""
+		self._elog_keys = set()
+		# Discard messages which will be collected by the subprocess,
+		# in order to avoid duplicates (bug #446136).
+		portage.elog.messages.collect_messages(key=mylink.mycpv)
+
+		# invalidate relevant vardbapi caches
+		if self.vartree.dbapi._categories is not None:
+			self.vartree.dbapi._categories = None
+		self.vartree.dbapi._pkgs_changed = True
+		self.vartree.dbapi._clear_pkg_cache(mylink)
+
+		return pids
+
+	def _run(self):
+			os.close(self._elog_reader_fd)
+			counter = self._counter
+			mylink = self._dblink
 
 			portage.output.havecolor = self.settings.get('NOCOLOR') \
 				not in ('yes', 'true')
@@ -207,8 +178,7 @@ class MergeProcess(ForkProcess):
 			self.settings.backup_changes("PORTAGE_BACKGROUND")
 
 			rval = 1
-			try:
-				if self.unmerge:
+			if self.unmerge:
 					if not mylink.exists():
 						rval = os.EX_OK
 					elif mylink.unmerge(
@@ -219,27 +189,11 @@ class MergeProcess(ForkProcess):
 						finally:
 							mylink.unlockdb()
 						rval = os.EX_OK
-				else:
+			else:
 					rval = mylink.merge(self.pkgloc, self.infloc,
 						myebuild=self.myebuild, mydbapi=self.mydbapi,
 						prev_mtimes=self.prev_mtimes, counter=counter)
-			except SystemExit:
-				raise
-			except:
-				traceback.print_exc()
-				# os._exit() skips stderr flush!
-				sys.stderr.flush()
-			finally:
-				os._exit(rval)
-
-		finally:
-			if pid == 0 or (pid is None and os.getpid() != parent_pid):
-				# Call os._exit() from a finally block in order
-				# to suppress any finally blocks from earlier
-				# in the call stack (see bug #345289). This
-				# finally block has to be setup before the fork
-				# in order to avoid a race condition.
-				os._exit(1)
+			return rval
 
 	def _async_waitpid_cb(self, *args, **kwargs):
 		"""
-- 
2.25.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.