From 2f657c642ff292fd44ed9cb253ab6d13da84303f Mon Sep 17 00:00:00 2001
From: Zac Medico <zmedico@gentoo.org>
Date: Mon, 15 Jun 2020 12:46:03 -0700
Subject: [PATCH 1/2] Revert "Support PORTAGE_LOG_FILTER_FILE_CMD (bug 709746)"

This reverts commit ab7556a5c0df418d61148a05347f4c73115e5747.
We've had reports of emerge hangs, so reverting this for now.

Bug: https://bugs.gentoo.org/709746
Signed-off-by: Zac Medico <zmedico@gentoo.org>
---
 lib/_emerge/AbstractEbuildProcess.py          |   3 +-
 lib/_emerge/BinpkgFetcher.py                  |   3 +-
 lib/_emerge/EbuildFetcher.py                  |   3 +-
 lib/_emerge/EbuildPhase.py                    |  47 ++------
 lib/_emerge/SpawnProcess.py                   |  58 +++-------
 lib/portage/dbapi/_MergeProcess.py            |   3 +-
 .../ebuild/_config/special_env_vars.py        |   8 +-
 lib/portage/util/_async/BuildLogger.py        | 109 ------------------
 lib/portage/util/_async/SchedulerInterface.py |  32 +----
 man/make.conf.5                               |   7 +-
 10 files changed, 30 insertions(+), 243 deletions(-)
 delete mode 100644 lib/portage/util/_async/BuildLogger.py

diff --git a/lib/_emerge/AbstractEbuildProcess.py b/lib/_emerge/AbstractEbuildProcess.py
index ae1aae55f1..1c1955cfe3 100644
--- a/lib/_emerge/AbstractEbuildProcess.py
+++ b/lib/_emerge/AbstractEbuildProcess.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2019 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
@@ -196,7 +196,6 @@ def _start_post_builddir_lock(self, lock_future=None, start_ipc_daemon=False):
 			null_fd = os.open('/dev/null', os.O_RDONLY)
 			self.fd_pipes[0] = null_fd
 
-		self.log_filter_file = self.settings.get('PORTAGE_LOG_FILTER_FILE_CMD')
 		try:
 			SpawnProcess._start(self)
 		finally:
diff --git a/lib/_emerge/BinpkgFetcher.py b/lib/_emerge/BinpkgFetcher.py
index 2e5861cc19..36d027de3c 100644
--- a/lib/_emerge/BinpkgFetcher.py
+++ b/lib/_emerge/BinpkgFetcher.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import functools
@@ -158,7 +158,6 @@ def _start(self):
 		self.env = fetch_env
 		if settings.selinux_enabled():
 			self._selinux_type = settings["PORTAGE_FETCH_T"]
-		self.log_filter_file = settings.get('PORTAGE_LOG_FILTER_FILE_CMD')
 		SpawnProcess._start(self)
 
 	def _pipe(self, fd_pipes):
diff --git a/lib/_emerge/EbuildFetcher.py b/lib/_emerge/EbuildFetcher.py
index 55349c33c4..1e40994fb1 100644
--- a/lib/_emerge/EbuildFetcher.py
+++ b/lib/_emerge/EbuildFetcher.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2020 Gentoo Authors
+# Copyright 1999-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import copy
@@ -225,7 +225,6 @@ def _start(self):
 			settings["NOCOLOR"] = nocolor
 
 		self._settings = settings
-		self.log_filter_file = settings.get('PORTAGE_LOG_FILTER_FILE_CMD')
 		ForkProcess._start(self)
 
 		# Free settings now since it's no longer needed in
diff --git a/lib/_emerge/EbuildPhase.py b/lib/_emerge/EbuildPhase.py
index ddb3dc7196..477e0ba97d 100644
--- a/lib/_emerge/EbuildPhase.py
+++ b/lib/_emerge/EbuildPhase.py
@@ -26,8 +26,6 @@
 from portage.util.futures.compat_coroutine import coroutine
 from portage.util import writemsg
 from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
-from portage.util._async.BuildLogger import BuildLogger
-from portage.util.futures import asyncio
 from portage.util.futures.executor.fork import ForkExecutor
 
 try:
