Skip to content

Reading and Writing

Gustavo Malvestiti edited this page Aug 16, 2026 · 1 revision
MyModConfig shared = holder.data();   // cheap, shared — treat as read-only
MyModConfig mine = holder.copy();     // deep copy you own, safe to mutate freely

data() returns the live published snapshot. It is cheap — no locking, no copying — and is the right choice for hot paths. The returned object is shared with every concurrent reader — mutating it would corrupt the live state. Take a copy() when you need to hold a reading that cannot shift while you examine multiple fields at once, or when you need to mutate values locally before applying them.

holder.update(config -> config.showHints = false);   // publish in memory only
holder.updateAndSave(config -> config.hudScale = 3); // publish and write to disk

The lambda passed to update receives a private candidate copy, not the live state. The candidate is validated before publication. If validation fails, the live state is never touched and the failed update is reported on the returned UpdateResult. A mutator that throws is treated as a defect and propagates regardless of the failure policy.

UpdateResult result = holder.updateAndSave(config -> config.hudScale = 99);
if (!result.accepted()) {
    result.violations().forEach(v -> LOGGER.warn("{}: {}", v.id(), v.message()));
}

Holders built with createAsync() add non-blocking variants that return CompletableFuture:

AsyncConfigHolder<MyModConfig> holder = /* ... */;
holder.saveAsync();
holder.updateAndSaveAsync(config -> config.hudScale = 3).thenAccept(result -> { /* ... */ });

Clone this wiki locally