Skip to content

Commit

Permalink
utils: Replace BuildDirLock with generic DirectoryLock
Browse files Browse the repository at this point in the history
DirectoryLock provides a generic locking implementation the replaces the
previously used BuildDirLock.
  • Loading branch information
sp1ritCS committed Nov 1, 2024
1 parent a608e56 commit 5e2e39d
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ __pycache__
*~
*.swp
packagecache
.wraplock
/MANIFEST
/build
/dist
Expand Down
4 changes: 3 additions & 1 deletion mesonbuild/msetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ def generate(self, capture: bool = False, vslite_ctx: T.Optional[dict] = None) -
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]:
Expand Down
19 changes: 12 additions & 7 deletions mesonbuild/utils/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@

"""base classes providing no-op functionality.."""

import enum
import os
import typing as T

from .. import mlog

__all__ = ['BuildDirLock']
__all__ = ['DirectoryLock', 'DirectoryLockAction']

# 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 DirectoryLock:
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
31 changes: 23 additions & 8 deletions mesonbuild/utils/posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,38 @@
import typing as T

from .core import MesonException
from .platform import BuildDirLock as BuildDirLockBase
from .platform import DirectoryLock as 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()
29 changes: 22 additions & 7 deletions mesonbuild/utils/win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,35 @@
import typing as T

from .core import MesonException
from .platform import BuildDirLock as BuildDirLockBase
from .platform import DirectoryLock as 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()
7 changes: 3 additions & 4 deletions unittests/allplatformstests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
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,
Expand Down Expand Up @@ -2447,10 +2447,9 @@ def test_identical_target_name_in_subdir_flat_layout(self):
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
Expand Down

0 comments on commit 5e2e39d

Please sign in to comment.