Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] locks: use fcntl.flock if fcntl.lockf is broken (bug 595146)
Date: Mon, 26 Sep 2016 06:45:23
Message-Id: 1474872291-16914-1-git-send-email-zmedico@gentoo.org
1 This is needed for Windows Subsystem for Linux (WSL), as well as
2 older versions of PyPy.
3
4 X-Gentoo-bug: 595146
5 X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=595146
6 ---
7 pym/portage/locks.py | 59 +++++++++++++++++++++++++++++++++++++++++++++-------
8 1 file changed, 52 insertions(+), 7 deletions(-)
9
10 diff --git a/pym/portage/locks.py b/pym/portage/locks.py
11 index 42ff1e3..a4564cf 100644
12 --- a/pym/portage/locks.py
13 +++ b/pym/portage/locks.py
14 @@ -8,8 +8,9 @@ __all__ = ["lockdir", "unlockdir", "lockfile", "unlockfile", \
15
16 import errno
17 import fcntl
18 -import platform
19 +import multiprocessing
20 import sys
21 +import tempfile
22 import time
23 import warnings
24
25 @@ -27,17 +28,61 @@ if sys.hexversion >= 0x3000000:
26
27 HARDLINK_FD = -2
28 _HARDLINK_POLL_LATENCY = 3 # seconds
29 -_default_lock_fn = fcntl.lockf
30 -
31 -if platform.python_implementation() == 'PyPy':
32 - # workaround for https://bugs.pypy.org/issue747
33 - _default_lock_fn = fcntl.flock
34
35 # Used by emerge in order to disable the "waiting for lock" message
36 # so that it doesn't interfere with the status display.
37 _quiet = False
38
39
40 +_lock_fn = None
41 +
42 +
43 +def _get_lock_fn():
44 + """
45 + Returns fcntl.lockf if proven to work, and otherwise returns fcntl.flock.
46 + On some platforms fcntl.lockf is known to be broken.
47 + """
48 + global _lock_fn
49 + if _lock_fn is not None:
50 + return _lock_fn
51 +
52 + def _test_lock(fd, lock_path):
53 + os.close(fd)
54 + try:
55 + with open(lock_path, 'a') as f:
56 + fcntl.lockf(f.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
57 + except EnvironmentError as e:
58 + if e.errno == errno.EAGAIN:
59 + # Parent process holds lock, as expected.
60 + sys.exit(0)
61 +
62 + # Something went wrong.
63 + sys.exit(1)
64 +
65 + fd, lock_path = tempfile.mkstemp()
66 + try:
67 + try:
68 + fcntl.lockf(fd, fcntl.LOCK_EX)
69 + except EnvironmentError:
70 + pass
71 + else:
72 + proc = multiprocessing.Process(target=_test_lock,
73 + args=(fd, lock_path))
74 + proc.start()
75 + proc.join()
76 + if proc.exitcode == os.EX_OK:
77 + # Use fcntl.lockf becase the test passed.
78 + _lock_fn = fcntl.lockf
79 + return _lock_fn
80 + finally:
81 + os.close(fd)
82 + os.unlink(lock_path)
83 +
84 + # Fall back to fcntl.flock.
85 + _lock_fn = fcntl.flock
86 + return _lock_fn
87 +
88 +
89 _open_fds = set()
90
91 def _close_fds():
92 @@ -146,7 +191,7 @@ def lockfile(mypath, wantnewlockfile=0, unlinkfile=0,
93
94 # try for a non-blocking lock, if it's held, throw a message
95 # we're waiting on lockfile and use a blocking attempt.
96 - locking_method = portage._eintr_func_wrapper(_default_lock_fn)
97 + locking_method = portage._eintr_func_wrapper(_get_lock_fn())
98 try:
99 if "__PORTAGE_TEST_HARDLINK_LOCKS" in os.environ:
100 raise IOError(errno.ENOSYS, "Function not implemented")
101 --
102 2.7.4

Replies