@@ -71,11 +69,6 @@ class EbuildPhase(CompositeTask):
 	_locked_phases = ("setup", "preinst", "postinst", "prerm", "postrm")
 
 	def _start(self):
-		future = asyncio.ensure_future(self._async_start(), loop=self.scheduler)
-		self._start_task(AsyncTaskFuture(future=future), self._async_start_exit)
-
-	@coroutine
-	def _async_start(self):
 
 		need_builddir = self.phase not in EbuildProcess._phases_without_builddir
 
@@ -133,7 +126,7 @@ def _async_start(self):
 			# Force background=True for this header since it's intended
 			# for the log and it doesn't necessarily need to be visible
 			# elsewhere.
-			yield self._elog('einfo', msg, background=True)
+			self._elog('einfo', msg, background=True)
 
 		if self.phase == 'package':
 			if 'PORTAGE_BINPKG_TMPFILE' not in self.settings:
@@ -141,12 +134,6 @@ def _async_start(self):
 					os.path.join(self.settings['PKGDIR'],
 					self.settings['CATEGORY'], self.settings['PF']) + '.tbz2'
 
-	def _async_start_exit(self, task):
-		task.future.cancelled() or task.future.result()
-		if self._default_exit(task) != os.EX_OK:
-			self.wait()
-			return
-
 		if self.phase in ("pretend", "prerm"):
 			env_extractor = BinpkgEnvExtractor(background=self.background,
 				scheduler=self.scheduler, settings=self.settings)
@@ -404,7 +391,6 @@ def _fail_clean_exit(self, clean_phase):
 		self.returncode = 1
 		self.wait()
 
-	@coroutine
 	def _elog(self, elog_funcname, lines, background=None):
 		if background is None:
 			background = self.background
@@ -421,30 +407,11 @@ def _elog(self, elog_funcname, lines, background=None):
 			portage.output.havecolor = global_havecolor
 		msg = out.getvalue()
 		if msg:
-			build_logger = None
-			try:
-				log_file = None
-				log_path = None
-				if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
-					log_path = self.settings.get("PORTAGE_LOG_FILE")
-				if log_path:
-					build_logger = BuildLogger(env=self.settings.environ(),
-						log_path=log_path,
-						log_filter_file=self.settings.get('PORTAGE_LOG_FILTER_FILE_CMD'),
-						scheduler=self.scheduler)
-					build_logger.start()
-					log_file = build_logger.stdin
-
-				yield self.scheduler.async_output(msg, log_file=log_file,
-					background=background)
-
-				if build_logger is not None:
-					build_logger.stdin.close()
-					yield build_logger.async_wait()
-			except asyncio.CancelledError:
-				if build_logger is not None:
-					build_logger.cancel()
-				raise
+			log_path = None
+			if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
+				log_path = self.settings.get("PORTAGE_LOG_FILE")
+			self.scheduler.output(msg, log_path=log_path,
+				background=background)
 
 
 class _PostPhaseCommands(CompositeTask):
@@ -513,4 +480,4 @@ def _soname_deps_qa(self):
 			qa_msg.extend("\t%s: %s" % (filename, " ".join(sorted(soname_deps)))
 				for filename, soname_deps in unresolved)
 			qa_msg.append("")
-			yield self.elog("eqawarn", qa_msg)
+			self.elog("eqawarn", qa_msg)
diff --git a/lib/_emerge/SpawnProcess.py b/lib/_emerge/SpawnProcess.py
index f96911571e..395d66bb91 100644
--- a/lib/_emerge/SpawnProcess.py
+++ b/lib/_emerge/SpawnProcess.py
@@ -1,4 +1,4 @@
-# Copyright 2008-2020 Gentoo Authors
+# Copyright 2008-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 try:
@@ -19,10 +19,7 @@
 from portage.localization import _
 from portage.output import EOutput
 from portage.util import writemsg_level
-from portage.util._async.BuildLogger import BuildLogger
 from portage.util._async.PipeLogger import PipeLogger
-from portage.util.futures import asyncio
-from portage.util.futures.compat_coroutine import coroutine
 
 class SpawnProcess(SubProcess):
 
