1*6777b538SAndroid Build Coastguard Worker# Copyright 2023 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker"""Exclusive filelocking for all supported platforms. 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard WorkerCopied from third_party/depot_tools/lockfile.py. 7*6777b538SAndroid Build Coastguard Worker""" 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard Workerimport contextlib 10*6777b538SAndroid Build Coastguard Workerimport fcntl 11*6777b538SAndroid Build Coastguard Workerimport logging 12*6777b538SAndroid Build Coastguard Workerimport os 13*6777b538SAndroid Build Coastguard Workerimport time 14*6777b538SAndroid Build Coastguard Worker 15*6777b538SAndroid Build Coastguard Worker 16*6777b538SAndroid Build Coastguard Workerclass LockError(Exception): 17*6777b538SAndroid Build Coastguard Worker """Error raised if timeout or lock (without timeout) fails.""" 18*6777b538SAndroid Build Coastguard Worker 19*6777b538SAndroid Build Coastguard Worker 20*6777b538SAndroid Build Coastguard Workerdef _open_file(lockfile): 21*6777b538SAndroid Build Coastguard Worker open_flags = (os.O_CREAT | os.O_WRONLY) 22*6777b538SAndroid Build Coastguard Worker return os.open(lockfile, open_flags, 0o644) 23*6777b538SAndroid Build Coastguard Worker 24*6777b538SAndroid Build Coastguard Worker 25*6777b538SAndroid Build Coastguard Workerdef _close_file(file_descriptor): 26*6777b538SAndroid Build Coastguard Worker os.close(file_descriptor) 27*6777b538SAndroid Build Coastguard Worker 28*6777b538SAndroid Build Coastguard Worker 29*6777b538SAndroid Build Coastguard Workerdef _lock_file(file_descriptor): 30*6777b538SAndroid Build Coastguard Worker fcntl.flock(file_descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB) 31*6777b538SAndroid Build Coastguard Worker 32*6777b538SAndroid Build Coastguard Worker 33*6777b538SAndroid Build Coastguard Workerdef _try_lock(lockfile): 34*6777b538SAndroid Build Coastguard Worker f = _open_file(lockfile) 35*6777b538SAndroid Build Coastguard Worker try: 36*6777b538SAndroid Build Coastguard Worker _lock_file(f) 37*6777b538SAndroid Build Coastguard Worker except Exception: 38*6777b538SAndroid Build Coastguard Worker _close_file(f) 39*6777b538SAndroid Build Coastguard Worker raise 40*6777b538SAndroid Build Coastguard Worker return lambda: _close_file(f) 41*6777b538SAndroid Build Coastguard Worker 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Workerdef _lock(path, timeout=0): 44*6777b538SAndroid Build Coastguard Worker """_lock returns function to release the lock if locking was successful. 45*6777b538SAndroid Build Coastguard Worker 46*6777b538SAndroid Build Coastguard Worker _lock also implements simple retry logic.""" 47*6777b538SAndroid Build Coastguard Worker elapsed = 0 48*6777b538SAndroid Build Coastguard Worker while True: 49*6777b538SAndroid Build Coastguard Worker try: 50*6777b538SAndroid Build Coastguard Worker return _try_lock(path + '.locked') 51*6777b538SAndroid Build Coastguard Worker except (OSError, IOError) as error: 52*6777b538SAndroid Build Coastguard Worker if elapsed < timeout: 53*6777b538SAndroid Build Coastguard Worker sleep_time = min(10, timeout - elapsed) 54*6777b538SAndroid Build Coastguard Worker logging.info( 55*6777b538SAndroid Build Coastguard Worker 'Could not create lockfile; will retry after sleep(%d).', 56*6777b538SAndroid Build Coastguard Worker sleep_time) 57*6777b538SAndroid Build Coastguard Worker elapsed += sleep_time 58*6777b538SAndroid Build Coastguard Worker time.sleep(sleep_time) 59*6777b538SAndroid Build Coastguard Worker continue 60*6777b538SAndroid Build Coastguard Worker raise LockError("Error locking %s (err: %s)" % 61*6777b538SAndroid Build Coastguard Worker (path, str(error))) from error 62*6777b538SAndroid Build Coastguard Worker 63*6777b538SAndroid Build Coastguard Worker 64*6777b538SAndroid Build Coastguard Worker@contextlib.contextmanager 65*6777b538SAndroid Build Coastguard Workerdef lock(path, timeout=0): 66*6777b538SAndroid Build Coastguard Worker """Get exclusive lock to path. 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Worker Usage: 69*6777b538SAndroid Build Coastguard Worker import lockfile 70*6777b538SAndroid Build Coastguard Worker with lockfile.lock(path, timeout): 71*6777b538SAndroid Build Coastguard Worker # Do something 72*6777b538SAndroid Build Coastguard Worker pass 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Worker """ 75*6777b538SAndroid Build Coastguard Worker release_fn = _lock(path, timeout) 76*6777b538SAndroid Build Coastguard Worker try: 77*6777b538SAndroid Build Coastguard Worker yield 78*6777b538SAndroid Build Coastguard Worker finally: 79*6777b538SAndroid Build Coastguard Worker release_fn() 80