summaryrefslogtreecommitdiffstats
path: root/pym/_emerge
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2008-12-25 01:22:12 +0000
committerZac Medico <zmedico@gentoo.org>2008-12-25 01:22:12 +0000
commita7c403d2b845431fbe8ee786690016a0b3a39aae (patch)
treeca633297454c81eb92120f5295cdb2446463f672 /pym/_emerge
parent26da1ffb71799b9222fd2dde143d34fc389d5b5b (diff)
downloadportage-a7c403d2b845431fbe8ee786690016a0b3a39aae.tar.gz
portage-a7c403d2b845431fbe8ee786690016a0b3a39aae.tar.bz2
portage-a7c403d2b845431fbe8ee786690016a0b3a39aae.zip
Add handling for POLLERR, POLLNVAL, and POLLHUP. Hopefully this solves hung poll calls
with defunct ebuild.sh processes, reported on solaris systems by Fabian Groffen <grobian@g.o>. (trunk r12287) svn path=/main/branches/2.1.6/; revision=12317
Diffstat (limited to 'pym/_emerge')
-rw-r--r--pym/_emerge/__init__.py112
1 files changed, 72 insertions, 40 deletions
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py
index c82bd2062..e2cc10626 100644
--- a/pym/_emerge/__init__.py
+++ b/pym/_emerge/__init__.py
@@ -1620,6 +1620,20 @@ class EbuildFetchonly(SlotObject):
portage.elog.elog_process(self.pkg.cpv, self.settings)
return retval
+class PollConstants(object):
+
+ """
+ Provides POLL* constants that are equivalent to those from the
+ select module, for use by PollSelectAdapter.
+ """
+
+ names = ("POLLIN", "POLLPRI", "POLLOUT", "POLLERR", "POLLHUP", "POLLNVAL")
+ v = 1
+ for k in names:
+ locals()[k] = getattr(select, k, v)
+ v *= 2
+ del k, v
+
class AsynchronousTask(SlotObject):
"""
Subclasses override _wait() and _poll() so that calls
@@ -1731,7 +1745,26 @@ class AsynchronousTask(SlotObject):
while self._exit_listener_stack:
self._exit_listener_stack.pop()(self)
-class PipeReader(AsynchronousTask):
+class PollTask(AsynchronousTask):
+
+ __slots__ = ("scheduler",) + \
+ ("_registered",)
+
+ _bufsize = 4096
+ _exceptional_events = PollConstants.POLLERR | PollConstants.POLLNVAL
+ _registered_events = PollConstants.POLLIN | PollConstants.POLLHUP | \
+ _exceptional_events
+
+ def _unregister_if_appropriate(self, event):
+ if self._registered:
+ if event & self._exceptional_events:
+ self._unregister()
+ self.cancel()
+ elif event & PollConstants.POLLHUP:
+ self._unregister()
+ self.wait()
+
+class PipeReader(PollTask):
"""
Reads output from one or more files and saves it in memory,
@@ -1740,10 +1773,8 @@ class PipeReader(AsynchronousTask):
current process.
"""
- __slots__ = ("input_files", "scheduler",) + \
- ("pid", "_read_data", "_registered", "_reg_ids")
-
- _bufsize = 4096
+ __slots__ = ("input_files",) + \
+ ("_read_data", "_reg_ids")
def _start(self):
self._reg_ids = set()
@@ -1752,12 +1783,18 @@ class PipeReader(AsynchronousTask):
fcntl.fcntl(f.fileno(), fcntl.F_SETFL,
fcntl.fcntl(f.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK)
self._reg_ids.add(self.scheduler.register(f.fileno(),
- PollConstants.POLLIN, self._output_handler))
+ self._registered_events, self._output_handler))
self._registered = True
def isAlive(self):
return self._registered
+ def cancel(self):
+ if self.returncode is None:
+ self.returncode = 1
+ self.cancelled = True
+ self.wait()
+
def _wait(self):
if self.returncode is not None:
return self.returncode
@@ -1784,10 +1821,11 @@ class PipeReader(AsynchronousTask):
break
buf = array.array('B')
- try:
- buf.fromfile(f, self._bufsize)
- except EOFError:
- pass
+ if event & PollConstants.POLLIN:
+ try:
+ buf.fromfile(f, self._bufsize)
+ except EOFError:
+ pass
if buf:
self._read_data.append(buf.tostring())
@@ -1795,6 +1833,7 @@ class PipeReader(AsynchronousTask):
self._unregister()
self.wait()
+ self._unregister_if_appropriate(event)
return self._registered
def _unregister(self):
@@ -1963,9 +2002,10 @@ class TaskSequence(CompositeTask):
self._final_exit(task)
self.wait()
-class SubProcess(AsynchronousTask):
+class SubProcess(PollTask):
- __slots__ = ("scheduler",) + ("pid", "_files", "_registered", "_reg_id")
+ __slots__ = ("pid",) + \
+ ("_files", "_reg_id")
# A file descriptor is required for the scheduler to monitor changes from
# inside a poll() loop. When logging is not enabled, create a pipe just to
@@ -2079,7 +2119,6 @@ class SpawnProcess(SubProcess):
_file_names = ("log", "process", "stdout")
_files_dict = slot_dict_class(_file_names, prefix="")
- _bufsize = 4096
def _start(self):
@@ -2157,7 +2196,7 @@ class SpawnProcess(SubProcess):
kwargs.pop("logfile", None)
self._reg_id = self.scheduler.register(files.process.fileno(),
- PollConstants.POLLIN, output_handler)
+ self._registered_events, output_handler)
self._registered = True
retval = self._spawn(self.args, **kwargs)
@@ -2189,10 +2228,11 @@ class SpawnProcess(SubProcess):
def _output_handler(self, fd, event):
files = self._files
buf = array.array('B')
- try:
- buf.fromfile(files.process, self._bufsize)
- except EOFError:
- pass
+ if event & PollConstants.POLLIN:
+ try:
+ buf.fromfile(files.process, self._bufsize)
+ except EOFError:
+ pass
if buf:
if not self.background:
buf.tofile(files.stdout)
@@ -2202,6 +2242,8 @@ class SpawnProcess(SubProcess):
else:
self._unregister()
self.wait()
+
+ self._unregister_if_appropriate(event)
return self._registered
def _dummy_handler(self, fd, event):
@@ -2212,15 +2254,18 @@ class SpawnProcess(SubProcess):
"""
files = self._files
buf = array.array('B')
- try:
- buf.fromfile(files.process, self._bufsize)
- except EOFError:
- pass
+ if event & PollConstants.POLLIN:
+ try:
+ buf.fromfile(files.process, self._bufsize)
+ except EOFError:
+ pass
if buf:
pass
else:
self._unregister()
self.wait()
+
+ self._unregister_if_appropriate(event)
return self._registered
class MiscFunctionsProcess(SpawnProcess):
@@ -2783,7 +2828,6 @@ class EbuildMetadataPhase(SubProcess):
_file_names = ("ebuild",)
_files_dict = slot_dict_class(_file_names, prefix="")
- _bufsize = SpawnProcess._bufsize
_metadata_fd = 9
def _start(self):
@@ -2823,7 +2867,7 @@ class EbuildMetadataPhase(SubProcess):
self._raw_metadata = []
files.ebuild = os.fdopen(master_fd, 'r')
self._reg_id = self.scheduler.register(files.ebuild.fileno(),
- PollConstants.POLLIN, self._output_handler)
+ self._registered_events, self._output_handler)
self._registered = True
retval = portage.doebuild(ebuild_path, "depend",
@@ -2845,8 +2889,9 @@ class EbuildMetadataPhase(SubProcess):
def _output_handler(self, fd, event):
files = self._files
- self._raw_metadata.append(files.ebuild.read())
- if not self._raw_metadata[-1]:
+ if event & PollConstants.POLLIN:
+ self._raw_metadata.append(files.ebuild.read())
+ if not self._raw_metadata[-1] or event & PollConstants.POLLHUP:
# Split lines here so they can be counted inside _set_returncode().
self._raw_metadata = "".join(self._raw_metadata).splitlines()
self._unregister()
@@ -2857,6 +2902,7 @@ class EbuildMetadataPhase(SubProcess):
self.metadata_callback(self.cpv, self.ebuild_path,
self.repo_path, metadata, self.ebuild_mtime)
+ self._unregister_if_appropriate(event)
return self._registered
def _set_returncode(self, wait_retval):
@@ -8576,20 +8622,6 @@ class PackageCounters(object):
(self.blocks - self.blocks_satisfied))
return "".join(myoutput)
-class PollConstants(object):
-
- """
- Provides POLL* constants that are equivalent to those from the
- select module, for use by PollSelectAdapter.
- """
-
- names = ("POLLIN", "POLLPRI", "POLLOUT", "POLLERR", "POLLHUP", "POLLNVAL")
- v = 1
- for k in names:
- locals()[k] = getattr(select, k, v)
- v *= 2
- del k, v
-
class PollSelectAdapter(PollConstants):
"""