@@ -37,8 +34,8 @@ class SpawnProcess(SubProcess):
 		"path_lookup", "pre_exec", "close_fds", "cgroup",
 		"unshare_ipc", "unshare_mount", "unshare_pid", "unshare_net")
 
-	__slots__ = ("args", "log_filter_file") + \
-		_spawn_kwarg_names + ("_main_task", "_selinux_type",)
+	__slots__ = ("args",) + \
+		_spawn_kwarg_names + ("_pipe_logger", "_selinux_type",)
 
 	# Max number of attempts to kill the processes listed in cgroup.procs,
 	# given that processes may fork before they can be killed.
@@ -140,43 +137,13 @@ def _start(self):
 						fcntl.fcntl(stdout_fd,
 						fcntl.F_GETFD) | fcntl.FD_CLOEXEC)
 
-		build_logger = BuildLogger(env=self.env,
-			log_path=log_file_path,
-			log_filter_file=self.log_filter_file,
-			scheduler=self.scheduler)
-		build_logger.start()
-
-		pipe_logger = PipeLogger(background=self.background,
+		self._pipe_logger = PipeLogger(background=self.background,
 			scheduler=self.scheduler, input_fd=master_fd,
-			log_file_path=build_logger.stdin,
+			log_file_path=log_file_path,
 			stdout_fd=stdout_fd)
-
-		pipe_logger.start()
-
+		self._pipe_logger.addExitListener(self._pipe_logger_exit)
+		self._pipe_logger.start()
 		self._registered = True
-		self._main_task = asyncio.ensure_future(self._main(build_logger, pipe_logger), loop=self.scheduler)
-		self._main_task.add_done_callback(self._main_exit)
-
-	@coroutine
-	def _main(self, build_logger, pipe_logger):
-		try:
-			if pipe_logger.poll() is None:
-				yield pipe_logger.async_wait()
-			if build_logger.poll() is None:
-				yield build_logger.async_wait()
-		except asyncio.CancelledError:
-			if pipe_logger.poll() is None:
-				pipe_logger.cancel()
-			if build_logger.poll() is None:
-				build_logger.cancel()
-			raise
-
-	def _main_exit(self, main_task):
-		try:
-			main_task.result()
-		except asyncio.CancelledError:
-			self.cancel()
-		self._async_waitpid()
 
 	def _can_log(self, slave_fd):
 		return True
@@ -200,17 +167,20 @@ def _spawn(self, args, **kwargs):
 
 		return spawn_func(args, **kwargs)
 
+	def _pipe_logger_exit(self, pipe_logger):
+		self._pipe_logger = None
+		self._async_waitpid()
+
 	def _unregister(self):
 		SubProcess._unregister(self)
 		if self.cgroup is not None:
 			self._cgroup_cleanup()
 			self.cgroup = None
-		if self._main_task is not None:
-			self._main_task.done() or self._main_task.cancel()
+		if self._pipe_logger is not None:
+			self._pipe_logger.cancel()
+			self._pipe_logger = None
 
 	def _cancel(self):
-		if self._main_task is not None:
-			self._main_task.done() or self._main_task.cancel()
 		SubProcess._cancel(self)
 		self._cgroup_cleanup()
 
diff --git a/lib/portage/dbapi/_MergeProcess.py b/lib/portage/dbapi/_MergeProcess.py
index 236d1a2552..371550079e 100644
--- a/lib/portage/dbapi/_MergeProcess.py
+++ b/lib/portage/dbapi/_MergeProcess.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import io
@@ -57,7 +57,6 @@ def _start(self):
 			self.fd_pipes = self.fd_pipes.copy()
 		self.fd_pipes.setdefault(0, portage._get_stdin().fileno())
 
-		self.log_filter_file = self.settings.get('PORTAGE_LOG_FILTER_FILE_CMD')
 		super(MergeProcess, self)._start()
 
 	def _lock_vdb(self):
diff --git a/lib/portage/package/ebuild/_config/special_env_vars.py b/lib/portage/package/ebuild/_config/special_env_vars.py
index f44cb9b1b5..440dd00b2f 100644
--- a/lib/portage/package/ebuild/_config/special_env_vars.py
+++ b/lib/portage/package/ebuild/_config/special_env_vars.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2019 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
 from __future__ import unicode_literals
@@ -175,7 +175,7 @@
 	"PORTAGE_RO_DISTDIRS",
 	"PORTAGE_RSYNC_EXTRA_OPTS", "PORTAGE_RSYNC_OPTS",
 	"PORTAGE_RSYNC_RETRIES", "PORTAGE_SSH_OPTS", "PORTAGE_SYNC_STALE",
-	"PORTAGE_USE", "PORTAGE_LOG_FILTER_FILE_CMD",
+	"PORTAGE_USE",
 	"PORTAGE_LOGDIR", "PORTAGE_LOGDIR_CLEAN",
 	"QUICKPKG_DEFAULT_OPTS", "REPOMAN_DEFAULT_OPTS",
 	"RESUMECOMMAND", "RESUMECOMMAND_FTP",
@@ -204,9 +204,7 @@
 	'PORTAGE_BZIP2_COMMAND':    'bzip2',
 }
 
