utils: Replace BuildDirLock with generic DirectoryLock

DirectoryLock provides a generic locking implementation the replaces the
previously used BuildDirLock.
pull/13854/head
Florian "sp1rit"​ 4 months ago
parent a608e56beb
commit 43254a5e55
No known key found for this signature in database
GPG Key ID: B1F4055D8460CE34
  1. 1
      .gitignore
  2. 4
      mesonbuild/msetup.py
  3. 22
      mesonbuild/utils/platform.py
  4. 31
      mesonbuild/utils/posix.py
  5. 29
      mesonbuild/utils/win32.py
  6. 7
      unittests/allplatformstests.py

1
.gitignore vendored

@ -22,6 +22,7 @@ __pycache__
*~
*.swp
packagecache
.wraplock
/MANIFEST
/build
/dist

@ -184,7 +184,9 @@ class MesonApp:
mlog.set_timestamp_start(time.monotonic())
if self.options.clearcache:
env.coredata.clear_cache()
with mesonlib.BuildDirLock(self.build_dir):
with mesonlib.DirectoryLock(self.build_dir, 'meson-private/meson.lock',
mesonlib.DirectoryLockAction.FAIL,
'Some other Meson process is already using this build directory. Exiting.'):
return self._generate(env, capture, vslite_ctx)
def _generate(self, env: environment.Environment, capture: bool, vslite_ctx: T.Optional[dict]) -> T.Optional[dict]:

@ -6,22 +6,30 @@ from __future__ import annotations
"""base classes providing no-op functionality.."""
import enum
import os
import typing as T
from .. import mlog
__all__ = ['BuildDirLock']
__all__ = ['DirectoryLock', 'DirectoryLockAction', 'DirectoryLockBase']
# This needs to be inherited by the specific implementations to make type
# checking happy
class BuildDirLock:
class DirectoryLockAction(enum.Enum):
IGNORE = 0
WAIT = 1
FAIL = 2
def __init__(self, builddir: str) -> None:
self.lockfilename = os.path.join(builddir, 'meson-private/meson.lock')
class DirectoryLockBase:
def __init__(self, directory: str, lockfile: str, action: DirectoryLockAction, err: str) -> None:
self.action = action
self.err = err
self.lockpath = os.path.join(directory, lockfile)
def __enter__(self) -> None:
mlog.debug('Calling the no-op version of BuildDirLock')
mlog.debug('Calling the no-op version of DirectoryLock')
def __exit__(self, *args: T.Any) -> None:
pass
class DirectoryLock(DirectoryLockBase):
pass

@ -10,23 +10,38 @@ import fcntl
import typing as T
from .core import MesonException
from .platform import BuildDirLock as BuildDirLockBase
from .platform import DirectoryLockBase, DirectoryLockAction
__all__ = ['BuildDirLock']
__all__ = ['DirectoryLock', 'DirectoryLockAction']
class BuildDirLock(BuildDirLockBase):
class DirectoryLock(DirectoryLockBase):
def __enter__(self) -> None:
self.lockfile = open(self.lockfilename, 'w', encoding='utf-8')
try:
fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
except (BlockingIOError, PermissionError):
self.lockfile = open(self.lockpath, 'w+', encoding='utf-8')
except FileNotFoundError:
self.lockfile = None
return
try:
flags = fcntl.LOCK_EX
if self.action != DirectoryLockAction.WAIT:
flags = flags | fcntl.LOCK_NB
fcntl.flock(self.lockfile, flags)
except BlockingIOError:
self.lockfile.close()
if self.action == DirectoryLockAction.IGNORE:
return
raise MesonException(self.err)
except PermissionError:
self.lockfile.close()
raise MesonException('Some other Meson process is already using this build directory. Exiting.')
raise MesonException(self.err)
except OSError as e:
self.lockfile.close()
raise MesonException(f'Failed to lock the build directory: {e.strerror}')
raise MesonException(f'Failed to lock directory {self.lockpath}: {e.strerror}')
def __exit__(self, *args: T.Any) -> None:
if self.lockfile is None or self.lockfile.closed:
return
fcntl.flock(self.lockfile, fcntl.LOCK_UN)
self.lockfile.close()

@ -10,20 +10,35 @@ import msvcrt
import typing as T
from .core import MesonException
from .platform import BuildDirLock as BuildDirLockBase
from .platform import DirectoryLockBase, DirectoryLockAction
__all__ = ['BuildDirLock']
__all__ = ['DirectoryLock', 'DirectoryLockAction']
class BuildDirLock(BuildDirLockBase):
class DirectoryLock(DirectoryLockBase):
def __enter__(self) -> None:
self.lockfile = open(self.lockfilename, 'w', encoding='utf-8')
try:
msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
except (BlockingIOError, PermissionError):
self.lockfile = open(self.lockpath, 'w+', encoding='utf-8')
except FileNotFoundError:
self.lockfile = None
return
try:
mode = msvcrt.LK_LOCK
if self.action != DirectoryLockAction.WAIT:
mode = msvcrt.LK_NBLCK
msvcrt.locking(self.lockfile.fileno(), mode, 1)
except BlockingIOError:
self.lockfile.close()
if self.action == DirectoryLockAction.IGNORE:
return
raise MesonException(self.err)
except PermissionError:
self.lockfile.close()
raise MesonException('Some other Meson process is already using this build directory. Exiting.')
raise MesonException(self.err)
def __exit__(self, *args: T.Any) -> None:
if self.lockfile is None or self.lockfile.closed:
return
msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
self.lockfile.close()

@ -28,7 +28,7 @@ import mesonbuild.coredata
import mesonbuild.machinefile
import mesonbuild.modules.gnome
from mesonbuild.mesonlib import (
BuildDirLock, MachineChoice, is_windows, is_osx, is_cygwin, is_dragonflybsd,
DirectoryLock, DirectoryLockAction, MachineChoice, is_windows, is_osx, is_cygwin, is_dragonflybsd,
is_sunos, windows_proof_rmtree, python_command, version_compare, split_args, quote_arg,
relpath, is_linux, git, search_version, do_conf_file, do_conf_str, default_prefix,
MesonException, EnvironmentException,
@ -2447,10 +2447,9 @@ class AllPlatformTests(BasePlatformTests):
def test_flock(self):
exception_raised = False
with tempfile.TemporaryDirectory() as tdir:
os.mkdir(os.path.join(tdir, 'meson-private'))
with BuildDirLock(tdir):
with DirectoryLock(tdir, 'lock', DirectoryLockAction.FAIL, 'failed to lock directory'):
try:
with BuildDirLock(tdir):
with DirectoryLock(tdir, 'lock', DirectoryLockAction.FAIL, 'expected failure'):
pass
except MesonException:
exception_raised = True

Loading…
Cancel
Save