conans/util/locks.py
import os
import time
import fasteners
from conans.util.files import load, save
from conans.util.log import logger
class NoLock(object):
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable
pass
class SimpleLock(object):
def __init__(self, filename):
self._lock = fasteners.InterProcessLock(filename, logger=logger)
def __enter__(self):
self._lock.acquire()
def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable
self._lock.release()
READ_BUSY_DELAY = 0.5
WRITE_BUSY_DELAY = 0.25
class Lock(object):
@staticmethod
def clean(folder):
if os.path.exists(folder + ".count"):
os.remove(folder + ".count")
if os.path.exists(folder + ".count.lock"):
os.remove(folder + ".count.lock")
def __init__(self, folder, locked_item, output):
self._count_file = folder + ".count"
self._count_lock_file = folder + ".count.lock"
self._locked_item = locked_item
self._output = output
self._first_lock = True
@property
def files(self):
return self._count_file, self._count_lock_file
def _info_locked(self):
if self._first_lock:
self._first_lock = False
self._output.info("%s is locked by another concurrent conan process, wait..."
% str(self._locked_item))
self._output.info("If not the case, quit, and do 'conan remove --locks'")
def _readers(self):
try:
return int(load(self._count_file))
except IOError:
return 0
except (UnicodeEncodeError, ValueError):
self._output.warn("%s does not contain a number!" % self._count_file)
return 0
class ReadLock(Lock):
def __enter__(self):
while True:
with fasteners.InterProcessLock(self._count_lock_file, logger=logger):
readers = self._readers()
if readers >= 0:
save(self._count_file, str(readers + 1))
break
self._info_locked()
time.sleep(READ_BUSY_DELAY)
def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable
with fasteners.InterProcessLock(self._count_lock_file, logger=logger):
readers = self._readers()
save(self._count_file, str(readers - 1))
class WriteLock(Lock):
def __enter__(self):
while True:
with fasteners.InterProcessLock(self._count_lock_file, logger=logger):
readers = self._readers()
if readers == 0:
save(self._count_file, "-1")
break
self._info_locked()
time.sleep(WRITE_BUSY_DELAY)
def __exit__(self, exc_type, exc_val, exc_tb): # @UnusedVariable
with fasteners.InterProcessLock(self._count_lock_file, logger=logger):
save(self._count_file, "0")
if exc_type is not None:
# If there was an exception while locking this, might be empty
# Try to clean up the trailing filelocks
try:
os.remove(self._count_file)
os.remove(self._count_lock_file)
path = os.path.dirname(self._count_file)
for _ in range(3):
try: # Take advantage that os.rmdir does not delete non-empty dirs
os.rmdir(path)
except Exception:
break # not empty
path = os.path.dirname(path)
except Exception:
pass