diff options
author | Zac Medico <zmedico@gentoo.org> | 2008-12-25 01:22:12 +0000 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2008-12-25 01:22:12 +0000 |
commit | a7c403d2b845431fbe8ee786690016a0b3a39aae (patch) | |
tree | ca633297454c81eb92120f5295cdb2446463f672 /pym/_emerge/__init__.py | |
parent | 26da1ffb71799b9222fd2dde143d34fc389d5b5b (diff) | |
download | portage-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/__init__.py')
-rw-r--r-- | pym/_emerge/__init__.py | 112 |
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): """ |