diff --git a/msal_extensions/cache_lock.py b/msal_extensions/cache_lock.py index c4f8f3d..67ada29 100644 --- a/msal_extensions/cache_lock.py +++ b/msal_extensions/cache_lock.py @@ -2,6 +2,11 @@ import os import sys import errno +import time +import logging + +logger = logging.getLogger(__name__) + import portalocker from distutils.version import LooseVersion @@ -25,7 +30,26 @@ def __init__(self, lockfile_path): flags=portalocker.LOCK_EX | portalocker.LOCK_NB, **open_kwargs) + def try_to_create_lock_file(self): + timeout = 5 + check_interval = 0.25 + current_time = getattr(time, "monotonic", time.time) + timeout_end = current_time() + timeout + while timeout_end > current_time(): + try: + with open(self._lockpath, 'x'): + return True + except ValueError: # This needs to be the first clause, for Python 2 to hit it + logger.warning("Python 2 does not support atomic creation of file") + return False + except FileExistsError: # Only Python 3 will reach this clause + logger.warning("Lock file exists, trying again after some time") + time.sleep(check_interval) + return False + def __enter__(self): + if not self.try_to_create_lock_file(): + logger.warning("Failed to create lock file") file_handle = self._lock.__enter__() file_handle.write('{} {}'.format(os.getpid(), sys.argv[0]).encode('utf-8')) return file_handle diff --git a/tests/test_cache_lock_file_perf.py b/tests/test_cache_lock_file_perf.py index 757fb80..8bc1a1c 100644 --- a/tests/test_cache_lock_file_perf.py +++ b/tests/test_cache_lock_file_perf.py @@ -67,7 +67,7 @@ def test_lock_for_high_workload(temp_location): def test_lock_for_timeout(temp_location): - num_of_processes = 10 + num_of_processes = 30 sleep_interval = 1 _run_multiple_processes(num_of_processes, temp_location, sleep_interval) count = _validate_result_in_cache(temp_location)