Multithreading? Lets make this engine Multithreaded

No not a complete overhaul but I do believe this engine needs to be patched up a bit to allow attaching to nodes from outside the render thread. This could be accomplished internally within the Node.attachChild method or any other method that deals with potential exceptions thrown by modifying the node graph outside the render thread.

To keep from breaking any frameworks maybe add a new Node called SynchronizedNode?

@squizzle said: No not a complete overhaul but I do believe this engine needs to be patched up a bit to allow attaching to nodes from outside the render thread. This could be accomplished internally within the Node.attachChild method or any other method that deals with potential exceptions thrown by modifying the node graph outside the render thread.

To keep from breaking any frameworks maybe add a new Node called SynchronizedNode?

Or just use the ways that already exist so that you avoid slowing down every scene graph operation just to handle these outside cases.

1 Like

Furthermore, the whole premise of this sort of haphazard approach to threading is nothing but trouble for the user in the long run. If you use threading then you need a proper architecture that deals with the issues. Having a SynchronizedNode or some other ugly thing will only duct tape over those issues temporarily.

2 Likes

Good points. Another idea is extending the Node class with a method that ensures that there are no threading issues. Something like a Node.ensureAddChild method. Putting it into a method extends the API and leaves the decision to use it to the end user therefore not slowing down scene graph operation unless the user desires to call the method that adds a little overhead.

@squizzle said: Good points. Another idea is extending the Node class with a method that ensures that there are no threading issues. Something like a Node.ensureAddChild method. Putting it into a method extends the API and leaves the decision to use it to the end user therefore not slowing down scene graph operation unless the user desires to call the method that adds a little overhead.

But why is the normal way so hard? It’s like three lines of code and keeps you 100% aware of the hairy issues. Besides which, the special add child method would be doing an enqueue exactly like you’d be doing yourself, it would just be hiding it and a whole host of other issues you’d have to be aware of.

Not hard, just cumbersome to have to pass a Application instance just to use the enqueue method. Course there are work-arounds like creating a static class, which is what I had to do. It just seems like the enqueue method was slapped on there without any real thought behind it. The majority of the engine you are dealing with Nodes and Spatials but if you need to attach any of those to the other in another thread you have to go to Application? It doesn’t seem beneficial to the API to have such that feature split off from the node graph.

@squizzle said: Not hard, just cumbersome to have to pass a Application instance just to use the enqueue method. Course there are work-arounds like creating a static class, which is what I had to do. It just seems like the enqueue method was slapped on there without any real thought behind it. The majority of the engine you are dealing with Nodes and Spatials but if you need to attach any of those to the other in another thread you have to go to Application? It doesn't seem beneficial to the API to have such that feature split off from the node graph.

But usually the thing managing that other thread is an app state… and thus has the Application instance.

Otherwise, you are going through all kinds of trouble to manage the threading and have it shut down properly, etc…

Where as some magic method on Node would have a dozen caveats. For example, how many forum posts do you think we would see “How come when I attach this child and then try to get it again it’s not there?” At least with enqueuing there is an explicit understanding that it happens later… and even still we see users often miss that.

And by the way, if you are managing your thread with an app state then you get a nice update() method too and can do your own enqueue style logic however you want.

For example, when adding random nodes from another thread it is often desirable to only do one at a time to avoid dropping too many frames.

That is very true. With programming anything is possible, for the most part.

Lets brainstorm a bit.

A custom node class that handles the asynchronous mess would probably deal with Futures a lot.

For example I have a AtomicNode that extends from Node. Internally the AtomicNode is asynchronous at heart. I can add a child Spatial to that node and if the Thread.currentThread isn’t the Render Thread then it can handle the Spatial normally otherwise it calls the Application’s enqueue method.

To get a Spatial from the AtomicNode I would call the normal Node.getChild but instead of a Spatial it would return a Future<Spatial> that allows me to check the status of the Spatial I originally wanted.

I will write up an implementation of this to post here later this week, hopefully, if time is available.

Well that is as far as my brainstorming can go right now it is almost midnight here and I have to eat the milk and cookies.