-validate_commands = ('PORTAGE_BZIP2_COMMAND', 'PORTAGE_BUNZIP2_COMMAND',
-	'PORTAGE_LOG_FILTER_FILE_CMD',
-)
+validate_commands = ('PORTAGE_BZIP2_COMMAND', 'PORTAGE_BUNZIP2_COMMAND',)
 
 # To enhance usability, make some vars case insensitive
 # by forcing them to lower case.
diff --git a/lib/portage/util/_async/BuildLogger.py b/lib/portage/util/_async/BuildLogger.py
deleted file mode 100644
index f5fea77ea7..0000000000
--- a/lib/portage/util/_async/BuildLogger.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# Copyright 2020 Gentoo Authors
-# Distributed under the terms of the GNU General Public License v2
-
-import subprocess
-
-from portage import os
-from portage.util import shlex_split
-from _emerge.AsynchronousTask import AsynchronousTask
-from portage.util._async.PipeLogger import PipeLogger
-from portage.util._async.PopenProcess import PopenProcess
-from portage.util.futures import asyncio
-from portage.util.futures.compat_coroutine import coroutine
-
-
-class BuildLogger(AsynchronousTask):
-	"""
-	Write to a log file, with compression support provided by PipeLogger.
-	If the log_filter_file parameter is specified, then it is interpreted
-	as a command to execute which filters log output (see the
-	PORTAGE_LOG_FILTER_FILE_CMD variable in make.conf(5)). The stdin property
-	provides access to a writable binary file stream (refers to a pipe)
-	that log content should be written to (usually redirected from
-	subprocess stdout and stderr streams).
-	"""
-
-	__slots__ = ('env', 'log_path', 'log_filter_file', '_main_task', '_stdin')
-
-	@property
-	def stdin(self):
-		return self._stdin
-
-	def _start(self):
-		filter_proc = None
-		log_input = None
-		if self.log_path is not None:
-			log_filter_file = self.log_filter_file
-			if log_filter_file is not None:
-				split_value = shlex_split(log_filter_file)
-				log_filter_file = split_value if split_value else None
-			if log_filter_file:
-				filter_input, stdin = os.pipe()
-				log_input, filter_output = os.pipe()
-				try:
-					filter_proc = PopenProcess(
-						proc=subprocess.Popen(
-							log_filter_file,
-							env=self.env,
-							stdin=filter_input,
-							stdout=filter_output,
-							stderr=filter_output,
-						),
-						scheduler=self.scheduler,
-					)
-					filter_proc.start()
-				except EnvironmentError:
-					# Maybe the command is missing or broken somehow...
-					os.close(filter_input)
-					os.close(stdin)
-					os.close(log_input)
-					os.close(filter_output)
-				else:
-					self._stdin = os.fdopen(stdin, 'wb', 0)
-					os.close(filter_input)
-					os.close(filter_output)
-
-		if self._stdin is None:
-			# Since log_filter_file is unspecified or refers to a file
-			# that is missing or broken somehow, create a pipe that
-			# logs directly to pipe_logger.
-			log_input, stdin = os.pipe()
-			self._stdin = os.fdopen(stdin, 'wb', 0)
-
-		# Set background=True so that pipe_logger does not log to stdout.
-		pipe_logger = PipeLogger(background=True,
-			scheduler=self.scheduler, input_fd=log_input,
-			log_file_path=self.log_path)
-		pipe_logger.start()
-
-		self._main_task = asyncio.ensure_future(self._main(filter_proc, pipe_logger), loop=self.scheduler)
-		self._main_task.add_done_callback(self._main_exit)
-
-	@coroutine
-	def _main(self, filter_proc, pipe_logger):
-		try:
-			if pipe_logger.poll() is None:
-				yield pipe_logger.async_wait()
-			if filter_proc is not None and filter_proc.poll() is None:
-				yield filter_proc.async_wait()
-		except asyncio.CancelledError:
-			if pipe_logger.poll() is None:
-				pipe_logger.cancel()
-			if filter_proc is not None and filter_proc.poll() is None:
-				filter_proc.cancel()
-			raise
-
-	def _cancel(self):
-		if self._main_task is not None:
-			self._main_task.done() or self._main_task.cancel()
-		if self._stdin is not None and not self._stdin.closed:
-			self._stdin.close()
-
-	def _main_exit(self, main_task):
-		try:
-			main_task.result()
-		except asyncio.CancelledError:
-			self.cancel()
-			self._was_cancelled()
-		self.returncode = self.returncode or 0
-		self._async_wait()
diff --git a/lib/portage/util/_async/SchedulerInterface.py b/lib/portage/util/_async/SchedulerInterface.py
index 3ff250d1d6..ec6417da10 100644
--- a/lib/portage/util/_async/SchedulerInterface.py
+++ b/lib/portage/util/_async/SchedulerInterface.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2020 Gentoo Authors
+# Copyright 2012-2018 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import gzip
@@ -7,8 +7,6 @@
 from portage import _encodings
 from portage import _unicode_encode
 from portage.util import writemsg_level
