Unfortunately I can’t share the exact code but I can try to explain what happened:
I had a block in my code that would run if the user had selected to run the plugin in “mono” but the buffer provided still had two channels (which happens in certain DAWs) So something like…
//declared in header
int channelSetting { 0 };
//deep inside processBlock().....(on the Audio thread, importantly)
if (getChannelSetting() == 1 && getMainBusNumInputChannels() == 2)
{
//do a bunch of stuff to account for this particular condition
}
//then execute the rest of the processBlock() code
where getChannelSetting was something like
int getChannelSetting()
{
return channelSetting;
}
But I found that this worked in Debug builds and when the optimization was disabled, but not Release builds compiled with optimization.
The problem here (*I think!!) was that getChannelSetting() returned a non-atomic member int which was set elsewhere on the message thread in something like
void setChannelSetting (int newSetting)
{
channelSetting = newSetting;
}
which is cleary a race condition when setChannelSetting() could be called from the message thread, and getChannelSetting() could be called from the audio thread. One thread could try to write to and the other could try to read from the member int channelSetting simultaneously.
The solution (or one solution at least) was to make the member channelSetting a std::atomic<int>, and re-write the code as follows:
//declared in header...
std::atomic<int> channelSetting { 0 };
//setters/getters
void setChannelSetting (int newSetting)
{
channelSetting.store(newSetting);
}
int getChannelSetting()
{
return channelSetting.load();
}
//deep inside processBlock().....(on the Audio thread, importantly)
const int cs = getChannelSetting();
if (cs == 1 && getMainBusNumInputChannels() == 2)
{
//do a bunch of stuff to account for this particular condition
}
//then execute the rest of the processBlock() code
So I think the optimizer assumed that the channel setting was ALWAYS 0 since it was initialized to 0, and since it never thought it could be changed from another thread, so that original condition never passed. My understanding is that std::atomic lets the compiler/optimizer know not to make this assumption, but I know nothing about how that actually works.
Hopefully that makes some sense!