mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 06:00:33 +02:00
Store: pruning rechecks each APK before deleting and spares ones being installed
Deletion re-stats under a lock shared with touch() (F-Droid cache reuse) and claim()/release() (held by the store around install), so a reused or installing APK is never removed from an out-of-date scan. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4fd8bb79af
commit
a7261b1601
5 files changed
+91
-13
No files matched your search
+39
-8
@@ -7,6 +7,36 @@ from . import SourceError, SourceLimited
|
||||
|
||||
UA = 'FrameControl/0.1'
|
||||
APK_CAP = 2 * 1024 ** 3 # cached APKs across all sources, least recently used go first
|
||||
RECENT = 3600 # an APK used this recently may be about to be installed; never pruned
|
||||
_use_lock = threading.Lock() # pruning's final check and delete vs touch()/claim()
|
||||
_in_use = {} # APK path -> installs using it
|
||||
|
||||
|
||||
def touch(path):
|
||||
"""Mark a cached APK as just used; False if pruning already removed it."""
|
||||
with _use_lock:
|
||||
try:
|
||||
os.utime(str(path))
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def claim(path):
|
||||
"""Protect a downloaded APK from pruning until release(path)."""
|
||||
path = os.path.abspath(str(path))
|
||||
with _use_lock:
|
||||
os.utime(path) # raises if it has gone
|
||||
_in_use[path] = _in_use.get(path, 0) + 1
|
||||
|
||||
|
||||
def release(path):
|
||||
path = os.path.abspath(str(path))
|
||||
with _use_lock:
|
||||
if _in_use.get(path, 0) > 1:
|
||||
_in_use[path] -= 1
|
||||
else:
|
||||
_in_use.pop(path, None)
|
||||
BACKOFF = 600 # seconds to leave a host alone after 403/429 without Retry-After
|
||||
_limited = {} # host -> time.time() before which we don't contact it
|
||||
_limited_lock = threading.Lock()
|
||||
@@ -77,16 +107,17 @@ def prune():
|
||||
except OSError:
|
||||
pass
|
||||
total = sum(size for _, size, _ in apks)
|
||||
for mtime, size, path in sorted(apks):
|
||||
for _, size, path in sorted(apks):
|
||||
if total <= APK_CAP:
|
||||
break
|
||||
if now - mtime < 3600: # may be about to be installed
|
||||
continue
|
||||
try:
|
||||
os.remove(path)
|
||||
total -= size
|
||||
except OSError:
|
||||
pass
|
||||
with _use_lock: # the scan is old news: check again right before deleting
|
||||
try:
|
||||
if os.path.abspath(path) in _in_use or time.time() - os.stat(path).st_mtime < RECENT:
|
||||
continue
|
||||
os.remove(path)
|
||||
total -= size
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def checked_url(url, hosts):
|
||||
|
||||
@@ -647,9 +647,7 @@ def download(source, entry_id, version_code=None):
|
||||
sha = version['sha256']
|
||||
path = frame_host.cache_dir('apk-sources', sha + '.apk')
|
||||
try:
|
||||
if path.exists() and _sha256(path) == sha:
|
||||
os.utime(str(path)) # most recently used, for cache pruning
|
||||
else:
|
||||
if not (path.exists() and _sha256(path) == sha and _web.touch(path)): # touch: recently used, not pruned
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
fd, tmp = tempfile.mkstemp(dir=str(path.parent), suffix='.part')
|
||||
os.close(fd)
|
||||
|
||||
@@ -314,7 +314,15 @@ def install(source_id, entry_id, version_code=None, progress=None):
|
||||
kwargs['artwork'] = downloaded.get('artwork') or entry.get('artwork')
|
||||
if progress:
|
||||
progress('Installing', None)
|
||||
result = frame_android.install(downloaded['apk'], **kwargs)
|
||||
from apk_sources import _web
|
||||
try:
|
||||
_web.claim(downloaded['apk']) # no cache pruning while it installs
|
||||
except OSError as e:
|
||||
raise SourceError('The downloaded APK disappeared before installing; try again') from e
|
||||
try:
|
||||
result = frame_android.install(downloaded['apk'], **kwargs)
|
||||
finally:
|
||||
_web.release(downloaded['apk'])
|
||||
if obb:
|
||||
# OBB files go into the app's own instance, which only exists while the app runs.
|
||||
with _lock:
|
||||
|
||||
Reference in new issue
Block a user