def test_writer_lock_initially_blocked_by_readers(self): """ When a reader lock is already held, another thread/process should not be able to acquire the lock for writing until the reader releases its lock """ lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a reader lock is already acquired assert lock.acquire_read_lock() is True child_proc_function = dummy_fn_requiring_writer_lock timeout = 1 attempts = 4 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same lock for writing, failing after 4 seconds res = pool.starmap_async(child_writer_dispatch, [(child_proc_function, lock_path, timeout, attempts)]) # When: we verify that the writer lock is not immediately acquired with pytest.raises(TimeoutError): res.get(timeout=1) # When: the reader releases its lock lock.release_read_lock() # Expect: the child process to acquire the writer lock result = res.get(timeout=10) # 10 second timeout to prevent a bad test from spoiling the fun assert result[0] == "A winner is you!"
def read_cache_data(cachename): """Reads data from a cache file. :param cachename: """ lock = fasteners.InterProcessReaderWriterLock(cachename + ".lock") with lock.read_lock(): LOG.debug("Reading cache file: %s", cachename) try: with open(cachename, 'rb') as cache_fd: try: data = pickle.load(cache_fd) except pickle.PickleError as exc: raise MicroprobeCacheError(exc) except EOFError as exc: raise MicroprobeCacheError(exc) except AttributeError as exc: raise MicroprobeCacheError(exc) except TypeError as exc: raise MicroprobeCacheError(exc) except ValueError as exc: raise MicroprobeCacheError(exc) except ModuleNotFoundError as exc: raise MicroprobeCacheError(exc) except FileNotFoundError as exc: raise MicroprobeCacheError(exc) except IOError as exc: raise MicroprobeCacheError(exc) return data
def test_writer_lock_timeout(self): """ If a writer lock is already held, another process should not be able to acquire the same lock, failing after n attempts """ lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_fn = dummy_fn_requiring_writer_lock timeout = 0.25 attempts = 4 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second res = pool.starmap_async(child_writer_dispatch, [(child_proc_fn, lock_path, timeout, attempts)]) # Expect: the child to fail acquiring the writer lock (raises as FileKeyringLockTimeout) with pytest.raises(FileKeyringLockTimeout): # 10 second timeout to prevent a bad test from spoiling the fun (raises as TimeoutException) res.get(timeout=10) lock.release_write_lock()
def test_writer_lock_released_on_abort(self): """ When a child process is holding the lock and aborts/crashes, we should be able to acquire the lock """ # Avoid running on macOS: calling abort() triggers the CrashReporter prompt, interfering with automated testing if platform == "darwin": return lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_function = dummy_abort_fn timeout = 0.25 attempts = 4 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second res = pool.starmap_async(child_writer_dispatch, [(child_proc_function, lock_path, timeout, attempts)]) # When: the writer lock is released lock.release_write_lock() # When: timing out waiting for the child process (because it aborted) with pytest.raises(TimeoutError): res.get(timeout=1) # Expect: Reacquiring the lock should succeed after the child exits, automatically releasing the lock assert lock.acquire_write_lock(timeout=(1)) is True
def test_writer_lock_reacquisition_failure(self): """ After the child process acquires the writer lock (and sleeps), the previous holder should not be able to quickly reacquire the lock """ lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_function = dummy_sleep_fn # Sleeps for DUMMY_SLEEP_VALUE seconds timeout = 0.25 attempts = 8 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second pool.starmap_async(child_writer_dispatch, [(child_proc_function, lock_path, timeout, attempts)]) # When: the writer lock is released lock.release_write_lock() # Brief delay to allow the child to acquire the lock sleep(1) # Expect: Reacquiring the lock should fail due to the child holding the lock and sleeping assert lock.acquire_write_lock(timeout=0.25) is False
def test_writer_lock_succeeds(self): """ If a write lock is already held, another process will be able to acquire the same lock once the lock is released by the current holder """ lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_fn = dummy_fn_requiring_writer_lock timeout = 0.25 attempts = 4 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second res = pool.starmap_async(child_writer_dispatch, [(child_proc_fn, lock_path, timeout, attempts)]) # Brief delay to allow the child to timeout once sleep(0.25) # When: the writer lock is released lock.release_write_lock() # Expect: the child to acquire the writer lock result = res.get(timeout=10) # 10 second timeout to prevent a bad test from spoiling the fun assert result[0] == "A winner is you!"
def test_writer_lock_initially_blocked_by_readers(self, ready_dir: Path, finished_dir: Path): """ When a reader lock is already held, another thread/process should not be able to acquire the lock for writing until the reader releases its lock """ lock_path = FileKeyring.lockfile_path_for_file_path( KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a reader lock is already acquired assert lock.acquire_read_lock() is True child_proc_function = dummy_fn_requiring_writer_lock timeout = 1 attempts = 10 num_workers = 1 with Pool(processes=num_workers) as pool: # When: a child process attempts to acquire the same lock for writing, failing after 4 seconds res = pool.starmap_async( child_writer_dispatch_with_readiness_check, [(child_proc_function, lock_path, timeout, attempts, ready_dir, finished_dir)], ) # Wait up to 30 seconds for all processes to indicate readiness assert poll_directory(ready_dir, num_workers, 30) is True log.warning(f"Test setup complete: {num_workers} workers ready") # Signal that testing should begin start_file_path: Path = ready_dir / "start" with open(start_file_path, "w") as f: f.write(f"{os.getpid()}\n") # When: we verify that the writer lock is not immediately acquired with pytest.raises(TimeoutError): res.get(timeout=5) # When: the reader releases its lock lock.release_read_lock() # Wait up to 30 seconds for all processes to indicate completion assert poll_directory(finished_dir, num_workers, 30) is True log.warning(f"Finished: {num_workers} workers finished") # Expect: the child process to acquire the writer lock result = res.get( timeout=10 ) # 10 second timeout to prevent a bad test from spoiling the fun assert result[0] == "A winner is you!"
def test_writer_lock_succeeds(self, ready_dir: Path, finished_dir: Path): """ If a write lock is already held, another process will be able to acquire the same lock once the lock is released by the current holder """ lock_path = FileKeyring.lockfile_path_for_file_path( KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_fn = dummy_fn_requiring_writer_lock timeout = 0.25 attempts = 8 num_workers = 1 with Pool(processes=num_workers) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second res = pool.starmap_async( child_writer_dispatch_with_readiness_check, [(child_proc_fn, lock_path, timeout, attempts, ready_dir, finished_dir)], ) # Wait up to 30 seconds for all processes to indicate readiness assert poll_directory(ready_dir, num_workers, 30) is True log.warning(f"Test setup complete: {num_workers} workers ready") # Signal that testing should begin start_file_path: Path = ready_dir / "start" with open(start_file_path, "w") as f: f.write(f"{os.getpid()}\n") # Brief delay to allow the child to timeout once sleep(0.50) # When: the writer lock is released lock.release_write_lock() # Expect: the child to acquire the writer lock result = res.get( timeout=10 ) # 10 second timeout to prevent a bad test from spoiling the fun assert result[0] == "A winner is you!" # Wait up to 30 seconds for all processes to indicate completion assert poll_directory(finished_dir, num_workers, 30) is True log.warning(f"Finished: {num_workers} workers finished")
def test_writer_lock_reacquisition_failure(self, ready_dir: Path, finished_dir: Path): """ After the child process acquires the writer lock (and sleeps), the previous holder should not be able to quickly reacquire the lock """ lock_path = FileKeyring.lockfile_path_for_file_path( KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_function = dummy_sleep_fn # Sleeps for DUMMY_SLEEP_VALUE seconds timeout = 0.25 attempts = 8 num_workers = 1 with Pool(processes=num_workers) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second pool.starmap_async( child_writer_dispatch_with_readiness_check, [(child_proc_function, lock_path, timeout, attempts, ready_dir, finished_dir)], ) # Wait up to 30 seconds for all processes to indicate readiness assert poll_directory(ready_dir, num_workers, 30) is True log.warning(f"Test setup complete: {num_workers} workers ready") # Signal that testing should begin start_file_path: Path = ready_dir / "start" with open(start_file_path, "w") as f: f.write(f"{os.getpid()}\n") # When: the writer lock is released lock.release_write_lock() # Brief delay to allow the child to acquire the lock sleep(1) # Expect: Reacquiring the lock should fail due to the child holding the lock and sleeping assert lock.acquire_write_lock(timeout=0.25) is False # Wait up to 30 seconds for all processes to indicate completion assert poll_directory(finished_dir, num_workers, 30) is True log.warning(f"Finished: {num_workers} workers finished")
def test_writer_lock_blocked_by_readers(self, ready_dir: Path, finished_dir: Path): """ When a reader lock is already held, another thread/process should not be able to acquire the lock for writing """ lock_path = FileKeyring.lockfile_path_for_file_path( KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a reader lock is already held lock.acquire_read_lock() child_proc_function = dummy_fn_requiring_writer_lock timeout = 0.25 attempts = 4 num_workers = 1 with Pool(processes=num_workers) as pool: # When: a child process attempts to acquire the same lock for writing, failing after 1 second res = pool.starmap_async( child_writer_dispatch_with_readiness_check, [(child_proc_function, lock_path, timeout, attempts, ready_dir, finished_dir)], ) # Wait up to 30 seconds for all processes to indicate readiness assert poll_directory(ready_dir, num_workers, 30) is True log.warning(f"Test setup complete: {num_workers} workers ready") # Signal that testing should begin start_file_path: Path = ready_dir / "start" with open(start_file_path, "w") as f: f.write(f"{os.getpid()}\n") # Wait up to 30 seconds for all processes to indicate completion assert poll_directory(finished_dir, num_workers, 30) is True log.warning(f"Finished: {num_workers} workers finished") # Expect: lock acquisition times out (raises as FileKeyringLockTimeout) with pytest.raises(FileKeyringLockTimeout): res.get(timeout=30) lock.release_read_lock()
def acquire_reader_lock(lock_path: Path, timeout=5, max_iters=6): lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) result = None for i in range(0, max_iters): if lock.acquire_read_lock(timeout=timeout): yield # <---- lock.release_read_lock() break else: print( f"Failed to acquire keyring reader lock after {timeout} seconds.", end="") if i < max_iters - 1: print(f" Remaining attempts: {max_iters - 1 - i}") else: print("") raise FileKeyringLockTimeout( "Exhausted all attempts to acquire the writer lock") return result
def write_cache_data(filename, data, data_reload=False): """Writes data to a cache file. :param filename: :param data: """ LOG.debug("Writing cache file: %s", filename) if data_reload: try: base_data = read_cache_data(filename) except MicroprobeCacheError as exc: base_data = None if base_data is not None: if type(base_data) != type(data): pass if isinstance(base_data, dict): # TODO: Control cache size of dictionary caches data.update(base_data) elif isinstance(base_data, cachetools.LRUCache): pass else: raise NotImplementedError lock = fasteners.InterProcessReaderWriterLock(filename + ".lock") if lock.acquire_write_lock(timeout=10): try: with open(filename, 'wb') as cache_fd: pickle.dump(data, cache_fd, protocol=pickle.HIGHEST_PROTOCOL) except IOError: # Unable to create cache files, disabling cache MICROPROBE_RC['no_cache'] = True except pickle.PickleError as exc: raise MicroprobeCacheError(exc) except EOFError as exc: raise MicroprobeCacheError(exc)
def test_writer_lock_blocked_by_readers(self): """ When a reader lock is already held, another thread/process should not be able to acquire the lock for writing """ lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a reader lock is already held lock.acquire_read_lock() child_proc_function = dummy_fn_requiring_writer_lock timeout = 0.25 attempts = 4 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same lock for writing, failing after 1 second res = pool.starmap_async(child_writer_dispatch, [(child_proc_function, lock_path, timeout, attempts)]) # Expect: lock acquisition times out (raises as FileKeyringLockTimeout) with pytest.raises(FileKeyringLockTimeout): res.get(timeout=2)
def test_writer_lock_reacquisition_success(self): """ After the child process releases the writer lock, we should be able to acquire the lock """ lock_path = FileKeyring.lockfile_path_for_file_path(KeyringWrapper.get_shared_instance().keyring.keyring_path) lock = fasteners.InterProcessReaderWriterLock(str(lock_path)) # When: a writer lock is already acquired lock.acquire_write_lock() child_proc_function = dummy_sleep_fn # Sleeps for DUMMY_SLEEP_VALUE seconds timeout = 0.25 attempts = 4 with Pool(processes=1) as pool: # When: a child process attempts to acquire the same writer lock, failing after 1 second pool.starmap_async(child_writer_dispatch, [(child_proc_function, lock_path, timeout, attempts)]) # When: the writer lock is released lock.release_write_lock() # Expect: Reacquiring the lock should succeed after the child finishes and releases the lock assert lock.acquire_write_lock(timeout=(DUMMY_SLEEP_VALUE + 0.25)) is True
} BUILD_SHARED_LIBS = {'static': 'off', 'shared': 'on'} LLVM_PROJECT_LABELS = [ 'llvm_project_sha1', 'llvm_project_sha1_date', 'llvm_project_dockerfile_sha1' ] GITHUB_REPO_NAME = github_repo_name.upper() GITHUB_REPO_NAME2 = github_repo_name2.upper() DOCKER_DIST_MANIFESTS = { 'v1': 'application/vnd.docker.distribution.manifest.v1+json', 'v2': 'application/vnd.docker.distribution.manifest.v2+json' } docker_rwlock = fasteners.InterProcessReaderWriterLock(docker_pushpull_rwlock) docker_api = docker.APIClient(base_url=docker_daemon_socket) # Validate whether the commit sha1 date is a valid UTC ISO 8601 date def valid_sha1_date(sha1_date): try: datetime.datetime.strptime(sha1_date, '%Y-%m-%dT%H:%M:%SZ') return True except: return False # Extract a regex pattern from a file. Used to get llvm-project sha1 # from utils/clone-mlir.sh. def extract_pattern_from_file(file_name, regex_pattern):
def __init__(self, path: PathLikeFrom) -> None: """Creates a lockfile at path.""" super().__init__() self.path = pathlike_from(path) self._rw_lock = fasteners.InterProcessReaderWriterLock( cast(Path, self.path))