-from portage.util.futures._asyncio.streams import _writer
-from portage.util.futures.compat_coroutine import coroutine
 from ..SlotObject import SlotObject
 
 class SchedulerInterface(SlotObject):
@@ -55,34 +53,6 @@ def __init__(self, event_loop, is_background=None, **kwargs):
 	def _return_false():
 		return False
 
-	@coroutine
-	def async_output(self, msg, log_file=None, background=None,
-		level=0, noiselevel=-1):
-		"""
-		Output a msg to stdio (if not in background) and to a log file
-		if provided.
-
-		@param msg: a message string, including newline if appropriate
-		@type msg: str
-		@param log_file: log file in binary mode
-		@type log_file: file
-		@param background: send messages only to log (not to stdio)
-		@type background: bool
-		@param level: a numeric logging level (see the logging module)
-		@type level: int
-		@param noiselevel: passed directly to writemsg
-		@type noiselevel: int
-		"""
-		global_background = self._is_background()
-		if background is None or global_background:
-			background = global_background
-
-		if not background:
-			writemsg_level(msg, level=level, noiselevel=noiselevel)
-
-		if log_file is not None:
-			yield _writer(log_file, _unicode_encode(msg))
-
 	def output(self, msg, log_path=None, background=None,
 		level=0, noiselevel=-1):
 		"""
diff --git a/man/make.conf.5 b/man/make.conf.5
index eb812150fb..a3bd662aee 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -1,4 +1,4 @@
-.TH "MAKE.CONF" "5" "Jun 2020" "Portage VERSION" "Portage"
+.TH "MAKE.CONF" "5" "May 2020" "Portage VERSION" "Portage"
 .SH "NAME"
 make.conf \- custom settings for Portage
 .SH "SYNOPSIS"
@@ -979,11 +979,6 @@ with an integer pid. For example, a value of "ionice \-c 3 \-p \\${PID}"
 will set idle io priority. For more information about ionice, see
 \fBionice\fR(1). This variable is unset by default.
 .TP
-.B PORTAGE_LOG_FILTER_FILE_CMD
-This variable specifies a command that filters build log output to a
-log file. In order to filter ANSI escape codes from build logs,
-\fBansifilter\fR(1) is a convenient setting for this variable.
-.TP
 .B PORTAGE_LOGDIR
 This variable defines the directory in which per\-ebuild logs are kept.
 Logs are created only when this is set. They are stored as

From ca763549507d995e91a49753b13bcca8748fae6c Mon Sep 17 00:00:00 2001
From: Zac Medico <zmedico@gentoo.org>
Date: Mon, 15 Jun 2020 20:14:36 -0700
Subject: [PATCH 2/2] Revert "PipeLogger: non-blocking write to pipe (bug
 709746)"

This reverts commit 3e46825a047067a96ed997fe394f85e042e542a8.
We've had reports of emerge hangs, so reverting this for now.

Bug: https://bugs.gentoo.org/709746
Signed-off-by: Zac Medico <zmedico@gentoo.org>
---
 lib/portage/tests/process/test_PipeLogger.py | 54 ---------------
 lib/portage/util/_async/PipeLogger.py        | 73 ++++----------------
 2 files changed, 13 insertions(+), 114 deletions(-)
 delete mode 100644 lib/portage/tests/process/test_PipeLogger.py

diff --git a/lib/portage/tests/process/test_PipeLogger.py b/lib/portage/tests/process/test_PipeLogger.py
deleted file mode 100644
index 2b9f10eeb1..0000000000
--- a/lib/portage/tests/process/test_PipeLogger.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2020 Gentoo Authors
-# Distributed under the terms of the GNU General Public License v2
-
-from portage import os
-from portage.tests import TestCase
-from portage.util._async.PipeLogger import PipeLogger
-from portage.util.futures import asyncio
-from portage.util.futures._asyncio.streams import _reader, _writer
-from portage.util.futures.compat_coroutine import coroutine, coroutine_return
-
-
-class PipeLoggerTestCase(TestCase):
-
-	@coroutine
-	def _testPipeLoggerToPipe(self, test_string, loop=None):
-		"""
-		Test PipeLogger writing to a pipe connected to a PipeReader.
-		This verifies that PipeLogger does not deadlock when writing
-		to a pipe that's drained by a PipeReader running in the same
-		process (requires non-blocking write).
-		"""
-
-		pr1, pw1 = os.pipe()
-		writer = asyncio.ensure_future(_writer(pw1, test_string.encode('ascii')), loop=loop)
-
-		pr, pw = os.pipe()
-
-		consumer = PipeLogger(background=True,
-			input_fd=pr1,
-			log_file_path=os.fdopen(pw, 'wb', 0),
-			scheduler=loop)
-		consumer.start()
-
-		# Before starting the reader, wait here for a moment, in order
-		# to exercise PipeLogger's handling of EAGAIN during write.
-		yield asyncio.wait([writer], timeout=0.01)
-
-		reader = _reader(pr, loop=loop)
-		yield writer
-		content = yield reader
-		yield consumer.async_wait()
-
-		self.assertEqual(consumer.returncode, os.EX_OK)
-
-		coroutine_return(content.decode('ascii', 'replace'))
-
-	def testPipeLogger(self):
-		loop = asyncio._wrap_loop()
-
-		for x in (1, 2, 5, 6, 7, 8, 2**5, 2**10, 2**12, 2**13, 2**14, 2**17, 2**17 + 1):
-			test_string = x * "a"
-			output = loop.run_until_complete(self._testPipeLoggerToPipe(test_string, loop=loop))
-			self.assertEqual(test_string, output,
-				"x = %s, len(output) = %s" % (x, len(output)))
diff --git a/lib/portage/util/_async/PipeLogger.py b/lib/portage/util/_async/PipeLogger.py
index 1776cc860a..a4258f350a 100644
--- a/lib/portage/util/_async/PipeLogger.py
+++ b/lib/portage/util/_async/PipeLogger.py
@@ -8,9 +8,6 @@
 
 import portage
 from portage import os, _encodings, _unicode_encode
-from portage.util.futures import asyncio
-from portage.util.futures.compat_coroutine import coroutine
-from portage.util.futures.unix_events import _set_nonblocking
 from _emerge.AbstractPollTask import AbstractPollTask
 
 class PipeLogger(AbstractPollTask):
@@ -24,15 +21,13 @@ class PipeLogger(AbstractPollTask):
 	"""
 
 	__slots__ = ("input_fd", "log_file_path", "stdout_fd") + \
