Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 23 additions & 39 deletions buildrunner/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@

from collections import OrderedDict
from datetime import datetime
import fcntl
import io
import logging
import os
import re
import sys
import time
import uuid
import portalocker
import timeout_decorator
import yaml.resolver
import yaml.scanner
import glob
Expand Down Expand Up @@ -213,46 +213,30 @@ def _acquire_flock_open(
:param exclusive: config exclusive lock (True) or shared lock (False), defaults to True
:return: opened file object if successful else None
"""

@timeout_decorator.timeout(
seconds=timeout_seconds, timeout_exception=FailureToAcquireLockException
)
def get_lock(file_obj, flags):
portalocker.lock(
file_obj,
flags,
)
return file_obj

# pylint: disable=unspecified-encoding,consider-using-with
file_obj = open(lock_file, mode)
pid = os.getpid()
lock_file_obj = None
retry_sleep_seconds = 0.5

start_time = current_time = time.time()
duration_seconds = current_time - start_time

while duration_seconds < timeout_seconds:
try:
# The LOCK_NB means non-blocking
# More information here:
# https://docs.python.org/3/library/fcntl.html#fcntl.flock
if exclusive:
# Obtain an exclusive lock, blocks shared (read) locks
fcntl.flock(file_obj, fcntl.LOCK_EX | fcntl.LOCK_NB)
else:
# Allow multiple people to read from the file at the same time
# If another instance adds an exclusive lock in the save method below,
# this will block until the lock is released
fcntl.flock(file_obj, fcntl.LOCK_SH | fcntl.LOCK_NB)
except (IOError, OSError):
# Limit the amount of logs written while waiting for the file lock
num_seconds_between_log_writes = 10
if (
0
<= (duration_seconds % num_seconds_between_log_writes)
<= retry_sleep_seconds
):
logger.info(
f"PID:{pid} waiting for file lock on {lock_file} after {duration_seconds} seconds"
)
else:
lock_file_obj = file_obj
break
time.sleep(retry_sleep_seconds)
duration_seconds = time.time() - start_time
pid = os.getpid()

if lock_file_obj is None:
try:
flags = (
portalocker.LockFlags.EXCLUSIVE
if exclusive
else portalocker.LockFlags.SHARED
)
lock_file_obj = get_lock(file_obj, flags)
except FailureToAcquireLockException:
file_obj.close()
raise FailureToAcquireLockException(
f"PID:{pid} failed to acquire file lock for {lock_file} after timeout of {timeout_seconds} seconds"
Expand Down Expand Up @@ -322,6 +306,6 @@ def release_flock(
"""
if lock_file_obj is None:
return
fcntl.flock(lock_file_obj, fcntl.LOCK_UN)
portalocker.unlock(lock_file_obj)
lock_file_obj.close()
logger.write(f"PID:{os.getpid()} released and closed file {lock_file_obj}")
3 changes: 2 additions & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ python-on-whales>=0.70.1
pydantic>=2.4.2
retry2>=0.9.5
colorlog>=6.8.0
rich>=13
rich>=13
portalocker>=2.10.1
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ paramiko==3.2.0
# fabric
pkginfo==1.9.6
# via twine
portalocker==2.10.1
# via -r requirements.in
pycparser==2.21
# via cffi
pydantic==2.4.2
Expand Down
Loading