fix: hold data directories while packs read them

This commit is contained in:
Abdessamad Derraz committed 2026-10-17 19:22:50 +02:00
1 parent d98f00cfbc
commit 1ffc0a6937
5 files changed
+101 -3

No files matched your search

+11 -2
View File
@@ -716,8 +716,17 @@ _data_dir_index: dict[str, dict[str, list[tuple[int, str]]]] = {}
def _data_dir_basenames(cache_dir: str) -> dict[str, list[tuple[int, str]]]:
"""Casefolded basename -> (walk order, path) of every file in a cache."""
key = os.path.realpath(cache_dir)
"""Casefolded basename -> (walk order, path) of every file in a cache.
Keyed by the tree's identity as well as its path: a refresh swaps in a
new directory under the same name, and an index of the old one named
files that were gone.
"""
try:
stat = os.stat(cache_dir)
except OSError:
return {}
key = f"{os.path.realpath(cache_dir)}:{stat.st_dev}:{stat.st_ino}"
if key not in _data_dir_index:
index: dict[str, list[tuple[int, str]]] = {}
order = 0
+11 -1
View File
@@ -64,7 +64,7 @@ from common import (
yaml_load,
)
import packresolve
from refresh_data_dirs import cache_lock
from refresh_data_dirs import cache_lock, hold_data_dirs
import region as region_mod
from manifestselect import SelectionIndex
import slot as slot_mod
@@ -2777,7 +2777,11 @@ def _given_flags(args) -> set[str]:
def main():
with contextlib.ExitStack() as held:
_main(held)
def _main(held: contextlib.ExitStack) -> None:
parser = argparse.ArgumentParser(description="Generate platform BIOS ZIP packs")
parser.add_argument("--platform", "-p", help="Platform name (e.g., retroarch)")
parser.add_argument(
@@ -2936,6 +2940,10 @@ def main():
_validate_args(args, parser)
# Hash lookup / pack mode
# A platform run may refresh the trees below, under the exclusive lock:
# it takes the shared ones after that. Every other mode reads them now.
if not (args.platform or args.all) or args.offline or args.no_refresh_data:
hold_data_dirs(held, load_data_dir_registry(args.platforms_dir))
has_from_md5 = bool(args.from_md5 or getattr(args, "from_md5_file", None))
if has_from_md5:
hashes = (
@@ -3049,6 +3057,8 @@ def main():
failed = sorted(k for k, v in results.items() if v is None)
if failed:
print(f"WARNING: data directory refresh failed: {', '.join(failed)}")
if data_registry:
hold_data_dirs(held, data_registry)
emu_profiles = load_emulator_profiles(args.emulators_dir)
+15
View File
@@ -62,6 +62,21 @@ def cache_lock(cache_dir: str | Path, shared: bool = False):
return _file_lock(cache_dir.with_name(f".{cache_dir.name}.lock"), shared=shared)
def hold_data_dirs(stack: contextlib.ExitStack, registry: dict) -> None:
"""Hold every cached data directory shared for the life of *stack*.
A pack resolves names against a tree, then copies the file minutes
later; a refresh swapping the tree in between shipped bytes nobody
verified, or died on a file the new tree no longer has. A refresh waits
for the holders. Never called before a refresh in the same process:
the exclusive lock would wait on this one.
"""
for entry in registry.values():
cache = entry.get("local_cache", "")
if cache and os.path.isdir(cache):
stack.enter_context(cache_lock(cache, shared=True))
def _load_versions(versions_path: str = VERSIONS_FILE) -> dict[str, dict]:
path = Path(versions_path)
if not path.exists():
+10
View File
@@ -25,6 +25,7 @@ Usage:
from __future__ import annotations
import argparse
import contextlib
import json
import os
import sys
@@ -2077,6 +2078,11 @@ def _run_listing(args: argparse.Namespace, parser: argparse.ArgumentParser) -> N
def main():
with contextlib.ExitStack() as held:
_main(held)
def _main(held: contextlib.ExitStack) -> None:
parser = argparse.ArgumentParser(description="Platform-native BIOS verification")
parser.add_argument("--platform", "-p", help="Platform name")
parser.add_argument(
@@ -2146,6 +2152,10 @@ def main():
"--platform, --all, --emulator, and --system are mutually exclusive"
)
_refuse_mode_flags(args, parser)
# The trees stay put while names resolve against them and files are read.
from refresh_data_dirs import hold_data_dirs
hold_data_dirs(held, load_data_dir_registry(args.platforms_dir))
with open(args.db) as f:
db = json.load(f)