Advice about VST locks


#1

I have a plugin with a big ammount of objects/structs that processBlock() read, thousands of primitive variables. And, like usual, the interface read/write on those objects and create and delete them.

I've reading a lot about CriticalSections, ReadWriteLock, semaphores, etc but I'm confused which approach should I take. It seems that coarse lock it's not a good idea, so I was thinking in few ReadWriteLock and I have doubts between standart Juce ReadWriteLock or TheVinn's one [it seems that TheVinn's one is faster for simple purposes].

It should be something like doing a read-lock when the processBlock() or the interface try to read one of these shared objects, and doing a write lock when the interface try to write, right? Should I use the AudioProcessor::getCallbackLock() for something?

I'd appreciate any advice :)

 

 


#2

Anyyway, if you cannot perform any locking in the processBlock call... how can you thread-safe modify with the interface the big structure of data adding or deleting element to it? is it possible in a VST?


#3

There's a lot in this thread.  It needs turning in to an FAQ or something: 

http://www.juce.com/forum/topic/whats-best-practice-gui-change-notification

See if that helps.  


#4

Thank you, but unfortunately that is not the problem. I know how do that my interface reacts to processBlock changes.

The problem is that I have a massive ammount of data that processBlock read, the interface read and the final user modifies [with a MIDI controller or his mouse]. So, if processBlock read that data while the user is changing a part of it, it can exist some problems. But I read that I cannot do any lock in the processBlock thread... so what can I do with Juce?

The other option is having two copies of the same data, and use a ReferenceCounted pointer to switch between them, but it doesn't seem a good solution with that ammount of information that is linked to other interface widgets.

I spend hours looking for the solution in the forum or another example projects, but I don't find nothing similar.


#5

I think it's the same problem as that thread. But on a larger scale.

You've got two threads I take it, the user-interface and audio.  How much data do you have?

Are you modifying it on both threads, or just the user-interface thread?  

How much data changes each time?

You can use AbstractFIFO to transfer the data from one thread to another without a lock.


#6

Exactly how large is this data and what format is it in? Is is POD, a ValueTree or some custom structure that holds pointers to lots of other bits of memory.

Having two copies (one that the UI modifies and then passes off to the audio thread) is almost certainly the quickest way of doing this. The only caveat would be if the audio thread modifies the data which would have to be reflected in the UI. If the audio thread is read-only, an atomic swap of the structure it is referencing would be the quickest way.


#7

The user data is variable, but usually it goes from 2.000 to 30.000 primitive variables (95% int32). The data is hold in custom structs/classes, and few of thems are broadcasters to the interface.

There is an small part from that data that audio thread exclusively modify, the interface thread only read from it with a timer, so it's no problem.

The interface is who does the most of the job modifing that data. The change in each block is variable, usually zero. But if the user is moving lots of nodes, it can be around 4.000 primitive variables. It's weird, but possible.

The problem doing atomic swaps isn't the data size, but the massive ammount of broadcasters that it contains to do the interface reacts. It's a very complicated gui. (around 70.000 lines). So if I do an swap, i should assign and unassign all the broadcasters again. Unless there is an easy way and I'm not aware of it.

Thanks for pointing me to AbstractFIFO, I'll do some research about it :)


#8

As well as Dave's fine suggestion other options are to: 

- Clone the data entirely via a FIFO. 

- To pass messages in the FIFO describing the user changes and use those to keep a copy up to date in the audio thread. 

- To ignore the problem and assume that worst case you'll get some tiny glitch no-one will care about.  Obvious no good if you're creating and deleting objects as you will probably hit a bit of unallocated memory at some point, which will bite.  But if you're dealing with a big fixed allocation it's probably less of an issue. 

I've been creating garbage collected objects in the GUI thread and passing them via a FIFO to the audio thread as required. But that might not be ideal for your problem.