Let’s not forget here that this is a core engine. A synchronous node may be helpful for a case of uses, but not all. So having it around here in the forum is fine, but it does not belong in the core.

For example it will probably create many problems if you do all loading in an thread pool like I do currently, as it would effectively move all work back to the render thread.

As for passing the application class around, actually you cannot avoid that so easily, because application is not necessarly a singleton. There are a few projects already using multiple headless contexts. Eg think about stuff similar to a matchmaking server, that has for each match a instance running (and each in a own sepearte thread, where would your node synchronize with)

Then there is another thing that you should keep in mind, multicontext applications. Currently jme does not support this, but it might be added at some point, as it would allow uploading of textures ect. from a secondary opengl context, without blocking the renderer.

So don’t let me stop you in trying to write a atomicnode or similar, but hopefully you can now understand while there is none in the core of jme, as satifiyning all possible use cases is kinda impossible.

Hey thanks for the input @Empire Phoenix.

You are right it is case by case.

I know this isn’t the right forum to ask but it is on topic, how do you prevent jme3 from popping up those error notification windows related to attaching to a node on the wrong thread?

a) Please start a new thread for a new question.
b) Mutating one and the same data structure from multiple processes (a.k.a. shared mutable data) is really, really, really hard to get right. I speak not only from personal experience but also from established consensus of people actually doing that. Build a framework that encourages multithreaded updates, see a framework that takes ten-fold the time to complete anything with, and that no average-skilled application programmer can use properly.
c) Shared mutable data means that if you have true multithreading with more than one processor core, one core will always wait until the other is done with updating. Worse, not only writers will block each other, readers will have to wait until no writer is active. You’ll end with a system where most of that additional processor horsepower is spent waiting.
d) Even if you have multiple writers, if they are running on different processors, moving the data between processor caches is a horribly inefficient process. We’re talking about hundreds(!) of CPU cycles spent with cache validation, cache flushing, and handing over responsibility for a specific memory address before that single one-byte update can go through. If writers happen to alternate banging on the same byte, you can slow down your application to a crawl.
e) What could be done is to set up the scene graph as an immutable data structure. This means rebuilding the changed parts of the scene graph at a rate of 60 times per second; this is entirely viable on desktop machines and newer versions of Android (actually it could be more efficient than in-place updates, however strange that might sound). Unfortunately, the garbage collector on older versions of Android is inefficient for that kind of scenario, so JME3 cannot do that.
f) Switching to an immutable scene graph would mean a full rewrite of all code that touches the scene graph, i.e. almost everything from internals to plugins to applications. So… impossible for JME3, maybe JME4. Even for JME4, claims to superiority require massive backing from benchmarks, the handwaving I’m presenting here isn’t and shouldn’t be enough to convince anybody.

I agree with pspeed, empire phoenix and tool forger. It is better to keep the core single threaded.
No matter what approach is taken it will most probably end up in some queue somewhere since the rendering must be single threaded (switching the thread for a GL-context might be possible but it is still just 1 thread per context so there you are with the queue again).

But there is nothing preventing a plug-in from providing convenience methods for common use cases. You could for example write an AppState that wraps the scene graph and performs the enqueue if it is needed, play with ASM/code generation or JDK proxies or whatever approach looks fun.

So my suggestion is to write a plug-in, it is a nice way to share add-on functionality and kick around ideas.

This seems to be more about thread safety than actual multithreading. For multithreading a proper model would be needed instead of just making the external methods more robust to failure when called from outside, just doing that is basically the worst form to realize threading. The OpenGL render process is a single threaded loop from the outside so the render loop of jME revolves around it. Yes, you do have to think about multithreading yourself but if you don’t do that you will have issues implementing it no matter if you have “safe” methods to call or not.

I think a better question would be to take a step back and ask why you are trying to do this? What problem are you trying to solve?

If it is just a matter of passing the Application instance around then a singleton AppContext handles that far more easily than threading the add/remove. Particularly since as has already been pointed out OpenGL is fundamentally single threaded.