Use single shared remote command lock

This commit is contained in:
Storm Dragon
2026-05-08 00:48:11 -04:00
parent a60efdbe07
commit b6689d93bf
2 changed files with 36 additions and 32 deletions
+14 -30
View File
@@ -39,7 +39,7 @@ from fenrirscreenreader.core.i18n import _
REMOTE_COMMAND_LOCK_TIMEOUT_SEC = 2.0
REMOTE_COMMAND_LOCK_PREFIX = "fenrirscreenreader-remote-"
REMOTE_COMMAND_LOCK_FILE = "fenrirscreenreader-remote-command.lock"
class RemoteManager:
@@ -282,28 +282,10 @@ class RemoteManager:
return
def _get_remote_command_lock_path(self, event_data):
event_hash = hashlib.sha256(event_data.encode("utf-8")).hexdigest()
return os.path.join(
tempfile.gettempdir(),
f"{REMOTE_COMMAND_LOCK_PREFIX}{event_hash}.lock",
)
return os.path.join(tempfile.gettempdir(), REMOTE_COMMAND_LOCK_FILE)
def _cleanup_stale_remote_command_locks(self, now):
try:
lock_files = os.listdir(tempfile.gettempdir())
except OSError:
return
for lock_file in lock_files:
if not lock_file.startswith(REMOTE_COMMAND_LOCK_PREFIX):
continue
lock_path = os.path.join(tempfile.gettempdir(), lock_file)
try:
lock_age = now - os.stat(lock_path).st_mtime
if lock_age > REMOTE_COMMAND_LOCK_TIMEOUT_SEC:
os.unlink(lock_path)
except OSError:
pass
def _get_remote_command_hash(self, event_data):
return hashlib.sha256(event_data.encode("utf-8")).hexdigest()
def _read_remote_command_lock(self, lock_file):
lock_file.seek(0)
@@ -312,12 +294,13 @@ class RemoteManager:
lock_pid = int(lock_parts[0]) if lock_parts else 0
except ValueError:
lock_pid = 0
return lock_pid
lock_hash = lock_parts[1] if len(lock_parts) > 1 else ""
return lock_pid, lock_hash
def _write_remote_command_lock(self, lock_file, now):
def _write_remote_command_lock(self, lock_file, event_hash, now):
lock_file.seek(0)
lock_file.truncate()
lock_file.write(f"{os.getpid()} {now}\n")
lock_file.write(f"{os.getpid()} {event_hash} {now}\n")
lock_file.flush()
os.fsync(lock_file.fileno())
@@ -335,8 +318,8 @@ class RemoteManager:
def _claim_remote_command(self, event_data):
lock_path = self._get_remote_command_lock_path(event_data)
event_hash = self._get_remote_command_hash(event_data)
now = time.time()
self._cleanup_stale_remote_command_locks(now)
lock_file = self._open_remote_command_lock(lock_path)
if lock_file is None:
return False
@@ -344,20 +327,21 @@ class RemoteManager:
with lock_file:
try:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
lock_pid = self._read_remote_command_lock(lock_file)
lock_pid, lock_hash = self._read_remote_command_lock(lock_file)
lock_stat = os.fstat(lock_file.fileno())
if lock_pid == os.getpid():
self._write_remote_command_lock(lock_file, event_hash, now)
return True
if not lock_pid:
self._write_remote_command_lock(lock_file, now)
if not lock_pid or lock_hash != event_hash:
self._write_remote_command_lock(lock_file, event_hash, now)
return True
if now - lock_stat.st_mtime < REMOTE_COMMAND_LOCK_TIMEOUT_SEC:
return False
self._write_remote_command_lock(lock_file, now)
self._write_remote_command_lock(lock_file, event_hash, now)
return True
finally:
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)