[SPIGOT-1279] YamlConfiguration should be thread safe Created: 07/Nov/15  Updated: 11/Jul/16  Resolved: 11/Jul/16

Status: Resolved
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: New Feature Priority: Major
Reporter: Spottedleaf Assignee: Unassigned
Resolution: Won't Fix Votes: 0
Labels: Bukkit
Environment:

Affects all environments



 Description   

The YamlConfiguration class is NOT thread safe.
This means it is not safe for me to edit the config while saving it on another thread, meaning I have to run all of my configuration operations on one thread, usually the main thread. I/O should not be ran on the main thread, but at this point it is really the only option (unless I dramatically change how I use the configs, but not everyone is aware of how to do that or how it works, and even then, it will only work for saving configs, not for reloading them).

Fixes:

  • Allow the internal 'map' field to be reassigned, and change the modifier to protected volatile (you'll see why)
  • Change the internal 'map' field to 'ConcurrentLinkedHashMap', or at least allow a constructor to set the field to whatever it wants to
  • When 'load(File)' is invoked, simply load the new contents into a new ConcurrentLinkedHashMap and replace the old map field (this prevents any Threads from walking in when a reload is not finished yet, without using a synchronized block!! :O)


 Comments   
Comment by md_5 [ 11/Jul/16 ]

As stated below, the correct approach is to saveToString and dump however you choose from there.

Comment by Daniel Ennis [ 13/Nov/15 ]

saveToString is not signifcant enough to warrant over complicating the implementation of a class that for a majority of its users is done on the main thread.

By adding thread safety, you will now have to synchronize in more places, lowering the overall (though, admit it would be very minor) performance of the system.

.saveToString is not a known bottleneck that causes any form of performance issues.

Disk IO is, and there is a tried and tested method of keeping the disk IO off the main thread already... It doesn't warrant complicating the process for one users specific needs.

Though I can get behind the idea of making .save(File) default run file IO on sep thread for users, and adding a new API saveSync(File) for blocking saves for users with extremely specific needs.

Comment by Spottedleaf [ 13/Nov/15 ]

I don't really see the point of adding extra overhead to the main thread with saveToString(). That is not an operation which is 'light'.

The same goes for reloading. There would still be considerable overhead for loadFromString().

I don't know what concurrent collections you are specifying.

Comment by Daniel Ennis [ 08/Nov/15 ]

FileConfiguration.save(File) calls saveToString(); I'ts the same thing.

Why are you needing to modify the config object async?

These operations should be done on main thread. There's no benefit to doing that off main.

Then simply do your saving where saveToString is also called sync, then the file IO part is done async.

the plugin dev community for most part are not modifying config options async...
It's just not a primary use of the system to be needed async, and if you really needed to, you could wrap it yourself like concurrent collections commonly do.

Comment by Spottedleaf [ 08/Nov/15 ]

And then you have the overhead of data -> String.
And you're dealing with the plugin development community, not many will even attempt that sort of fix, or even know it exists.

I don't think for an API I should have to use such a hacky solution to fix a problem that was overlooked in the development of it.

EDIT:

There's also more to this than saving and loading configurations - only one thread can create or remove keys from the config, otherwise your saving function may become broken.

Comment by Daniel Ennis [ 08/Nov/15 ]

The save method is just a utility for you. You can solve this by simply doing

String data = config.saveToString();
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
write data to file here.
});

This is a much easier method than complicating the internals of Configurations, as this is all that method does for you...

Now, we could argue that we could async this by default for config.save(File) but that might break someones workflow (insert XKCD) on some silly expectation to write then immediately read while blocking.

Comment by SpigotMC [ 07/Nov/15 ]

Your build is not the latest and therefore may be the reason you are having this issue. Spigot is 1 version(s) behind. CraftBukkit is 0 version(s) behind. This message was automatically generated and is not guaranteed to be a solution to your issue.

Generated at Tue Apr 22 05:44:23 UTC 2025 using Jira 10.3.5#10030005-sha1:190c783f2bd6c69cd5accdb70f97e48812a78d14.