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 )