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