Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/virtualenv/util/lock.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
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 contextlib import contextmanager | |
7 from threading import Lock, RLock | |
8 | |
9 from filelock import FileLock, Timeout | |
10 | |
11 from virtualenv.util.path import Path | |
12 | |
13 | |
14 class _CountedFileLock(FileLock): | |
15 def __init__(self, lock_file): | |
16 parent = os.path.dirname(lock_file) | |
17 if not os.path.exists(parent): | |
18 try: | |
19 os.makedirs(parent) | |
20 except OSError: | |
21 pass | |
22 super(_CountedFileLock, self).__init__(lock_file) | |
23 self.count = 0 | |
24 self.thread_safe = RLock() | |
25 | |
26 def acquire(self, timeout=None, poll_intervall=0.05): | |
27 with self.thread_safe: | |
28 if self.count == 0: | |
29 super(_CountedFileLock, self).acquire(timeout=timeout, poll_intervall=poll_intervall) | |
30 self.count += 1 | |
31 | |
32 def release(self, force=False): | |
33 with self.thread_safe: | |
34 if self.count == 1: | |
35 super(_CountedFileLock, self).release() | |
36 self.count = max(self.count - 1, 0) | |
37 | |
38 | |
39 _lock_store = {} | |
40 _store_lock = Lock() | |
41 | |
42 | |
43 class ReentrantFileLock(object): | |
44 def __init__(self, folder): | |
45 self._lock = None | |
46 path = Path(folder) | |
47 self.path = path.resolve() if path.exists() else path | |
48 | |
49 def __repr__(self): | |
50 return "{}({})".format(self.__class__.__name__, self.path) | |
51 | |
52 def __div__(self, other): | |
53 return ReentrantFileLock(self.path / other) | |
54 | |
55 def __truediv__(self, other): | |
56 return self.__div__(other) | |
57 | |
58 def _create_lock(self, name=""): | |
59 lock_file = str(self.path / "{}.lock".format(name)) | |
60 with _store_lock: | |
61 if lock_file not in _lock_store: | |
62 _lock_store[lock_file] = _CountedFileLock(lock_file) | |
63 return _lock_store[lock_file] | |
64 | |
65 @staticmethod | |
66 def _del_lock(lock): | |
67 with _store_lock: | |
68 if lock is not None: | |
69 with lock.thread_safe: | |
70 if lock.count == 0: | |
71 _lock_store.pop(lock.lock_file, None) | |
72 | |
73 def __del__(self): | |
74 self._del_lock(self._lock) | |
75 | |
76 def __enter__(self): | |
77 self._lock = self._create_lock() | |
78 self._lock_file(self._lock) | |
79 | |
80 def __exit__(self, exc_type, exc_val, exc_tb): | |
81 self._release(self._lock) | |
82 | |
83 def _lock_file(self, lock, no_block=False): | |
84 # multiple processes might be trying to get a first lock... so we cannot check if this directory exist without | |
85 # a lock, but that lock might then become expensive, and it's not clear where that lock should live. | |
86 # Instead here we just ignore if we fail to create the directory. | |
87 try: | |
88 os.makedirs(str(self.path)) | |
89 except OSError: | |
90 pass | |
91 try: | |
92 lock.acquire(0.0001) | |
93 except Timeout: | |
94 if no_block: | |
95 raise | |
96 logging.debug("lock file %s present, will block until released", lock.lock_file) | |
97 lock.release() # release the acquire try from above | |
98 lock.acquire() | |
99 | |
100 @staticmethod | |
101 def _release(lock): | |
102 lock.release() | |
103 | |
104 @contextmanager | |
105 def lock_for_key(self, name, no_block=False): | |
106 lock = self._create_lock(name) | |
107 try: | |
108 try: | |
109 self._lock_file(lock, no_block) | |
110 yield | |
111 finally: | |
112 self._release(lock) | |
113 finally: | |
114 self._del_lock(lock) | |
115 | |
116 | |
117 __all__ = ( | |
118 "Timeout", | |
119 "ReentrantFileLock", | |
120 ) |