Is BinaryData safe?

I am designing a system around juce::SharedResourcePointer and afaik they are shared across plugin boundaries, not just instances.

As in two different plugins which include the same class as SharedResourcePointer would see the same object. Is that correct?

Following that thought, if two plugins have the same name in their BinaryData, would they see the same static data?
My concern is, when a generic system uses the same names but different data…

Thanks for clarification

Yes, generally that’s correct and it’s why in most cases using static design patterns like singleton is a no-no in audio plugins because it’ll be accessible for all instances of the plugin. Not something you normally want in a singleton.

But for a binary resource in BinaryData you should be ok.

With the caveat that some hosts have the capability to genuinely fire up plugins in their own process with nothing shared. Bitwig is such a host, but it is optional. It’s something to bear in mind.

Awesome, thanks for confirming.

I don’t understand why the static in BinaryData is different from the static in the SharedResourcePointer, but I take your word for it :slight_smile:

Chances are the BinaryData structs can get inlined anyway?

I am aware of the out of process option. It would be the same like running the plugin n two different hosts, in which situation the plugin would still have to be functional.

To be clear they should only be shared across instances of the same plugin not all plugins so I don’t think it would be possible to have different binary data as that would be a different dynamic library?

1 Like

Oh, that would be good. I was told otherwise for the SharedResourcePointer but I lack the intimate knowledge of dynamic loaded libraries like plugins.

Thank you for chiming in.

It’s worth baring in mind that the identifier you use in code (i.e. BinaryData::whatever_wav) is not the same as the identifier used by the compiler/linker.

I.e. if you have lib_A and lib_B which both include lib_C.h which declares a global static variable, both lib_A and lib_B receive their own copy of that variable - printing the address of the variable from lib_A will give a different address to the exact same variable in lib_B.

So as Anthony said - if you have multiple instances of the same plugin, they’ll use the same static data because they’re from the same library (hence why singletons are dangerous). However instances of separate plugins that come from different dynamic libs will use their own separate static data, even if that data comes from a common dependency, and/or is named the same in the same scope.

You could easily check that by writing two separate plugins, both with the same binary data, and printing the address while loading them both in the same instance of a host.

2 Likes

Great, thanks for the link and the explanation.

1 Like

It can be useful to have a static shared singleton, say for activation of the plugin or managing samples, global settings…

Well, as long as you make it very clear what your intention is and bear in mind thread safety because the singleton is accessible by all instances of the plugin, yes. You can.

Nice link @ImJimmi I was struggling to find a good one.

In the context of plugin activation, say you have an ActivationManager that is a static shared singleton with an std::atomic bool isActivated.

On one plugin instance, the user clicks activate, which grabs the ActivationManager, who then pings a backend and checks out if all is good. Once this is complete it sets isActivated to true. Then if the user goes to another instance of the plugin it should also safely be able to check isActivated and see that it is true.

AFAIK, with atomic, since only ActivationManager will ever set isActivated you are totally fine. Even in the tiny event that two threads were to set it at exactly the same moment, worse case is that you might read a true instead of a false.

I’m open to correction on all of the above!

As long as you bear in mind that some DAWs - such as Bitwig - support sandboxed processes so nothing is shared. In your case it’ll just be that all sandboxed processes will go through the activation check.

In the example you gave, the worst that’ll happen is that you get a race condition and the activation check happens more than once. If your situation is somewhat idempotent as I suspect it is in this case, then it won’t matter.

A mutex member declared in ActivationManager would fix this but not for the case of the sandboxed processes.

But the case remains that some uses of singletons in plugins is extremely dodgy.

For example, it’s fairly common to want access to the PluginProcessor instance. Instead of passing this around all the time you might have the idea of making a singleton that stores a reference to it.

Bad idea. The singleton will hold a reference to the first PluginProcessor instance that it gets initialised with. This means that other plugin processes will also reference the singleton with that original PluginProcessor instance instead of the one that’s relevant to them. Things will quickly get hairy.

Singletons in plugin land become some kind of hyper global entity rather than the usual canonical version c++ programmers are used to.

Makes total sense @Nitsuj70

On a related topic, say the static shared singleton ActivationManager allowed instances to add themselves as listeners…when an event is posted by the ActivationManager it will trigger the callback in each instance of the plugin. In the callback of each instance you can do something like callOnMessageThread and pass in what ever you need from the ActivationManager

The issue is adding and removing listeners to the ActivationManager and making sure that if you are doing this on the thread that creates the plugin, that you don’t ever have a situation where the daw is blocked.

It can work fine and be safe, but only if you keep things simple, say ActivationManager triggering sending back basic messages about its state.