-		("_io_loop_task", "_log_file", "_log_file_real")
+		("_log_file", "_log_file_real")
 
 	def _start(self):
 
 		log_file_path = self.log_file_path
-		if hasattr(log_file_path, 'write'):
-			self._log_file = log_file_path
-			_set_nonblocking(self._log_file.fileno())
-		elif log_file_path is not None:
+		if log_file_path is not None:
+
 			self._log_file = open(_unicode_encode(log_file_path,
 				encoding=_encodings['fs'], errors='strict'), mode='ab')
 			if log_file_path.endswith('.gz'):
@@ -62,8 +57,7 @@ def _start(self):
 				fcntl.fcntl(fd, fcntl.F_SETFD,
 					fcntl.fcntl(fd, fcntl.F_GETFD) | fcntl.FD_CLOEXEC)
 
-		self._io_loop_task = asyncio.ensure_future(self._io_loop(fd), loop=self.scheduler)
-		self._io_loop_task.add_done_callback(self._io_loop_done)
+		self.scheduler.add_reader(fd, self._output_handler, fd)
 		self._registered = True
 
 	def _cancel(self):
@@ -71,8 +65,8 @@ def _cancel(self):
 		if self.returncode is None:
 			self.returncode = self._cancelled_returncode
 
-	@coroutine
-	def _io_loop(self, fd):
+	def _output_handler(self, fd):
+
 		background = self.background
 		stdout_fd = self.stdout_fd
 		log_file = self._log_file 
