tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lockfile.py (1941B)


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