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