diff options
Diffstat (limited to 'lib/portage/locks.py')
-rw-r--r-- | lib/portage/locks.py | 50 |
1 files changed, 32 insertions, 18 deletions
diff --git a/lib/portage/locks.py b/lib/portage/locks.py index 1c3e13ce4..ee40451b1 100644 --- a/lib/portage/locks.py +++ b/lib/portage/locks.py @@ -79,6 +79,11 @@ class _lock_manager: del _open_inodes[self.inode_key] +def _lockf_test_lock_fn(path, fd, flags): + fcntl.lockf(fd, flags) + return functools.partial(unlockfile, (path, fd, flags, fcntl.lockf)) + + def _get_lock_fn(): """ Returns fcntl.lockf if proven to work, and otherwise returns fcntl.flock. @@ -88,10 +93,7 @@ def _get_lock_fn(): if _lock_fn is not None: return _lock_fn - if _test_lock_fn( - lambda path, fd, flags: fcntl.lockf(fd, flags) - and functools.partial(unlockfile, (path, fd, flags, fcntl.lockf)) - ): + if _test_lock_fn(_lockf_test_lock_fn): _lock_fn = fcntl.lockf return _lock_fn @@ -103,19 +105,6 @@ def _get_lock_fn(): def _test_lock_fn( lock_fn: typing.Callable[[str, int, int], typing.Callable[[], None]] ) -> bool: - def _test_lock(fd, lock_path): - os.close(fd) - try: - with open(lock_path, "a") as f: - lock_fn(lock_path, f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) - except (TryAgain, OSError) as e: - if isinstance(e, TryAgain) or e.errno == errno.EAGAIN: - # Parent process holds lock, as expected. - sys.exit(0) - - # Something went wrong. - sys.exit(1) - fd, lock_path = tempfile.mkstemp() unlock_fn = None try: @@ -125,7 +114,17 @@ def _test_lock_fn( pass else: _lock_manager(fd, os.fstat(fd), lock_path) - proc = multiprocessing.Process(target=_test_lock, args=(fd, lock_path)) + proc = multiprocessing.Process( + target=_subprocess_test_lock, + args=( + # Since file descriptors are not inherited unless the fork start + # method is used, the subprocess should only try to close an + # inherited file descriptor for the fork start method. + fd if multiprocessing.get_start_method() == "fork" else None, + lock_fn, + lock_path, + ), + ) proc.start() proc.join() if proc.exitcode == os.EX_OK: @@ -141,6 +140,21 @@ def _test_lock_fn( return False +def _subprocess_test_lock(fd, lock_fn, lock_path): + if fd is not None: + os.close(fd) + try: + with open(lock_path, "a") as f: + lock_fn(lock_path, f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) + except (TryAgain, OSError) as e: + if isinstance(e, TryAgain) or e.errno == errno.EAGAIN: + # Parent process holds lock, as expected. + sys.exit(0) + + # Something went wrong. + sys.exit(1) + + def _close_fds(): """ This is intended to be called after a fork, in order to close file |