Index: git_cache.py |
diff --git a/git_cache.py b/git_cache.py |
index 9f835b5ffc144d8c6753c70585c09cea2250002b..9137ef5f58e27cc95a0e0a701693d61af364cba4 100755 |
--- a/git_cache.py |
+++ b/git_cache.py |
@@ -44,8 +44,9 @@ class RefsHeadsFailedToFetch(Exception): |
class Lockfile(object): |
"""Class to represent a cross-platform process-specific lockfile.""" |
- def __init__(self, path): |
+ def __init__(self, path, timeout=0): |
self.path = os.path.abspath(path) |
+ self.timeout = timeout |
self.lockfile = self.path + ".lock" |
self.pid = os.getpid() |
@@ -91,16 +92,26 @@ class Lockfile(object): |
def lock(self): |
"""Acquire the lock. |
- Note: This is a NON-BLOCKING FAIL-FAST operation. |
- Do. Or do not. There is no try. |
+ This will block with a deadline of self.timeout seconds. |
+ If self.timeout is zero, this is a NON-BLOCKING FAIL-FAST operation. |
""" |
- try: |
- self._make_lockfile() |
- except OSError as e: |
- if e.errno == errno.EEXIST: |
- raise LockError("%s is already locked" % self.path) |
- else: |
- raise LockError("Failed to create %s (err %s)" % (self.path, e.errno)) |
+ elapsed = 0 |
+ while True: |
+ try: |
+ self._make_lockfile() |
+ return |
+ except OSError as e: |
+ if elapsed < self.timeout: |
+ sleep_time = min(3, self.timeout - elapsed) |
+ logging.info('Could not create git cache lockfile; ' |
+ 'will retry after sleep(%d).', sleep_time); |
+ elapsed += sleep_time |
+ time.sleep(sleep_time) |
+ continue |
+ if e.errno == errno.EEXIST: |
+ raise LockError("%s is already locked" % self.path) |
+ else: |
+ raise LockError("Failed to create %s (err %s)" % (self.path, e.errno)) |
def unlock(self): |
"""Release the lock.""" |
@@ -401,13 +412,13 @@ class Mirror(object): |
logging.warn('Fetch of %s failed' % spec) |
def populate(self, depth=None, shallow=False, bootstrap=False, |
- verbose=False, ignore_lock=False): |
+ verbose=False, ignore_lock=False, lock_timeout=0): |
assert self.GetCachePath() |
if shallow and not depth: |
depth = 10000 |
gclient_utils.safe_makedirs(self.GetCachePath()) |
- lockfile = Lockfile(self.mirror_path) |
+ lockfile = Lockfile(self.mirror_path, lock_timeout) |
if not ignore_lock: |
lockfile.lock() |
@@ -582,6 +593,7 @@ def CMDpopulate(parser, args): |
'shallow': options.shallow, |
'bootstrap': not options.no_bootstrap, |
'ignore_lock': options.ignore_locks, |
+ 'lock_timeout': options.timeout, |
} |
if options.depth: |
kwargs['depth'] = options.depth |
@@ -625,7 +637,8 @@ def CMDfetch(parser, args): |
git_dir = os.path.abspath(git_dir) |
if git_dir.startswith(cachepath): |
mirror = Mirror.FromPath(git_dir) |
- mirror.populate(bootstrap=not options.no_bootstrap) |
+ mirror.populate( |
+ bootstrap=not options.no_bootstrap, lock_timeout=options.timeout) |
return 0 |
for remote in remotes: |
remote_url = subprocess.check_output( |
@@ -634,7 +647,8 @@ def CMDfetch(parser, args): |
mirror = Mirror.FromPath(remote_url) |
mirror.print = lambda *args: None |
print('Updating git cache...') |
- mirror.populate(bootstrap=not options.no_bootstrap) |
+ mirror.populate( |
+ bootstrap=not options.no_bootstrap, lock_timeout=options.timeout) |
subprocess.check_call([Mirror.git_exe, 'fetch', remote]) |
return 0 |
@@ -683,6 +697,8 @@ class OptionParser(optparse.OptionParser): |
help='Increase verbosity (can be passed multiple times)') |
self.add_option('-q', '--quiet', action='store_true', |
help='Suppress all extraneous output') |
+ self.add_option('--timeout', type='int', default=0, |
+ help='Timeout for acquiring cache lock, in seconds') |
def parse_args(self, args=None, values=None): |
options, args = optparse.OptionParser.parse_args(self, args, values) |