Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/virtualenv/util/lock.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 """holds locking functionality that works across processes""" | |
2 from __future__ import absolute_import, unicode_literals | |
3 | |
4 import logging | |
5 import os | |
6 from abc import ABCMeta, abstractmethod | |
7 from contextlib import contextmanager | |
8 from threading import Lock, RLock | |
9 | |
10 from filelock import FileLock, Timeout | |
11 from six import add_metaclass | |
12 | |
13 from virtualenv.util.path import Path | |
14 | |
15 | |
16 class _CountedFileLock(FileLock): | |
17 def __init__(self, lock_file): | |
18 parent = os.path.dirname(lock_file) | |
19 if not os.path.isdir(parent): | |
20 try: | |
21 os.makedirs(parent) | |
22 except OSError: | |
23 pass | |
24 super(_CountedFileLock, self).__init__(lock_file) | |
25 self.count = 0 | |
26 self.thread_safe = RLock() | |
27 | |
28 def acquire(self, timeout=None, poll_intervall=0.05): | |
29 with self.thread_safe: | |
30 if self.count == 0: | |
31 super(_CountedFileLock, self).acquire(timeout=timeout, poll_intervall=poll_intervall) | |
32 self.count += 1 | |
33 | |
34 def release(self, force=False): | |
35 with self.thread_safe: | |
36 if self.count == 1: | |
37 super(_CountedFileLock, self).release(force=force) | |
38 self.count = max(self.count - 1, 0) | |
39 | |
40 | |
41 _lock_store = {} | |
42 _store_lock = Lock() | |
43 | |
44 | |
45 @add_metaclass(ABCMeta) | |
46 class PathLockBase(object): | |
47 def __init__(self, folder): | |
48 path = Path(folder) | |
49 self.path = path.resolve() if path.exists() else path | |
50 | |
51 def __repr__(self): | |
52 return "{}({})".format(self.__class__.__name__, self.path) | |
53 | |
54 def __div__(self, other): | |
55 return type(self)(self.path / other) | |
56 | |
57 def __truediv__(self, other): | |
58 return self.__div__(other) | |
59 | |
60 @abstractmethod | |
61 def __enter__(self): | |
62 raise NotImplementedError | |
63 | |
64 @abstractmethod | |
65 def __exit__(self, exc_type, exc_val, exc_tb): | |
66 raise NotImplementedError | |
67 | |
68 @abstractmethod | |
69 @contextmanager | |
70 def lock_for_key(self, name, no_block=False): | |
71 raise NotImplementedError | |
72 | |
73 @abstractmethod | |
74 @contextmanager | |
75 def non_reentrant_lock_for_key(name): | |
76 raise NotImplementedError | |
77 | |
78 | |
79 class ReentrantFileLock(PathLockBase): | |
80 def __init__(self, folder): | |
81 super(ReentrantFileLock, self).__init__(folder) | |
82 self._lock = None | |
83 | |
84 def _create_lock(self, name=""): | |
85 lock_file = str(self.path / "{}.lock".format(name)) | |
86 with _store_lock: | |
87 if lock_file not in _lock_store: | |
88 _lock_store[lock_file] = _CountedFileLock(lock_file) | |
89 return _lock_store[lock_file] | |
90 | |
91 @staticmethod | |
92 def _del_lock(lock): | |
93 with _store_lock: | |
94 if lock is not None: | |
95 with lock.thread_safe: | |
96 if lock.count == 0: | |
97 _lock_store.pop(lock.lock_file, None) | |
98 | |
99 def __del__(self): | |
100 self._del_lock(self._lock) | |
101 | |
102 def __enter__(self): | |
103 self._lock = self._create_lock() | |
104 self._lock_file(self._lock) | |
105 | |
106 def __exit__(self, exc_type, exc_val, exc_tb): | |
107 self._release(self._lock) | |
108 | |
109 def _lock_file(self, lock, no_block=False): | |
110 # multiple processes might be trying to get a first lock... so we cannot check if this directory exist without | |
111 # a lock, but that lock might then become expensive, and it's not clear where that lock should live. | |
112 # Instead here we just ignore if we fail to create the directory. | |
113 try: | |
114 os.makedirs(str(self.path)) | |
115 except OSError: | |
116 pass | |
117 try: | |
118 lock.acquire(0.0001) | |
119 except Timeout: | |
120 if no_block: | |
121 raise | |
122 logging.debug("lock file %s present, will block until released", lock.lock_file) | |
123 lock.release() # release the acquire try from above | |
124 lock.acquire() | |
125 | |
126 @staticmethod | |
127 def _release(lock): | |
128 lock.release() | |
129 | |
130 @contextmanager | |
131 def lock_for_key(self, name, no_block=False): | |
132 lock = self._create_lock(name) | |
133 try: | |
134 try: | |
135 self._lock_file(lock, no_block) | |
136 yield | |
137 finally: | |
138 self._release(lock) | |
139 finally: | |
140 self._del_lock(lock) | |
141 | |
142 @contextmanager | |
143 def non_reentrant_lock_for_key(self, name): | |
144 with _CountedFileLock(str(self.path / "{}.lock".format(name))): | |
145 yield | |
146 | |
147 | |
148 class NoOpFileLock(PathLockBase): | |
149 def __enter__(self): | |
150 raise NotImplementedError | |
151 | |
152 def __exit__(self, exc_type, exc_val, exc_tb): | |
153 raise NotImplementedError | |
154 | |
155 @contextmanager | |
156 def lock_for_key(self, name, no_block=False): | |
157 yield | |
158 | |
159 @contextmanager | |
160 def non_reentrant_lock_for_key(self, name): | |
161 yield | |
162 | |
163 | |
164 __all__ = ( | |
165 "NoOpFileLock", | |
166 "ReentrantFileLock", | |
167 "Timeout", | |
168 ) |