diff options
author | Zac Medico <zmedico@gentoo.org> | 2011-03-17 14:12:13 -0700 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2011-03-18 12:50:14 -0700 |
commit | 768958c9d1504f72d2f252c1793a5bc48a2a4a85 (patch) | |
tree | b5d66a46683197736331029a4b1da01a4a66f407 /bin | |
parent | f72d4b20478a33da08dd30f399b3a55cc7a9dda4 (diff) | |
download | portage-768958c9d1504f72d2f252c1793a5bc48a2a4a85.tar.gz portage-768958c9d1504f72d2f252c1793a5bc48a2a4a85.tar.bz2 portage-768958c9d1504f72d2f252c1793a5bc48a2a4a85.zip |
ebuild-ipc: use non-blocking read
This makes it possible for the daemon to send a reply without blocking,
thereby improving performance and also making it possible for the
daemon to do a non-blocking write without a race condition.
This reverts part of commit 81fc303212b8379219cf5d463c8717359b972dba,
which probably didn't help portability anyway. Now, ebuild-ipc is using
non-blocking read with os.read and EAGAIN handling, just like
EbuildIpcDaemon since commit 7e5b81da12dd7bd59f6620840dc0d824e3f4d69a
(known compatible with FreeBSD).
Diffstat (limited to 'bin')
-rwxr-xr-x | bin/ebuild-ipc.py | 54 |
1 files changed, 41 insertions, 13 deletions
diff --git a/bin/ebuild-ipc.py b/bin/ebuild-ipc.py index d8e7e5512..cb77171ec 100755 --- a/bin/ebuild-ipc.py +++ b/bin/ebuild-ipc.py @@ -134,23 +134,43 @@ class EbuildIpc(object): return os.WEXITSTATUS(wait_retval[1]) - def _receive_reply(self): + def _receive_reply(self, input_fd): - # File streams are in unbuffered mode since we do atomic - # read and write of whole pickles. - input_file = open(self.ipc_out_fifo, 'rb', 0) - - # For maximum portability, use a single atomic read. + # Timeouts are handled by the parent process, so just + # block until input is available. For maximum portability, + # use a single atomic read. buf = None - try: - buf = input_file.read(self._BUFSIZE) - except IOError as e: - if not buf: + while True: + try: + events = select.select([input_fd], [], []) + except select.error as e: portage.util.writemsg_level( - "ebuild-ipc: %s\n" % (e,), + "ebuild-ipc: %s: %s\n" % \ + (portage.localization._('during select for read'), e), level=logging.ERROR, noiselevel=-1) + continue - input_file.close() + if events[0]: + # For maximum portability, use os.read() here since + # array.fromfile() and file.read() are both known to + # erroneously return an empty string from this + # non-blocking fifo stream on FreeBSD (bug #337465). + try: + buf = os.read(input_fd, self._BUFSIZE) + except OSError as e: + if e.errno != errno.EAGAIN: + portage.util.writemsg_level( + "ebuild-ipc: %s: %s\n" % \ + (portage.localization._('read error'), e), + level=logging.ERROR, noiselevel=-1) + break + # Assume that another event will be generated + # if there's any relevant data. + continue + + # Only one (atomic) read should be necessary. + if buf: + break retval = 2 @@ -192,6 +212,13 @@ class EbuildIpc(object): self._no_daemon_msg() return 2 + # Open the input fifo before the output fifo, in order to make it + # possible for the daemon to send a reply without blocking. This + # improves performance, and also makes it possible for the daemon + # to do a non-blocking write without a race condition. + input_fd = os.open(self.ipc_out_fifo, + os.O_RDONLY|os.O_NONBLOCK) + # Use forks so that the child process can handle blocking IO # un-interrupted, while the parent handles all timeout # considerations. This helps to avoid possible race conditions @@ -231,12 +258,13 @@ class EbuildIpc(object): if pid == 0: os.close(pr) - retval = self._receive_reply() + retval = self._receive_reply(input_fd) os._exit(retval) os.close(pw) retval = self._wait(pid, pr, portage.localization._('during read')) os.close(pr) + os.close(input_fd) return retval def ebuild_ipc_main(args): |