Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/virtualenv/util/lock.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
| author | shellac |
|---|---|
| date | Mon, 01 Jun 2020 08:59:25 -0400 |
| parents | 79f47841a781 |
| children |
comparison
equal
deleted
inserted
replaced
| 4:79f47841a781 | 5:9b1c78e6ba9c |
|---|---|
| 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) |
