thread safe vst

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Sorry to drag up an old thread, but I'm looking at making my framework thread safe and this topic seems to cover what I'm looking for. Anyway a I have a couple of questions on this :)
mystran wrote: The one thing you can generally rely though, is that loads/stores of a properly aligned variable (of 32-bits) tagged with "volatile" are predictable in the sense that loads/stores are always done exactly once from/to memory
How is correct alignment guaranteed? would I have to do something like this on windows:

Code: Select all

__declspec(align(32)) volatile float gain;
Is the surround delay example in the VST2.4 not thread safe?

Cheers,

Matt

Post

tony tony chopper wrote:Lots of programmers try & test
That applies and causes alot of problems in general, in my experience.
Cakewalk by Bandlab / FL Studio
Squire Stratocaster / Chapman ML3 Modern V2 / Fender Precision Bass

Formerly known as arke, VladimirDimitrievich, bslf, and ctmg. Yep, those bans were deserved.

Post

matt42 wrote:How is correct alignment guaranteed? would I have to do something like this on windows:
It depends on the compiler, but assuming MSVC, then unless you specify something other than the defaults (for variables or with compiler switches) you can generally expect any primitive data types of 8-bytes or less (including floats and doubles) to be naturally aligned. You can also expect SSE types (__m128 etc) to align properly (ie 16 bytes) in stack (at least old version of GCC didn't do this, but never had problems with MSVC).

For x86 on windows, malloc will align up to 8 bytes (assuming the allocation size is at least 8 bytes). You can request more with _mm_malloc (and overload new/delete to use _mm_malloc/_mm_free) if you need SSE or whatever types. While the documentation is a bit messy on structure alignments, empirical experience suggests that SSE types (__m128) align to 16 bytes inside structures (and classes obviously, and assuming no special /Zp switches or #pragmas at least; the structure itself still needs to be in stack or allocated with _mm_alloc). I'd guess it's a special case somewhere inside the compiler.

http://msdn.microsoft.com/en-us/library/Aa290049 has some info if you feel like reading... and then there's the documentation for the align declarations and /Zp switches and so on...
Is the surround delay example in the VST2.4 not thread safe?
Can't remember what the VST2.4 code does. Can't be bothered to check.

Unless you have a general thread-safe mechanism for data that is accessed from multiple threads, then seemingly minor changes in source code can sometimes result in unexpected changes in thread-safety of the generated object code.

What this means is that you could use a method that is technically not guaranteed to result in thread-safe code, but it might still work perfectly fine, because the compiler happens to create thread-safe code anyway. Another consideration is different compilers; back in the days there was a period during which the Linux kernel wouldn't compile right with the (then new) GCC3 series, because the new GCC versions were far more aggressive about optimizing variables not tagged "volatile." The less aggressive old compilers just happened to generate safe code anyway.

Post

Everyone here is wrong. For an average VST 2.4 plug-in, with atomic parameters (which by definition are all 32 bit floats), there is generally no need for your code to be thread-aware.

The only exception I've found to this was either with eXT or Orion (I forget which offhand) which sometimes likes to send editor setParameter changes while the call to editor close IS STILL EXECUTING. This is obviously idiocy and is entirely the host's fault.

Fortunately there's an easy fix. Put a critical section around the entire contents of editor::setParameter() and editor::close() so they can't execute at the same time.

You should also expect effect::setParameter to only ever be called BETWEEN calls to your process function. So as long as you convert your floats into whatever internal form of data your process needs INSIDE your setParameter function, you don't need to worry about half-formed data being used by your process function.

This is of course all by design, but Steinberg makes hosts, so they were happy to under-advise in their sparse documentation about best practices for threading on the HOST side. On the PLUG-IN side, you can ignore it EXCEPT for this one thing...

The only concession I make to multithreading in VST plug-ins is to put a critical section around editor::setParameter() and editor::close(). That's it. Anything else you think you need to be thread-aware for is a logic bug (well, unless your editor is sending structures to the effect, which is NOT how VST plug-ins are intended to work, but I suppose you could hack something like this together).

Other than this single concession for one or two poorly designed hosts, a VST 2.4 plug-in can effectively ignore threading.

Now a bunch of people are going to tell you I'm wrong. Ignore them, they think too much.
Last edited by AdmiralQuality on Mon Jul 04, 2011 12:40 am, edited 1 time in total.

Post

hibrasil wrote:I noticed iPlug has mutex locks all over the place
That's a bad sign. Anytime the GUI and audio vie for the same lock you get 'priority inversion' - in simple language:
- The GUI has low priority.
- The audio has a high priority.
- A Lock causes one thread to wait on the other.
Therefore any lock can cause the audio to wait on some SLOW GUI operation, like redrawing a large section of screen. Result = glitches, dropouts, stuttering.

To be fair on the creators of iPlug, VST 2.4's threading model is horrible and archaic. Sometimes you need to resort to locks just to get the thing to work at all.

Relying on atomic operations:
This is the next most amature method, and it's tempting because in the most simple cases it works fine. Like when the plugin has only a few parameters, and there's no complex relationship between the parameters. With this method you can adjust any one parameter cleanly, but if you need to update two 'at once', like two coefficients on one filter, you're out of luck. The worst part is the 'un-testability' because 95% of the time the two parameters will update during the same timeslice, giving you a false sense of security. Typically it's not till after you release the plugin that reports of random unpredictable crashes start to trickle in.

Why should 1000 newbie coders be set loose trying to write their own complex bug prone inter-thread code?
It's quite possible to write a plugin API that handles the threading issues for you. I think any good plugin SDK should provide a ready-to-use solution to the problem.

Post

Jeff McClintock wrote: Relying on atomic operations:
This is the next most amature method, and it's tempting because in the most simple cases it works fine. Like when the plugin has only a few parameters, and there's no complex relationship between the parameters. With this method you can adjust any one parameter cleanly, but if you need to update two 'at once', like two coefficients on one filter, you're out of luck.
Relying on atomic operations is perfectly safe. Relying on atomic operations to provide anything other than independent atomic operations on atomic pieces of data is not. This is hardly a problem if the parameters are independent (like they usually are in a simple plugin). It is typically perfectly safe to update eg "cutoff" and "resonance" parameters independently (say at the beginning of processWhatever() or something), then use audio-local copies of those to calculate actual coefficients. Even if the parameters where out of sync, the coefficients will always make sense. Trying to update complete sets of coefficients (or other complex data) using atomic operations is obvious braindead.

Anyway regarding priority inversions, one possible work around is to never wait for a lock in audio thread: attempt to grab the lock, but if you'd have to wait just try again next round (ie specify zero timeout or whatever). I actually use this with visualization data form audio to GUI, but it's not really ideal for parameter updates which you typically don't want to delay arbitrarily.

Post

Jeff McClintock wrote: It's quite possible to write a plugin API that handles the threading issues for you. I think any good plugin SDK should provide a ready-to-use solution to the problem.
Absolutely. And the VST2.x SDK was one of them. Unfortunately you could only ignore threading on the plug-in side, and a bunch of "clever" host developers did some insane things on their host-side implementations, which us plug-in developers get blamed for if we don't build-in contingencies for.

But like I said, for VST2.4 (and probably 2.3 as well), as far as I know the editor::close/setParameter concurrency problem is the only fix that demands any thread-aware coding.

Post

mystran wrote:Anyway regarding priority inversions, one possible work around is to never wait for a lock in audio thread: attempt to grab the lock, but if you'd have to wait just try again next round.
Cool idea. That would prevent stuttering etc.

What I did in SynthEdit is a 'non-blocking ringbuffer'. You can push a message on in one thread, it pops out in the other thread.
I overloaded the equals operator, so the coder can write...

GainParameter = 0.5;

The SDK does the hard work, pushing a message onto the buffer, sending it to the other thread, setting the variable, then notifying your plugin that 'parameter #5 just changed'.

I'm gonna release it as open-source, mayby that will help lower the barriers a little for new plugin coders.

Post

I <3 this thread.
Image
Don't do it my way.

Post

AdmiralQuality wrote:You should also expect effect::setParameter to only ever be called BETWEEN calls to your process function.
That's not specified anywhere in the VST SDK 2.x, so actually you shouldn't expect anything of the kind. Just that you've been lucky so far doesn't mean that this is always so; there might be a host you haven't tested yet that handles this differently.
AdmiralQuality wrote:Now a bunch of people are going to tell you I'm wrong. Ignore them, they think too much.
I'd rather say that you don't think enough. Or rather - a little bit of defensive programming can spare you a lot of spurious problems with your PlugIn.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

arakula wrote:
AdmiralQuality wrote:You should also expect effect::setParameter to only ever be called BETWEEN calls to your process function.
That's not specified anywhere in the VST SDK 2.x, so actually you shouldn't expect anything of the kind. Just that you've been lucky so far doesn't mean that this is always so; there might be a host you haven't tested yet that handles this differently.
AdmiralQuality wrote:Now a bunch of people are going to tell you I'm wrong. Ignore them, they think too much.
I'd rather say that you don't think enough. Or rather - a little bit of defensive programming can spare you a lot of spurious problems with your PlugIn.
It can and therefore should be the host's responsibility. Why make any conflict possible?

Post

AdmiralQuality wrote:It can and therefore should be the host's responsibility. Why make any conflict possible?
That may be the ideal world for a PlugIn, but, just like in real life, you have to be acknowledge that you'll likely meet situations where you're not pampered top to bottom and all is done for your cozy comfort.

Just like I, mainly a host developer, have to acknowledge the fact that there are quite some PlugIns out there that don't play by the rules "as they should be", according to the "Book of Arakula", Chapters 1-3 8-), you have to acknowledge the fact that not all hosts are equal. In my opinion, it is a bad idea to ignore that and blindly assume that all will be just the way you'd think logical. Paying a little attention to things that might go wrong, or in another direction than you think they should, is never bad. Can prevent a lot of headache.

BTW, I could also argue that it should be the PlugIn's responsibility - it might be interested in getting changes as fast as possible, and that would mean that they're sent from the UI (or any other) thread whenever an external event (mouse, MIDI, OSC, whatever) necessitates them, at once.

I don't, mind you, and am totally with you in saying that things should be as conflict-free as possible. The problem is that there's such a lot of ambiguity in the VST definition, and in quite a lot of cases each of the possible interpretations can be defended... and you'll surely find it implemented in some arcane host or PlugIn, too.

You need to be prepared.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

AdmiralQuality wrote:Everyone here is wrong. For an average VST 2.4 plug-in, with atomic parameters (which by definition are all 32 bit floats), there is generally no need for your code to be thread-aware.
Well, the potential hazard is when you need to convert a set of parameters into another set of coefficients. In this case I'd still advocate doing a copy (unless you always read each parameter exactly once) to processing-local variables such that concurrent setParameter (while you're calculating coeffs) won't lead to any race conditions.
The only exception I've found to this was either with eXT or Orion (I forget which offhand) which sometimes likes to send editor setParameter changes while the call to editor close IS STILL EXECUTING. This is obviously idiocy and is entirely the host's fault.
Yeah, I've had this happen in other hosts too.
Fortunately there's an easy fix. Put a critical section around the entire contents of editor::setParameter() and editor::close() so they can't execute at the same time.
Trouble is this is another priority-inversion trap since setParameter often gets called in audio threads.
You should also expect effect::setParameter to only ever be called BETWEEN calls to your process function.
This isn't really true as setParameter calls originating from GUI thread can occur concurrently with process functions, or even other calls to setParameter itself.
The only concession I make to multithreading in VST plug-ins is to put a critical section around editor::setParameter() and editor::close().
Well, doing that will fix any race conditions. You can still get potential priority inversions, though Windows not being RTOS those will get resolved eventually anyway (though sometimes after some audio drop-outs).

Post

mystran wrote:
The only concession I make to multithreading in VST plug-ins is to put a critical section around editor::setParameter() and editor::close().
Well, doing that will fix any race conditions. You can still get potential priority inversions, though Windows not being RTOS those will get resolved eventually anyway (though sometimes after some audio drop-outs).
Note that that's editor::setParameter I'm talking about, not effect::setParameter.

I'm not entirely sure about setParameter being called concurrently with process. I'll test for it. Could be I've been getting away with it because I'm 100% atomic. (I like how that sounds. ;) )

Post

AdmiralQuality wrote:
mystran wrote:
The only concession I make to multithreading in VST plug-ins is to put a critical section around editor::setParameter() and editor::close().
Well, doing that will fix any race conditions. You can still get potential priority inversions, though Windows not being RTOS those will get resolved eventually anyway (though sometimes after some audio drop-outs).
Note that that's editor::setParameter I'm talking about, not effect::setParameter.
Oh ok. Sure. I don't even have such a method anywhere, so I guess this must be something specific to VSTGUI?

Post Reply

Return to “DSP and Plugin Development”