Shared variables and volatile

borkman
Posts: 55
Joined: Tue May 09, 2023 7:26 pm

Shared variables and volatile

Post by borkman »

In Java, variables shared by different threads, even if only one thread writes and the others read, need to either be in a synchronized method/block or marked volatile to ensure visibility. Synchronized seems to be discouraged for use in modules, for many good reasons. However, I don't often see volatile being used in module examples or discussed as a requirement. Even the VMD example of a most basic module sets a non-volatile variable in Notify() and reads the variable in ProcessSample(), presumably happening in different threads. Does anyone mark shared variables as volatile? Is VM doing something interesting in their jvm to avoid the need? The threading discussions of late (good stuff!) got me thinking as to why most modules seem to work just fine with regards to variable value visibility even though, as far as I understand things, this seems like a problem waiting to happen.
ColinP
Posts: 1000
Joined: Mon Aug 03, 2020 7:46 pm

Re: Shared variables and volatile

Post by ColinP »

I don't think VM is doing anything special to cope with the problems. It's just that primitives other than long and double don't cause trouble and while most modules are probably unsafely transfering doubles between threads it's unlikely to cause anything worse than noise and then only extremely rarely.

This is because doubles are stored in IEEE 754 format - so 1 bit for sign, 11 bits for the exponent and 52 bits for the significand. This means that almost all of the "important" information is in just one of the 32 bit chunks. So if we are unlucky enough for a thread switch to happen halfway, so that reading and writing operations interleave, the error is very unlikely to be catastrophic. Also the Java spec only says that double assignment isn't guaranteed to be atomic, so in some situations it may be.

Transferring anything more fragile than doubles without synchronization is a recipe for disaster though. And it's kind of worrying that CA's documentation not only does not mention the risks but actually presents bad practice as best practice.
ColinP
Posts: 1000
Joined: Mon Aug 03, 2020 7:46 pm

Re: Shared variables and volatile

Post by ColinP »

Another point of interest is the real-world cost of locking. I think there's something of a misconception about them being really inefficient. But they are only inefficient when they actually cause a blocking situation. For controlling conflict where the locked zones are small and there's little actual contention then a lock will not block for the vast majority of the time and if it does block the period of blocking could be extremely short.

So in a common situation where the likelihood of blocking is very low the overhead is not much higher than the time it takes to acquire and release a lock and although I've not done a benchmark I would hazard a guess that we are talking about delays measured in nanoseconds.
borkman
Posts: 55
Joined: Tue May 09, 2023 7:26 pm

Re: Shared variables and volatile

Post by borkman »

I agree the potential problem of partial writes of long and doubles is most likely, in practice, not a huge deal. I was talking about visibility, which seems more of a potential problem. Unless a shared variable is in a synchronized method/block or is marked volatile, there is no guarantee that each thread will see changes. I doubt most people mark their variables as volatile (though I may be wrong, which is one reason I brought this up). Certainly CA's docs don't. From a pure Java standpoint this seems problematic and a potential source of random errors. Unless I am missing something, which I hopefully am.

Oracle docs
Volatile — Visibility in JVM
Concurrency, Visibility, and Memory
ColinP
Posts: 1000
Joined: Mon Aug 03, 2020 7:46 pm

Re: Shared variables and volatile

Post by ColinP »

Ah, I now see what you mean borkman. I hadn't really considered that vulnerability properly.

So if I understand it correctly when the compiler or even the CPU reorders instructions inside a thread for optimisation purposes, even though this doesn't affect the semantics INSIDE the thread, it can have unexpected consequences in the semantics BETWEEN threads.

So volatile isn't just about adding atomicity to longs and doubles it also helps maintain inter-thread "happens before" ordering semantics and caching consistency.

And this becomes even more important in multi-core systems because threads might be running on different cores and each core needs to see a consistent view of shared data despite there being multiple caches.

Also this problem can impact references so even though reference assignment is automatically atomic, without using the volatile keyword inconsistancies might still arise.

So it seems we hava a lot to consider when handling concurrency correctly and there are multiple tools we may need to apply to ensure this in different scenarios...

* avoiding state where possible
* using immutability where possible
* using atomicity
* using volatile
* using intrinsic or explicit locking mechanisms

Crossing your fingers isn't really an option. :D

In practical terms for VM devlopers I think this means we should try to do almost everything inside the ProcessSample() thread and only do work in other threads where it really makes sense from an efficiency POV or it's unavoidable (as in getting user input and painting).

And we need to look at all shared data with intense scrutiny to make sure concurrency problems don't arise.
borkman
Posts: 55
Joined: Tue May 09, 2023 7:26 pm

Re: Shared variables and volatile

Post by borkman »

ColinP wrote: Thu Aug 03, 2023 7:59 am In practical terms for VM devlopers I think this means we should try to do almost everything inside the ProcessSample() thread and only do work in other threads where it really makes sense from an efficiency POV or it's unavoidable (as in getting user input and painting).
That's where I settled too. Variables written and read in ProcessSample() have no multi-threading concerns unless they are also read and/or written elsewhere. So don't. :P But when you must, such as drawing on a canvas or updating the UI, immutability is a great way to ensure a consistent, static frame of data between threads.

I suspect most developers are not using volatile variables and are rarely, if ever, having issues due to this fact. Maybe it can be ignored in practice? ¯\_(ツ)_/¯ Cherry Audio does in probably the first module example most new VM developers see.
UrbanCyborg
Posts: 625
Joined: Mon Nov 15, 2021 9:23 pm

Re: Shared variables and volatile

Post by UrbanCyborg »

Seems I recall a recent post by one of the admins stating that Notification() and ProcessSample() run in the same thread. If so, then a lot of these concerns go by the board. Can anyone verify this?

Reid
Cyberwerks Heavy Industries -- viewforum.php?f=76
ColinP
Posts: 1000
Joined: Mon Aug 03, 2020 7:46 pm

Re: Shared variables and volatile

Post by ColinP »

Hi Reid, do you mean this post?...
Cherryaudio Danny wrote: Wed Jul 26, 2023 6:21 pm It is possible for notifications to come from the GUI thread, timer threads, and even the sound engine thread if adjustments are made to knobs within the ProcessSample() function. However, it is not recommended to modify knobs within the ProcessSample() function as this is a time-sensitive thread and GUI functions shouldn't occur within it.
Notify() doesn't run in "a thread" as such. It's just a dumb as a bag of hammers reentrant switch.

I've spent a lot of effort investigating this and as far as I can tell what you see is what it is. There ain't any fancy wrapping around it. It gets called directly from multiple threads and sometimes recursively with absolutely no synchronization or conflict control whatsoever.
UrbanCyborg
Posts: 625
Joined: Mon Nov 15, 2021 9:23 pm

Re: Shared variables and volatile

Post by UrbanCyborg »

I don't think that was the one, especially since it seems to say the opposite. On another note, though, Notify() must be running in some thread, even if it's the default start thread for the app. What did you mean by that statement?

Reid
Cyberwerks Heavy Industries -- viewforum.php?f=76
ColinP
Posts: 1000
Joined: Mon Aug 03, 2020 7:46 pm

Re: Shared variables and volatile

Post by ColinP »

Hi Reid, that's the only recent post I've seen on the subject, so I can't comment on any other posts.

What I mean is that unlike ProcessSample(), which runs in its own dedicated thread, Notify() is reenterant code that's called by multiple threads, sometimes by itself (i.e. recursively) and often all at the same time.

For instance when you click on a button, Notify() is called with parameters indicating a Button_Changed event. In the handler for that event you might call SetValue() on a knob to change it to some special value. This would trigger a recursive call to the same Notify() method this time with a Knob_Changed event. Inside that handler you might recalculate some value used in the ProcessSample() thread. But while doing so a timer event might fire so a third entrance of Notity() happens this time from a different thread and with parameters indicating a GUI_Update_Timer or Named_Timer event. Midway through handling that the ProcessSample() thread could interrput proceedings and the two threads calling Notify() would pause for a while. Then when they resumed the AWT repaint thread might wake up and call Notify() with parameters indicating a Canvas_Painting event.

So at one point here Notify() might be running four times and in three different threads simultaneously. It can do this because methods are reenterant and all the call parameters are kept separate and automatically popped on and off the stack.

Although the example above might sound contrived, it isn't really as this kind of thing is going on all the time.

So one can't really say that Notify() runs in "a thread" because it's just the code you see in VMD - a simple switch statement and it is often being called simultaneously by multiple threads.
Post Reply

Return to “Module Designer”