Class LoomCacheService
- All Implemented Interfaces:
org.gradle.api.services.BuildService<org.gradle.api.services.BuildServiceParameters.None>
在 JVM 内按 key 去重,并叠加跨进程文件锁,使同一缓存条目的生产在「单 daemon --parallel」与 「多 daemon」下都串行化。读已发布缓存的路径由调用方自身的存在性检查无锁完成,无需经过此服务。
-
Field Summary
Fields -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic Duration共享缓存锁的默认等待超时.static org.gradle.api.provider.Provider<LoomCacheService> get(org.gradle.api.Project project) 返回当前 Loom classloader 对应的缓存服务.<T> AsyncCache<T> getAsyncCache(String cacheName) 返回当前 Loom classloader 作用域内按名称共享的异步缓存.static StringmodDepsKey(File rootDir, String mappingsIdentifier) mod 依赖重映射写 LOCAL 共享remapped_mods缓存时的互斥 key(按根项目目录 + mappings 标识分).void在按 key 取得的「JVM 监视器 + 跨进程文件锁」保护下执行一个互斥动作.voidrunExclusiveChecking(Path lockRoot, String key, Duration timeout, Callable<Boolean> upToDate, Callable<Void> onUpToDate, Callable<Void> produce) 双检锁形式执行「先检查、再生产」:已就绪时连锁都不取.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface org.gradle.api.services.BuildService
getParameters
-
Field Details
-
NAME
- See Also:
-
-
Constructor Details
-
LoomCacheService
public LoomCacheService()
-
-
Method Details
-
getAsyncCache
返回当前 Loom classloader 作用域内按名称共享的异步缓存.调用方必须保证同一名称始终使用同一种值类型。服务注册名包含 classloader 身份, 因而不会读取另一插件 classloader 创建的强类型缓存实例。
-
runExclusive
public void runExclusive(Path lockRoot, String key, Duration timeout, Callable<Void> action) throws Exception 在按 key 取得的「JVM 监视器 + 跨进程文件锁」保护下执行一个互斥动作.读路径应由调用方自身的存在性检查无锁完成;仅在判定需要生产时才调用本方法取锁。 各 provider 以
shouldRefreshOutputs/Files.exists作为无锁快路径, 配合产物的原子发布(写入即完整),保证读方不会看到半成品。- Parameters:
lockRoot- 锁文件目录key- 互斥 keytimeout- 等待跨进程锁的超时时间action- 取得锁后执行的动作- Throws:
Exception
-
runExclusiveChecking
public void runExclusiveChecking(Path lockRoot, String key, Duration timeout, Callable<Boolean> upToDate, Callable<Void> onUpToDate, Callable<Void> produce) throws Exception 双检锁形式执行「先检查、再生产」:已就绪时连锁都不取.为什么需要它:
runExclusive(java.nio.file.Path, java.lang.String, java.time.Duration, java.util.concurrent.Callable<java.lang.Void>)是纯串行——后到的调用方必须先拿到锁, 才能进入临界区做isUpToDate()检查,然后立刻退出。干净环境(CI 首次构建)下 N 个项目配置同一份 Minecraft 时,它们算出同一个 key,于是第 2..N 个项目各自白等 「第一个项目做完整条 download→remap」的时间,而这个时间远超锁的 1 分钟超时。双检锁把等待变成「只等第一个项目生产完」,而不是「每个都排一遍队」:
- 锁外检查:已就绪 → 直接跑
action之外的回退分支,不取锁; - 未就绪 → 取锁(可能等前一个项目生产完);
- 锁内重检:若前一个项目已经把产物做好,就跳过生产。这是关键一步: 没有它,第 2..N 个项目拿到锁后会把同一份产物再重建一遍。
两次检查都必须是纯只读的存在性/脏标志判断(调用方保证),不得在锁外写共享缓存。
- Parameters:
lockRoot- 锁文件目录key- 互斥 keytimeout- 等待跨进程锁的超时时间upToDate- 只读就绪判定;锁内会被再次调用onUpToDate- 已就绪时的动作(跑在锁外,应做「读已发布产物」这类无副作用的事)produce- 需要生产时的动作(跑在锁内)- Throws:
Exception
- 锁外检查:已就绪 → 直接跑
-
defaultTimeout
共享缓存锁的默认等待超时.可通过系统属性
fabric.loom.lock.timeout.minutes覆盖 (在 gradle.properties 中写systemProp.fabric.loom.lock.timeout.minutes=10)。 未配置时:CI 上 10 分钟(干净环境首次构建要下载、解压、重映射整份 MC, 实测在 CI runner 上远超 1 分钟;原先按「CI 上不太可能解锁」取的 1 分钟正是 多项目并发配置时超时的直接原因),本地 30 分钟(容忍首次冷构建的慢速下载, 但避免在异常持有者上无限等待——配合等待日志中的持有者 PID 可手动处置)。 -
modDepsKey
mod 依赖重映射写 LOCAL 共享remapped_mods缓存时的互斥 key(按根项目目录 + mappings 标识分).同一 checkout 的多个并发构建(如同时跑 client 与 server)共享该 key、串行化写入; 不同 checkout 根目录不同 → key 不同 → 互不阻塞。当前唯一的取锁点是
SourceRemapper.remapAll(sources 缓存);二进制产出已改由 L3 任务持有产出目录、 由任务图串行化,不再使用本 key。key 由本助手统一生成,避免两处写法漂移。 -
get
public static org.gradle.api.provider.Provider<LoomCacheService> get(org.gradle.api.Project project) 返回当前 Loom classloader 对应的缓存服务.同一 Gradle root 可能通过约定插件和直接插件加载多份 Loom。注册名包含当前 classloader 身份,避免从另一 loader 取得不可强转的服务实现;同一 loader 的多个 子项目仍共享服务和内存缓存。跨 daemon 正确性继续由
CacheEntryLock保证。
-