Caveats and possible problems of multithreading mc


#1

Hello all, I’ve got some interesting discussion topic here about bringing asynchronous approach for MC processing.

Idea came to me, while I was looking into others attempts to make server faster.
I was particularly looking into use of multithreading, as in my opinion the weakest part of mc is doing it all in one thread.

Right of the bat, the idea: unattached loaded parts of the world can not affect each other and can be processed separately.
That’s it.

Look at this simplified map of the world.
Steve face - player
Greed cells - loaded chunks, entity processing
Yellow cells - loaded chunks, lazy

image

Regions, that are not touching each other can not affect each other in any way. No entity, or block update can traverse unloaded chunks.

Of course there are some ways to load more chunks, like placing on borders redstone, hoppers, fire (causes problems in nether), etc. But lets keep these things out for simplification the discussion, as the topic is about separate regions, which will be there anyway.

So basically I image architecture, where main thread is busy, by allocating tasks for threads.
Each thread is busy by processing its part of the world.
Sometimes there will be event of merging two separate processing regions, and that should be handled my main thread, by merging two tasks in one and keeping in in one of two thread.

Idea looks elegant to me, and this makes it too suspicious - why wasn’t it implemented yet?
So I want to discuss here, what possible problems might such approach bring.

My thoughts on possible problems:

  1. Difference in tick count in separate threads.

One thread my throttle with some fancy redstone lag-machine and fall behind others. But is it really a problem? It is perfectly fine, as separate loaded parts do not affect each other.
The only thing, that should be identical for whole world - time of day. And this can be handled by main thread.​

  1. Pets.

Funny enough, its the only thing, that I come up with, that can traverse between two separate regions.
You keep your dogs sit some where and leave. When somebody else drop water on them, they should teleport to you, whereever you are in the same world.
Funky staff. Can be handled as portals in next point - transfer data between tasks.​

  1. Other worlds chunks.

This is trickier. Chunks in nether can be loaded by using a portal. Even when through an item in it.
Either such chunks should be in the same task as chunks overworld.
Or it is in separate task, and usage of portals should be handled differently to pass data from one task to another.​

  1. Scoreboards and other server-wide data.

Can be handled by main thread and be accessible as readonly for tasks.​

What are your thoughts?
What do you think of other problems that can emerge?

Some background about my reasons and capabilities.
I’ve got decent programming experience in mainstream languages like python, C and C++ through 6 years of work, but my Java skills are really limited down to couple plugins, I wrote this year for my friends server.
Holidays are coming and I want to make this project as a week-long learning session for myself to grow my grasp on Java, multithreading and spigot internals in particular.
If it will or will not work, I will be happy, but I don’t want to spend so much time and effort, if the idea if flawed in its cradle. It will be just frustrating and teach me too little.


#2

Heya,
multithreading was considered quite often in the past, but there are many problems with it. The biggest one being that plugins currently are not written in a way that can handle it. Besides that the minecraft server internal code also is not written in any way that would allow for it (everything is very single threated and even worse completelly dependent on each other). because of these things, you would probably need to reprogram a big chuck of the core server code. then you need to make sure that it actually all works and that you don’t have corruptions or deadlocks. and the you still have the plugin problem.

if you dont have years long experience of multithreaded programming i would highly discourage you from even trying it. similarly i am not confident that it will be implemented soon. (i mean it was implemented before, but it wont become part of paper i assume)


#3

Funny story, you pretty much described my already planned goals :slight_smile:

Check out this project which touches on this exact idea: https://github.com/PaperMC/Paper/projects/3
Most of the details are in: https://github.com/PaperMC/Paper/issues/1001


#4

The idea is a good one although the biggest obstacle is the bukkit api. Any concurrent call into plugins can break them since they expect the server to be single-threaded. So you’d have to add locks and synchronization points to handle that. For example, when calling an event, you’d want to acquire a global lock, and then wait for all other threads to wait on that lock (they’d block on it in a synchronization point). Suspending the other worker threads isn’t an option since plugins could potentially modify state they’re handling as they’re suspended, and good luck tracking down that bug.

Ideally the synchronization points would be at a place where code does not expect state (that could be modified by a plugin) read before entering the synchronization point to be in the state that it was after exiting. In places where state could be modified you’d have to handle that on a case-by-case basis, and this would most certainly be the case for calling events. And there a lot of events.

There’s also lots of code, so you’d have to look through quite a bit of it to ensure you aren’t messing with shared state across threads.

Loading chunks next to a region should merge regions (potentially more than 2 regions to merge), and while you can minimize these happening in the nms server (and change vanilla behaviour) you must handle cases where plugins do it. And they can do it any time you enter plugin code.

You might be able to get away with delaying cross-region activities (to be executed synchronously) but that might change vanilla behaviour so you’d need to check that.

And then you have to update this patch every time mojang pushes an update.

There’s a lot more cases to handle, but you get the idea. There’s a lot to consider, especially with plugins, and especially maintenance of the patch.