-
-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forge configs occasionally become corrupted #32
Comments
It doesn't help that nightconfig's filewatcher is apparently really aggressive and checks for file changes up to a million times a second apparently (???) which could exacerbate this race |
Hi! NightConfig's author here.
@Override
public void load() {
if (!currentlyWriting) {
synchronized (this) {
// HERE is missing a double-check if(!currentlyWriting)
if (closed) {
throw new IllegalStateException("Cannot (re)load a closed FileConfig");
}
parser.parse(nioPath, config, parsingMode, nefAction);
}
}
} edit: but since write() and parse() are in a synchronized block, reading and writing still isn't possible at the same time. So it's weird 🤔 As for the FileWatcher, that shouldn't cause the problem. A partial write of the file and/or partially updated config may be the root cause instead. |
It's the NeoForge (previously Forge) Discord Server, https://discord.neoforged.net |
thanks! |
Update: TheElectronWill/night-config#152 has been merged with a full rewrite of FileConfig, based on concurrent configurations (see the comments on the PR). When the new API is used properly, it should hopefully fix the problem :) |
NightConfig v3.7.0 released 🎉 |
This is a continuation of MinecraftForge/MinecraftForge#9122.
After performing an analysis of the code Forge uses to interact with the Night Config library, I identified a design flaw which seems to have the potential for race conditions and corruption. In
ConfigFileTypeHandler
when a config is first registered and loaded, a file watcher is attached. Shortly after that method returnssave
is invoked on the config object. In summary, this can lead to the following sequence of events:Files.newOutputStream
is invoked with the config file's path, which, according to the Javadoc, "initially [truncates] an existing regular-file to a size of 0 if it exists." I verified using a simple test program that, at least on Linux, theOutputStream
given by this method is not atomic. The file is truncated immediately upon return of the method, and writes are seen in the underlying file even before the stream is closed.When testing on 1.16, I was able to reliably observe symptoms similar to the theory I have described above - by augmenting the Forge file watcher's
run
function with my own, I confirmed that thecurrentlyWriting
flag inWriteSyncFileConfig
(the config class in NC responsible for saving) was stilltrue
when the watcher was invoked, indicating that saving is NOT completed before the watcher is fired.I think fully fixing this will require a fundamental rethink of how the file watcher is used in Forge, as ideally it needs to never be listening when
.save
is called.For completeness, here is a link to the Discord conversation in which I first brought up this new theory.
The text was updated successfully, but these errors were encountered: