Bug? RaiseEvent2 in different threads

Informatix

Expert
Licensed User
Longtime User
In my game, I encounter from time to time a very annoying bug as it crashes the app. I use B4A v3.50.

I have two threads in parallel: the libGDX thread and a computation thread. In these two threads, functions are called in Java libraries and some of these functions trigger an event. To trigger these events, I use RaiseEvent2 and not RaiseEventFromDifferentThread because I need speed and, in all computations, my event handler in my B4A code has to return a result.

The problem occurs when I click on one of the active objects on screen during the computation in the background. There are sometimes collisions between events from the different threads and the sender of an event become the sender of the other event, leading to a big failure of my code. The problem is coincidental (most of the time, all works fine) and thus difficult to reproduce. If I ignore the received event with the wrong sender, I also ignore the user action or a computation demand. It's what I'm doing now to avoid crashes but it's not really a workaround (a computation not done has big consequences). I want to find a solution where I don't lose any event.

Could this be fixed with the Synchronized keyword in the RaiseEvent2 declaration?
 

Informatix

Expert
Licensed User
Longtime User
You should not rely on Sender if you are raising an event from a background thread. Sender is a single field. It will not work correctly with concurrent events. You should instead pass the "sender" as a parameter (or supply a different method to get the sender).
None of these libraries were written with the idea that they could be run in the background (it's not their common use) so Sender is passed as it should be passed in a standard library. It's not a problem with one of these libs as Sender has probably no use and is not used, but for the other lib (libGDX) Sender is supposed to be used in various and multiple cases. Ignoring it is not an option, and rewriting the RaiseEvents does not look like a definitive solution because that supposes that the two events will never use the same variables or methods (and in B4A there's nothing to manage synchronization). And can you guarantee there will be no other side-effect in your code when the RaiseEvents run in parallel?
I know it's a particular need (although it's a typical way of programming a game) but I can't see any good solution other than using the Synchronized keyword. If you have another idea, I will be glad to hear it.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
And can you guarantee there will be no other side-effect in your code when the RaiseEvents run in parallel?
No. The "user" code is expected to run on the main thread. This is how the internal framework is designed.

If the event sub doesn't access any global variable and any UI element then in most cases it will work fine. There are exceptions like Regex and DateTime which are not thread safe.

The standard solution is to delegate the event to the main thread with raiseEventFromDifferentThread. As you wrote you will lose the return value in that case. You can force the background thread to wait for the result however it is a bit tricky and it is not a good solution for a real time game.

Adding synchronize will make it impossible to run parallel code.
 

Informatix

Expert
Licensed User
Longtime User
Adding synchronize will make it impossible to run parallel code.

I don't need true parallelism. What a modern OS on a single core can simulate is enough. My "background" thread runs continuously, but my "main" thread (quotes are there because both threads are run separately from the main UI thread which is not used) waits for the user actions and does not consume a lot of the CPU time, so running the events in sequence (because of Synchronized) is not a problem at all and probably no one will notice that even on the slowest device. On the contrary, a user can notice an event that has been missed or ignored by my application so I have to find a real solution, including rewriting the BA class if needed.
 

Informatix

Expert
Licensed User
Longtime User
What is exactly the problem now? The Sender object value?
The problem didn't change. I have to use the Sender value in most of my libGDX events and I can't because another thread running in the background "pollutes" this value. RaiseEvent2 (or RaiseEvent) is not thread-safe and this is a big issue in my game. I can't do nothing with an event received with the wrong sender so I have to discard the whole event and some user actions are lost. For now, I can't see any other solution than adding the Synchronized keyword to RaiseEvents2. As I don't want to spend time to understand and rewrite the BA class (and test this new version), my questions are: do you think that adding the Synchronized keyword is a good idea and do you agree to change the RaiseEvent2 declaration? That should have no impact on other applications.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
do you think that adding the Synchronized keyword is a good idea and do you agree to change the RaiseEvent2 declaration?
It is not possible to add Synchronized to raiseEvent2. It will break apps that rely on the ability to raise events on other threads. Mainly using the Threading library.

For example you can use the Threading library to run your code on a background thread. This code can create network connections or other long tasks. Making this method a synchronized method will cause the app to freeze while the code executes.

It is however possible to make Sender itself thread safe. It is actually quite simple to do it. If you like I can send you a modified version of Core library with this feature (which will later be included in a future update).
 

Informatix

Expert
Licensed User
Longtime User
It is not possible to add Synchronized to raiseEvent2. It will break apps that rely on the ability to raise events on other threads. Mainly using the Threading library.

For example you can use the Threading library to run your code on a background thread. This code can create network connections or other long tasks. Making this method a synchronized method will cause the app to freeze while the code executes.

Ah okay, I didn't know that the RaiseFromDifferentThread function also calls RaiseEvent2. I will try to find another solution.

It is however possible to make Sender itself thread safe. It is actually quite simple to do it. If you like I can send you a modified version of Core library with this feature (which will later be included in a future update).

Great. I can't wait to test it.
 
Top