COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :
Old 08-13-2015, 03:53 AM   #1
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default WDL-OL threading questions

I now have two issues that need threading. One is, when passing data from the ProcessDoubleReplacing to a GUI class and the other is when passing "events" from the GUI class to the plug-in class.

However, I cannot find information regarding how threading in WDL-OL works and how to do the thread locks that I need.

So what I need is a short tutorial on how to do the threading.

Last edited by mviljamaa; 08-30-2015 at 01:42 PM.
mviljamaa is offline   Reply With Quote
Old 08-14-2015, 04:31 PM   #2
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

No simple answers for this...

You should watch out for variables that can potentially be shared by multiple threads and then do appropriate synchronization around the use of those variables.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 08-15-2015, 03:19 AM   #3
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

What I don't understand is, how many mutexes should I be using and where should they be placed?

That is, is mutex a class specific object?

In the examples I'm seeing IMutexLock used in the audio processing class (IPlugMyPlug) in various parts, which I read to mean that these parts (e.g. bodies of functions) of code can never be executed simultaneously (if some part that has the mutex locking is executing, then the other parts cannot be accessed before the one running has ended).

So if I need mutexes for controlling when the audio processing is run and when the GUI processing is run and they pass data between. Then how many, where to place them and how to operate them?

Another is, when there's a data, say a std::vector, that's accessed from the GUI and the audio processing, then how should this be guarded against, so that both cannot read or write it at the same time?
mviljamaa is offline   Reply With Quote
Old 08-15-2015, 06:41 AM   #4
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
Another is, when there's a data, say a std::vector, that's accessed from the GUI and the audio processing, then how should this be guarded against, so that both cannot read or write it at the same time?
std::vector is like any other variable, so the same rules rules apply as for anything else that must be protected from access by multiple threads at the same time.

IPlug has an internal mutex object per plugin object, that can be used with the IMutexLock object. (That is, when that is instantiated, things are locked until the current scope ends.) More complicated scenarios might require you to manually lock and unlock the internal mutex or even add your own additional mutex objects. But the basic IMutexLock method should be enough for basic stuff. It's very difficult to say when will you need to do the locking, it will completely depend on your particular code and how different threads will be able to change shared variables.

Note that the IMutexLock class is named misleadingly. It is not itself a mutex/lock, it just uses an existing mutex object that it gets from the "this" variable passed into its constructor.

Some example code...

Code:
void MyPlug::OnParamChange(int paramIdx) 
{ 
  IMutexLock lock(this);
  ... deal with the parameter change... 
}

void Reset() 
{ 
  IMutexLock lock(this); 
  ... do the resetting stuff...
}


void MyPlug::clear_vector()
{
  // the cout line is executed outside the mutex lock
  std::cout << "Clearing the vector...\n";
  IMutexLock locker(this);
  // Now we are in the locked region until the end of the function
  m_vec.clear();
}

void MyPlugProcessDoubleReplacing(double** inputs, double** outputs, int nFrames);

