comparison env/lib/python3.7/site-packages/virtualenv/util/lock.py @ 0:26e78fe6e8c4 draft

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