xref: /aosp_15_r20/external/angle/build/fuchsia/test/lockfile.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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