{
  // "mutex already locked for us", so you don't need to 
  // or even must not lock the IPlug internal mutex here
  // yourself
  if (m_vec.size()==0) { // do whatever to handle the vector being empty... return; }
  // etc
}
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.

Last edited by Xenakios; 08-15-2015 at 06:52 AM.
Xenakios is offline   Reply With Quote
Old 08-15-2015, 07:19 AM   #5
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

If there's a case when another object accesses another, then the mutex should be activated in which object?
mviljamaa is offline   Reply With Quote
Old 08-15-2015, 07:29 AM   #6
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
If there's a case when another object accesses another, then the mutex should be activated in which object?
Mutexes don't actually have anything to do with variables or objects, they are just a way to control code execution order when there are multiple threads. It's a side effect of that code execution ordering that reads and writes of variables will become "protected".

Just do your mutex locking in your plugin object methods, that is probably the easiest way. Otherwise you would need to pass around the plugin object pointer to be able to use IMutexLock etc.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 08-15-2015, 07:38 AM   #7
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

How do I see the threads then? I don't know what threads my code is currently using. Or how many there are? Do I need to create them?
mviljamaa is offline   Reply With Quote
Old 08-15-2015, 07:47 AM   #8
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
How do I see the threads then? I don't know what threads my code is currently using. Or how many there are? Do I need to create them?
The host program using your plugin will create the threads. There may be one, or many threads. You shouldn't really need to know the number of threads used by the host.

All the GUI/drawing stuff runs in the GUI/main thread. It should be reasonably clear which methods are run by that thread. (Constructors, destructors, IControl::OnMouseDown, IControl:: Draw etc) The host and all the plugins use the same GUI/main thread, by the way.

ProcessDoubleReplacing is most likely run by one of the host's audio threads. (It's so rare it wouldn't be that you should always consider ProcessDoubleReplacing as if it is run by another thread than the GUI/main thread.)

Then there are trickier methods like OnParamChange which may be run by either the GUI thread or by the audio thread. OnParamChange would be run by the GUI thread when the user manipulates a control connected to a parameter. But it would be run by the audio thread when a parameter has an automation envelope in the host.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.

Last edited by Xenakios; 08-15-2015 at 07:53 AM.
Xenakios is offline   Reply With Quote
Old 08-15-2015, 08:19 AM   #9
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

So it doesn't matter which object I pass to the IMutexLock? I.e. I can pass "this" in the IPlug context and it's the same as passing it "SomePanelControl*" in the IPlug context. Where SomePanelControl* is some control that accessed some IPlug method.
mviljamaa is offline   Reply With Quote
Old 08-15-2015, 08:39 AM   #10
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
So it doesn't matter which object I pass to the IMutexLock? I.e. I can pass "this" in the IPlug context and it's the same as passing it "SomePanelControl*" in the IPlug context. Where SomePanelControl* is some control that accessed some IPlug method.
It matters a lot, IMutexLock's constructor only accepts an IPlugBase pointer (and by extension pointers to subclasses of IPlugBase). So passing something else into the constructor of IMutexLock would be a compile error.

Remember, IMutexLock itself isn't a mutex, it only uses the internal mutex object of IPlugBase. The "this" passed into the constructor is just for the purposes of getting access to the mutex object in the plugin, it has nothing to do with what variables you are trying to protect from multiple threads access.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 08-15-2015, 08:43 AM   #11
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

Quote:
Originally Posted by Xenakios View Post
It matters a lot, IMutexLock's constructor only accepts an IPlugBase pointer (and by extension pointers to subclasses of IPlugBase). So passing something else into the constructor of IMutexLock would be a compile error.

Remember, IMutexLock itself isn't a mutex, it only uses the internal mutex object of IPlugBase. The "this" passed into the constructor is just for the purposes of getting access to the mutex object in the plugin, it has nothing to do with what variables you are trying to protect from multiple threads access.
So I can use the same Mutex object for all protection cases? Even if they occured somewhere else. Given that I have a reference to IPlug of course. But if I don't, then it doesn't matter if I create more mutexes? I can merely use them just like I used the one in the IPlug?

Or i.e. the mutex does not relate to the object that contains it? It's merely based there by some convenience? Couldn't I then use one global mutex for all mutex locking?
mviljamaa is offline   Reply With Quote
Old 08-15-2015, 08:59 AM   #12
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
So I can use the same Mutex object for all protection cases? Even if they occured somewhere else. Given that I have a reference to IPlug of course. But if I don't, then it doesn't matter if I create more mutexes? I can merely use them just like I used the one in the IPlug?

Or i.e. the mutex does not relate to the object that contains it? It's merely based there by some convenience? Couldn't I then use one global mutex for all mutex locking?
At this point I would strongly suggest against using more mutexes in the code. That would only make things more complicated. You don't seem to completely yet grasp why and how even one mutex is used, so using more would just lead to more problems. Note that "somewhere else in the code" doesn't have anything to do with threads. Any code can be run by any thread, it doesn't have anything to do with source code files, classes or class methods. So yeah, using the same mutex object will (mostly) work and I'd recommend you keep things that way.

You could use a global mutex object but has the huge downside in something like VST plugins that all instances of the same plugin in the host would share the same mutex object. That would make it impossible for the host to do multithreaded audio processing on multiple CPU cores, among other disastrous problems!

The IPlugBase object has the mutex as a member variable just for the convenience of the developer. The mutex objects could live elsewhere, but the plugin base class is a nice logical place for it, even though the mutex as such has nothing to do with the plugin object. (At a lower level, a mutex/critical section is an operating system object that has no idea what it is supposed to be used for. It just blocks the execution of code by threads as needed.)
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 08-30-2015, 01:42 PM   #13
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

So any ideas what I should do in the following case:

In ProcessDoubleReplacing I'm doing a call

guipanel->UpdateFFT(mSTFT);

That passes the Gamma STFT object to the gui where it's saved into a std::vector and plotted in the Draw function until another frame arrives and the vector is refilled. Since this is in ProcessDoubleReplacing, then there's some Mutex already locked.

The FFT plot displays some occasional glitches, which I was told, might be because of threading.
mviljamaa is offline   Reply With Quote
Old 08-30-2015, 01:59 PM   #14
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Getting data correctly and quickly from an audio thread into the GUI thread is an infamous problem and there may not be an easy solution that works for everything...

I haven't worked on this problem space too much lately, so I can't offer any fresh insights into it. In the past in Qt-based code I've used Qt's signals to pass some data from the audio thread into the GUI thread. IPlug/WDL unfortunately don't have anything like Qt's signals/slots. These days in Juce-based code I would first try using Juce's MessageManager::callAsync method and benchmark if it is too heavy to be used in the audio thread. Unfortunately, something like MessageManager::callAsync doesn't exist in IPlug/WDL, either.

Maybe someone else who has more recent IPlug experience could chime in on this...
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 08-30-2015, 02:23 PM   #15
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

Actually seems like it got fixed by not passing the STFT around, but constructing the std::vector in the ProcessDoubleReplacing "loop" and then passing the vector. This way there are no "race conditions" happening in the STFT. I.e. that the STFT updates while the GUI is still reading it.
mviljamaa is offline   Reply With Quote
Old 08-30-2015, 02:33 PM   #16
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
Actually seems like it got fixed by not passing the STFT around, but constructing the std::vector in the ProcessDoubleReplacing "loop" and then passing the vector. This way there are no "race conditions" happening in the STFT. I.e. that the STFT updates while the GUI is still reading it.
That's not an ideal solution either. When you construct an std::vector, you are making a heap allocation that takes an indeterminate amount of time to accomplish. (Roughly the same would happen if you you used "array new", ie float* buf=new float[bufsize] or similar.) One should generally avoid doing operations in the audio thread that can take a "random" amount of time to run. That said, if it seems to work, it may be good enough...
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 08-30-2015, 02:40 PM   #17
mviljamaa
Human being with feelings
 
Join Date: Jun 2015
Posts: 348
Default

It seems to work. And I don't know what kind of symptoms should I be seeing/hearing, if it wasn't working.
mviljamaa is offline   Reply With Quote
Old 08-30-2015, 02:52 PM   #18
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by mviljamaa View Post
And I don't know what kind of symptoms should I be seeing/hearing, if it wasn't working.
Audio glitches. If you are using Reaper to test your plugin, you might want to check how it behaves when you turn off Reaper's anticipative fx processing. But it might be a good idea to try some other hosts too, while you are it. Also you should test what happens when you have many (up to dozens of) instances of your plugin running in the host.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 05-01-2017, 05:32 AM   #19
Soundbytes
Human being with feelings
 
Soundbytes's Avatar
 
Join Date: May 2006
Posts: 58
Default never allocate memory in the audio thread

I know this is an old thread. Still since this forum is the main source of information for new users I cannot leave it uncommented.

Here is my advice:
Do NOT allocate memory in the audio thread. Don't do it! Never!
Even if you find that it does not cause any glitches on your machine.
What computers do your customers use? And which Audio DAW? Can you test on all these configurations? if not then always consider the worst case scenario before you decide on how to implement a feature.

If you cannot avoid memory allocation in the audio thread at all then make it deterministic by using your own memory pool.

If you still have a different opinion then please read this:
http://www.rossbencina.com/code/real...ts-for-nothing

thanks
__________________
www.soundbytes.de
Soundbytes is offline   Reply With Quote
Old 05-01-2017, 05:48 AM   #20
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by Soundbytes View Post
Do NOT allocate memory in the audio thread. Don't do it! Never!
There's one exception where I would disagree with that. If not allocating memory in the audio thread would cause a crash to happen because some buffer isn't large enough, I would rather allocate than allow the crash to happen. After all, which is worse, a crash or getting a momentary glitch in the audio? Of course things should be set up so that situation would arise very rarely. (Plugins formats usually have things anyway to allow the host to tell about buffer sizes before the audio streaming starts...)
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 05-02-2017, 06:01 AM   #21
Soundbytes
Human being with feelings
 
Soundbytes's Avatar
 
Join Date: May 2006
Posts: 58
Default

Quote:
After all, which is worse, a crash or getting a momentary glitch in the audio?
Never ship a plugin when there is the slightest chance that one of the above can happen!
Imagine a musician using your plugin with headphones on. Imagine a stage performance in front of a large crowd.
Now figure what harm a single audio glitch will do to to the ears of the listeners, to the reputation of the performer and to your own reputation.
(and let's not forget to the reputation of the framework you are using.)
Why would you want to take such risks? There are proven ways to safely execute these operations. Here are some hints:

- Do all memory allocation that you are able to precalculate in open(). (Do NOT do it in the plugin constructor - the plugin gets instantiated and destructed when the vst-host is is started. )
- If you can not calculate your memory requirements in advance then use a dedicated thread to allocate the memory. Hand an atomic pointer to the audio thread once the memory allocation is completed.
- Never block the audio thread!
__________________
www.soundbytes.de
Soundbytes is offline   Reply With Quote
Old 05-02-2017, 04:05 PM   #22
stw
Human being with feelings
 
stw's Avatar
 
Join Date: Apr 2012
Posts: 279
Default

Quote:
Originally Posted by Soundbytes View Post
- Do all memory allocation that you are able to precalculate in open(). (Do NOT do it in the plugin constructor - the plugin gets instantiated and destructed when the vst-host is is started. )
Could you please elaborate on that? I don't get what should be wrong in allocating memory in the constructor. What could happen if the plug is constructed/destructed succesfully? And when will open() be called?
stw is offline   Reply With Quote
Old 05-02-2017, 04:24 PM   #23
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by stw View Post
I don't get what should be wrong in allocating memory in the constructor.
If the allocation is large, it might slow down the host's plugin scanning process. (But properly coded hosts will do that pretty rarely anyway...) It doesn't seem like a big concern really.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 05-03-2017, 04:50 AM   #24
Soundbytes
Human being with feelings
 
Soundbytes's Avatar
 
Join Date: May 2006
Posts: 58
Default

Quote:
Originally Posted by stw View Post
Could you please elaborate on that? I don't get what should be wrong in allocating memory in the constructor. What could happen if the plug is constructed/destructed succesfully?
I do not allocate memory in the plugin constructor for several reasons:
(1) Avoiding unneccessary steps will speed up daw startup.
(2) I had some problems with an older version of Sonar that only was able to load my plugin after I moved the memory allocation away from the constructor.
(3) Memory requirements often depend on block size and sample rate. These are not defined (or in many cases have incorrect but ok seeming default values when the daw starts up.)

Quote:
And when will open() be called?
Oops I got that wrong. Of course I meant IPlugBase::Reset().
Note: keep in mind that Reset() is not guaranteed to run only once during the plugin lifetime and take the neccessary precautions.
__________________
www.soundbytes.de
Soundbytes is offline   Reply With Quote
Old 05-03-2017, 05:43 AM   #25
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by Soundbytes View Post
(1) Avoiding unneccessary steps will speed up daw startup.
Has to be a pretty braindead host if it actually loads plugins every time at its startup. (That said, it looks like for example Pro Tools indeed does work like that...)
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 05-03-2017, 06:38 AM   #26
Soundbytes
Human being with feelings
 
Soundbytes's Avatar
 
Join Date: May 2006
Posts: 58
Default

Quote:
Originally Posted by Xenakios View Post
Has to be a pretty braindead host if it actually loads plugins every time at its startup. (That said, it looks like for example Pro Tools indeed does work like that...)
All cubase versions I think up to 5 did it. maybe the more recent versions still work this way. I don't know because I have not used them.
__________________
www.soundbytes.de
Soundbytes is offline   Reply With Quote
Old 05-13-2017, 01:00 AM   #27
stw
Human being with feelings
 
stw's Avatar
 
Join Date: Apr 2012
Posts: 279
Default

Here's a little follow up question on mutexing.
Though i don't have any troubles doing it the "default" way i'd just like to ask how you all are dealing with the mutex lock in OnParamChange()?
Right now the lock is still placed at the beginning of the function. But i'm a little concerned that it locks too much unnecessary parts since in some cases e.g. different GUI functions are triggered,nested param changes are done or small buffers (32/64 doubles) are allocated.
However, maybe it's related to this, that i recognized significant CPU hit when reading automation from Reaper, even if no values are changed (Reaper seems to permanently call OnParamChange when in read mode?). Which brought me to the workaround only to compare for changed data first.

So, is it recommended or maybe required to place the mutex at selected parts in OnParamChange()?

Last edited by stw; 05-13-2017 at 01:20 AM.
stw is offline   Reply With Quote
Old 05-13-2017, 08:26 PM   #28
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

Quote:
Originally Posted by stw View Post
...So, is it recommended or maybe required to place the mutex at selected parts in OnParamChange()?
See my comments here: http://forum.cockos.com/showthread.p...82#post1691082

It doesn't work like the comments indicate. There is a lock held before calling OnParamChange, so it doesn't matter what you do there. My plugin needs to wait briefly for audio processing during effects changes (else make nasty noises), so I had to modify IPlug to relinquish the lock before calling OnParamChange—to make it work like it says it works.
earlevel is offline   Reply With Quote
Old 05-14-2017, 02:20 AM   #29
Youlean
Human being with feelings
 
Youlean's Avatar
 
Join Date: May 2015
Location: Serbia
Posts: 654
Default

Quote:
Originally Posted by earlevel View Post
See my comments here: http://forum.cockos.com/showthread.p...82#post1691082

It doesn't work like the comments indicate. There is a lock held before calling OnParamChange, so it doesn't matter what you do there. My plugin needs to wait briefly for audio processing during effects changes (else make nasty noises), so I had to modify IPlug to relinquish the lock before calling OnParamChange—to make it work like it says it works.
Indeed. It seems that OnParamChange is locking a lot but it is not necessary. You will be pretty safe if you remove all locks.
Youlean is offline   Reply With Quote
Old 05-14-2017, 10:39 PM   #30
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

Quote:
Originally Posted by Youlean View Post
Indeed. It seems that OnParamChange is locking a lot but it is not necessary. You will be pretty safe if you remove all locks.
I assume that by "safe if you remove all locks", you're referring to the framework (at least the part calling OnParamChange). For others reading the thread, I have to add: Of course, it's likely that you want to lock before changing parameters, and unlock immediately after. Otherwise, you can end up processing a buffer of samples with a partially updated filter, for instance, resulting in a glitch when adjusting or automating controls. But, the framework should not lock then call OnParamChange.
earlevel is offline   Reply With Quote
Old 05-15-2017, 12:51 AM   #31
stw
Human being with feelings
 
stw's Avatar
 
Join Date: Apr 2012
Posts: 279
Default

Quote:
Originally Posted by earlevel View Post
See my comments here: http://forum.cockos.com/showthread.p...82#post1691082

It doesn't work like the comments indicate. There is a lock held before calling OnParamChange, so it doesn't matter what you do there. My plugin needs to wait briefly for audio processing during effects changes (else make nasty noises), so I had to modify IPlug to relinquish the lock before calling OnParamChange—to make it work like it says it works.
Thanks for the link. I didn't know that IPlug locks a param change anyway.
So what's the use of the mutex lock in OnParamChange()?
How did you modify IPlug? Did you comment out the locks in IPlugBase and all plug APIs?
stw is offline   Reply With Quote
Old 05-15-2017, 10:19 AM   #32
earlevel
Human being with feelings
 
Join Date: Dec 2015
Posts: 331
Default

Quote:
Originally Posted by stw View Post
How did you modify IPlug? Did you comment out the locks in IPlugBase and all plug APIs?
I don't know that the calling routines need to lock at all, but I left the locks, while unlocking before the call:

Code:
void IPlugBase::SetParameterFromGUI(int idx, double normalizedValue)
{
  Trace(TRACELOC, "%d:%f", idx, normalizedValue);
  { // exclude lock from OnParamChange
    WDL_MutexLock lock(&mMutex);
    GetParam(idx)->SetNormalized(normalizedValue);
    InformHostOfParamChange(idx, normalizedValue);
  }
  OnParamChange(idx);
}
Do similar (limit the scope of the lock with {} block) everywhere OnParamChange and OnParamReset are called. For me, I believe this was only the above, IPlugAAX::UpdateParameterNormalizedValue, and IPlugBase::UnserializeParams.
earlevel is offline   Reply With Quote
Old 05-30-2017, 08:27 AM   #33
Nowhk
Human being with feelings
 
Join Date: Mar 2016
Posts: 234
Default

Quote:
Originally Posted by earlevel View Post
I don't know that the calling routines need to lock at all, but I left the locks, while unlocking before the call:

Code:
void IPlugBase::SetParameterFromGUI(int idx, double normalizedValue)
{
  Trace(TRACELOC, "%d:%f", idx, normalizedValue);
  { // exclude lock from OnParamChange
    WDL_MutexLock lock(&mMutex);
    GetParam(idx)->SetNormalized(normalizedValue);
    InformHostOfParamChange(idx, normalizedValue);
  }
  OnParamChange(idx);
}
Do similar (limit the scope of the lock with {} block) everywhere OnParamChange and OnParamReset are called. For me, I believe this was only the above, IPlugAAX::UpdateParameterNormalizedValue, and IPlugBase::UnserializeParams.
Unfortunately that's not enough for this problem: http://forum.cockos.com/showthread.php?t=186686
It still crash on FL.

However yes, if every "mother" function where OnParamChange is called already lock, I don't see why I should lock again within my override OnParamChange (as tell by framework).

If you choose to keep lock within OnParamChange, you could also consider to "unlock before" functions such as VSTSetParameter

But in both case, you unlock before lock again (within OnParamChange). Not sure why this would improve things.
Why not keep framework as it is and just get rid the lock within OnParamChange (even if IPlug explicitly warn you to do it)?
Nowhk is offline   Reply With Quote
Old 11-01-2017, 05:49 AM   #34
Andi!
Human being with feelings
 
Andi!'s Avatar
 
Join Date: Nov 2015
Location: Germany
Posts: 82
Default

Just to give another idea: what I did is, that I changed the SetParameterFromGUI method how it was mentioned here (no lock for the OnParamChange call, just before). At the beginning of OnParamChange I still use the lock, but just for non audio & automation related parameters I direcly call lock.destroy() after locking it. This prevents the last cpu spikes for me in the audio processing when changing parameters in the gui. For the FL issue I could imagine just to skip the lock in SetParameterFromGUI for this host.
Andi! is offline   Reply With Quote
Old 09-05-2020, 11:33 AM   #35
stw
Human being with feelings
 
stw's Avatar
 
Join Date: Apr 2012
Posts: 279
Default

Old thread but i was suddenly caught by the issue again...

One of my users got a reproducable dead lock in a host called "Numerology" (i didn't know it even exists) when altering a control that was reading automation data. After outcommenting the mutex lock in IPlugBase/SetparameterFromGUI the lock was gone.
Code:
 void IPlugBase::SetParameterFromGUI(int idx, double normalizedValue)
{
  Trace(TRACELOC, "%d:%f", idx, normalizedValue);
    { // exclude lock from OnParamChange
//        if(GetHost() != kHostNumerology)
//            WDL_MutexLock lock(&mMutex);
        
        GetParam(idx)->SetNormalized(normalizedValue);
        InformHostOfParamChange(idx, normalizedValue);
    }
  OnParamChange(idx);
}
First i only disabled the mutex lock only for that host without any issues. But since i got ONE other lock myself on another host and very different settings (and this was the only one ever) after that report i was curious what would happen if i put the lock out generally.
I'm testing it for a week now on different hosts and different systems under all kind of settings (automation etc.) without any glitches or any other issues.
I'm aware that a parameter change should be locked - nevertheless i'm thinking about leaving it out, because i got a prob with it and so far no issue without it.
So my question is: Has anyone ever tried this before or have a deeper insight why this could work or will end in a desaster sooner or later?
stw is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 08:04 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.