@@ -82,19 +76,14 @@ def _io_loop(self, fd):
 
 			if buf is None:
 				# not a POLLIN event, EAGAIN, etc...
-				future = self.scheduler.create_future()
-				self.scheduler.add_reader(fd, future.set_result, None)
-				try:
-					yield future
-				finally:
-					if not self.scheduler.is_closed():
-						self.scheduler.remove_reader(fd)
-						future.done() or future.cancel()
-				continue
+				break
 
 			if not buf:
 				# EOF
-				return
+				self._unregister()
+				self.returncode = self.returncode or os.EX_OK
+				self._async_wait()
+				break
 
 			else:
 				if not background and stdout_fd is not None:
@@ -131,39 +120,8 @@ def _io_loop(self, fd):
 								fcntl.F_GETFL) ^ os.O_NONBLOCK)
 
 				if log_file is not None:
-					if isinstance(log_file, gzip.GzipFile):
-						# Use log_file.write since data written directly
-						# to the file descriptor bypasses compression.
-						log_file.write(buf)
-						log_file.flush()
-						continue
-
-					write_buf = buf
-					while write_buf:
-						try:
-							# Use os.write, since the log_file.write method
-							# looses data when an EAGAIN occurs.
-							write_buf = write_buf[os.write(log_file.fileno(), write_buf):]
-						except EnvironmentError as e:
-							if e.errno != errno.EAGAIN:
-								raise
-							future = self.scheduler.create_future()
-							self.scheduler.add_writer(self._log_file.fileno(), future.set_result, None)
-							try:
-								yield future
-							finally:
-								if not self.scheduler.is_closed():
-									self.scheduler.remove_writer(self._log_file.fileno())
-									future.done() or future.cancel()
-
-	def _io_loop_done(self, future):
-		try:
-			future.result()
-		except asyncio.CancelledError:
-			self.cancel()
-			self._was_cancelled()
-		self.returncode = self.returncode or os.EX_OK
-		self._async_wait()
+					log_file.write(buf)
+					log_file.flush()
 
 	def _unregister(self):
 		if self.input_fd is not None:
@@ -175,16 +133,11 @@ def _unregister(self):
 				self.input_fd.close()
 			self.input_fd = None
 
-		if self._io_loop_task is not None:
-			self._io_loop_task.done() or self._io_loop_task.cancel()
-			self._io_loop_task = None
-
 		if self.stdout_fd is not None:
 			os.close(self.stdout_fd)
 			self.stdout_fd = None
 
 		if self._log_file is not None:
-			self.scheduler.remove_writer(self._log_file.fileno())
 			self._log_file.close()
 			self._log_file = None
 
