Package net.fabricmc.loom.util.download
Class DownloadLock
java.lang.Object
net.fabricmc.loom.util.download.DownloadLock
- All Implemented Interfaces:
AutoCloseable
单个下载目标的跨进程互斥锁.
锁文件里记的是什么
锁文件 <name>.lock 用 Files.createFile(java.nio.file.Path, java.nio.file.attribute.FileAttribute<?>...) 独占创建,创建成功后立刻写入持有者信息
(PID、进程启动时刻、主机名、token)。判活全部基于这份信息:锁文件存在只说明「曾经有人持有」,
与「现在有人在写」是两件事——改造前把两者当成同一件事,于是崩溃残留会让下一次构建白下载一遍,
而真正并发的第二个写入者却直接被判失败。
残留与「正在写」怎么区分
- 持有者在本机、PID 已不存在(或进程启动时刻与记录不符,说明 PID 被复用)→ 残留,立即接管;
- 持有者在本机、PID 还活着且不是本 JVM → 正在写,等待;
- 持有者是本 JVM:本 classloader 的持有表里有它 → 另一个线程在写,等待;表里没有、且锁文件已存在
超过
SAME_JVM_GRACE→ 本 JVM 的遗留锁(收尾删除失败或另一份 Loom classloader 已放手),接管; - 跨主机(共享缓存盘 / NFS)或持有者信息还没写完 → 无法判活,按锁文件年龄推断:
刚创建视为正在写,超过
EMPTY_CONTENT_GRACE/FOREIGN_STALE_AFTER视为残留.
为什么等待超时也接管
这把锁只负责去重与礼仪,正确性不依赖它:下载写入本次尝试专用的唯一临时文件,
写完整后原子落位(AtomicFiles),因此两个写入者同时存在
最多重复一次网络传输,不会产出半成品或损坏的文件。既然如此,「持有者挂死也不让构建失败」
就比「等不到锁就抛异常」更合适。
-
Method Summary
Modifier and TypeMethodDescriptionstatic DownloadLock取得lockFile的独占权,必要时接管残留锁.voidclose()booleanReturns 本次取得锁是否属于「持有者看起来还活着但仍被接管」.booleanReturns 取得锁的过程中是否碰过别人留下的锁文件(等过或接管过).
-
Method Details
-
acquire
取得lockFile的独占权,必要时接管残留锁.- Parameters:
lockFile- 锁文件路径,父目录会被创建timeout- 等待「正在写」的持有者释放锁的最长时间,超时后强制接管- Returns:
- 已持有的锁,调用方必须在 finally / try-with-resources 中关闭
- Throws:
IOException- 无法创建或接管锁文件时
-
tookOverLiveLock
public boolean tookOverLiveLock()Returns 本次取得锁是否属于「持有者看起来还活着但仍被接管」..调用方据此可以加一条提示;下载本身不依赖该信息。
- Returns:
- 本次取得锁是否属于「持有者看起来还活着但仍被接管」
-
waitedForLock
public boolean waitedForLock()Returns 取得锁的过程中是否碰过别人留下的锁文件(等过或接管过)..调用方据此判断「锁内重检」是否值得做:从未碰到过锁文件,就不可能有人在此期间把产物写好, 重检只是白白多一次 IO(并可能多算一次 hash)。
- Returns:
- 取得锁的过程中是否碰过别人留下的锁文件(等过或接管过)
-
close
public void close()- Specified by:
closein interfaceAutoCloseable
-