Add immutable revisions, PGVN editor, asset replacement, merge and rebase workflows, authenticated media delivery and reachability GC. Harden the script interpreter, pin unrpyc and remove tracked build metadata. Include current compatibility checks, Naninovel import and regression tests.
50 lines
1.4 KiB
Python
50 lines
1.4 KiB
Python
"""Shared writer leases and exclusive GC on the common data volume."""
|
|
import asyncio
|
|
import os
|
|
from contextlib import asynccontextmanager
|
|
|
|
from app.config import get_settings
|
|
|
|
|
|
def acquire(exclusive=False):
|
|
root = get_settings().data_dir
|
|
root.mkdir(parents=True, exist_ok=True)
|
|
handle = (root / "maintenance.lock").open("a+b")
|
|
try:
|
|
if os.name == "nt":
|
|
import msvcrt
|
|
if handle.tell() == 0:
|
|
handle.write(b"0")
|
|
handle.flush()
|
|
handle.seek(0)
|
|
msvcrt.locking(handle.fileno(), msvcrt.LK_LOCK, 1)
|
|
else:
|
|
import fcntl
|
|
fcntl.flock(handle, fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH)
|
|
return handle
|
|
except BaseException:
|
|
handle.close()
|
|
raise
|
|
|
|
|
|
def release(handle):
|
|
if os.name == "nt":
|
|
import msvcrt
|
|
handle.seek(0)
|
|
msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
|
|
handle.close()
|
|
|
|
|
|
@asynccontextmanager
|
|
async def storage_access(exclusive=False):
|
|
task = asyncio.create_task(asyncio.to_thread(acquire, exclusive))
|
|
try:
|
|
handle = await asyncio.shield(task)
|
|
except asyncio.CancelledError:
|
|
task.add_done_callback(lambda done: release(done.result()) if not done.cancelled() and not done.exception() else None)
|
|
raise
|
|
try:
|
|
yield
|
|
finally:
|
|
release(handle)
|