xref: /aosp_15_r20/external/cronet/build/fuchsia/test/lockfile.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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