commit: 5ef5fbaab88de47d4dbab333661d3525261d7633 Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Mon Sep 26 06:26:35 2016 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Sun Oct 2 04:33:35 2016 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=5ef5fbaa
locks: use fcntl.flock if fcntl.lockf is broken (bug 595146) This is needed for Windows Subsystem for Linux (WSL), as well as older versions of PyPy. X-Gentoo-bug: 595146 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=595146 Acked-by: Brian Dolbec <dolsen <AT> gentoo.org> pym/portage/locks.py | 59 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/pym/portage/locks.py b/pym/portage/locks.py index 42ff1e3..f61e181 100644 --- a/pym/portage/locks.py +++ b/pym/portage/locks.py @@ -8,8 +8,9 @@ __all__ = ["lockdir", "unlockdir", "lockfile", "unlockfile", \ import errno import fcntl -import platform +import multiprocessing import sys +import tempfile import time import warnings @@ -27,17 +28,61 @@ if sys.hexversion >= 0x3000000: HARDLINK_FD = -2 _HARDLINK_POLL_LATENCY = 3 # seconds -_default_lock_fn = fcntl.lockf - -if platform.python_implementation() == 'PyPy': - # workaround for https://bugs.pypy.org/issue747 - _default_lock_fn = fcntl.flock # Used by emerge in order to disable the "waiting for lock" message # so that it doesn't interfere with the status display. _quiet = False +_lock_fn = None + + +def _get_lock_fn(): + """ + Returns fcntl.lockf if proven to work, and otherwise returns fcntl.flock. + On some platforms fcntl.lockf is known to be broken. + """ + global _lock_fn + if _lock_fn is not None: + return _lock_fn + + def _test_lock(fd, lock_path): + os.close(fd) + try: + with open(lock_path, 'a') as f: + fcntl.lockf(f.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) + except EnvironmentError as e: + if e.errno == errno.EAGAIN: + # Parent process holds lock, as expected. + sys.exit(0) + + # Something went wrong. + sys.exit(1) + + fd, lock_path = tempfile.mkstemp() + try: + try: + fcntl.lockf(fd, fcntl.LOCK_EX) + except EnvironmentError: + pass + else: + proc = multiprocessing.Process(target=_test_lock, + args=(fd, lock_path)) + proc.start() + proc.join() + if proc.exitcode == os.EX_OK: + # Use fcntl.lockf because the test passed. + _lock_fn = fcntl.lockf + return _lock_fn + finally: + os.close(fd) + os.unlink(lock_path) + + # Fall back to fcntl.flock. + _lock_fn = fcntl.flock + return _lock_fn + + _open_fds = set() def _close_fds(): @@ -146,7 +191,7 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0, # try for a non-blocking lock, if it's held, throw a message # we're waiting on lockfile and use a blocking attempt. - locking_method = portage._eintr_func_wrapper(_default_lock_fn) + locking_method = portage._eintr_func_wrapper(_get_lock_fn()) try: if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ: raise IOError(errno.ENOSYS, "Function not implemented")
