Is there any downside to using SharedResourcePointer?

Just a quick question,

Since discovering SharedResourcePointer I feel like I’m using it all the time for various “Manager” classes. I just want to check is there any downside to using SharedResoucePointer? It seems pretty effiecient and far easier than passing a reference to every class that would need the resource.

2 Likes

I would be careful, a Singleton that is a Manager seems like it could be a bad idea. I’m not sure if you are working on a plugin or an app, but now if you are working on a plugin they are all sharing the same manager, is this really what you want? You may only want one of the now, but in future will you need many? Will your code be harder to refactor? Will he be harder to unit test because many parts are sharing the same manager and now they are sharing global state?

Hmm… I am working on plugins and I hear what you’re saying but I feel like it is what I want actually? I thought the benefit of SharedResourcePointer was that it can act like a singleton but is a bit more careful about things.

For example, I have a class called “Profile”. Profile Usually contains a ValueTree with things like settings, user info etc. It’s pretty convenient if many components can just create a profile SharedResourcePointer (whether in the same instance or across several) and access the profile value tree.

I guess my main concern would be if multiple plugin instances tried accessing and possibly rewriting the value tree at the same time? I’m honestly not too familiar with multi thread safeguarding. Maybe what I just proposed is an absolute catastrophe waiting to happen lol, that’s why I’m asking.

As you mention, you indeed have a potential thread safety issue. That being said, it is fairly easily to solve. Your SharedResourcePointer should not hold directly a ValueTree, but instead a custom class that you build and that serializes access using locks for example. Of course, this comes with a trade-off of performance so you should be mindful about that.

Also, because the resource is shared, you can lose synchronization pretty easily. Let’s say plugin A reads the shared data and displays it on screen. In the meantime, plugin B edits the data. Now plugin A is displaying stale data.

As long as you keep everything in mind and write clean code, you should be fine. But when you give the example of “Profile” with “settings” etc, I personally do not think this is a good choice, for the simple reason that - I may be wrong without more info - it sounds like very lightweight objects. I would use SharedResourcePointer only for heavy data that would just be a waste of memory to allocate in each plugin.

I appreciate the info about the locks, I should probably learn those anyway so that seems like a good direction.

Your SharedResourcePointer should not hold directly a ValueTree , but instead a custom class that you build and that serializes access using locks for example. Of course, this comes with a trade-off of performance so you should be mindful about that.

Yes the profile class contains the value tree. Sorry if that wasn’t clear.

you can lose synchronization pretty easily.

I haven’t had too much issues with this either as alot of the time I hook up listeners to the value tree. So if for example the UI setting “theme” changes, all of the components that are listeners on instance A & B will still be updated. In fact I have found to be part of the benefit of sharedResourcePointer over just a local “Profile” instance.

I would use SharedResourcePointer only for heavy data that would just be a waste of memory to allocate in each plugin.

I do agree that seems like the better and more intended use, but I still haven’t found a more convenient option. Previously I used to make the profile class just a regular object and pass a reference around to every component that needed it which definitely gets a bit extra. At one point I used singletons but I quickly learned how frowned upon they are lol. But then SharedResourcePointer seemed to be the best of both worlds.

I think if you only access this stuff from the message thread, as there is one shared resource and also one message thread per process, you shouldn’t have thread sync issues. Still, having a single instance per process of something that can change the plugin’s state at runtime seems problematic. DAWs may run all plugins in one process, or one per process, or many per process in many processes. Each of these cases would show different behavior.

I also use it for stuff that’s not heavy to store, but is heavy to create, and won’t change afterwards.

1 Like

About SharedResourcePointer…

How exactly is this:

SharedResourcePointer<Data> data;

use_data (data.get());

different than this:

static const Data& getData()
{
    static const Data d;
    return d;
}

use_data (getData());

Are they different?

SharedResourcePointer is actually ref counted, so the object will be destroyed once the last owner releases the pointer. Like a shared_ptr

1 Like

Ah, I see. Thanks!

Could you elaborate a little more on the DAW processes?
If two plugin instances are running in separate processes, do they no longer share Static data (or in this case a SharedResourcePointer) ?

And if you feel like it, do you have an example of the different behavior you mentioned?

Exactly. They don’t. If each plugin run in its own process, although you use a SharedResourcePointer in every one of them, each plugin will actually allocate its own object. So you should not assume in your implementation that it is always a single object. It may or may not be.

It is more and more common for DAWs to run plugins out of process to avoid crashes, and will probably become a norm eventually. Reaper does that, also Bitwig, I think Logic does too now… I don’t write plugins personally so I’m not sure but people here could give you an accurate list I’m sure.

If you really need to guarantee that a single object is used across plugins in any DAW, you should instead use something like a file on disk, with an InterProcessLock.

5 Likes

Something I’ve been looking into recently is Boost’s interprocess shared memory classes.

Might be overkill for your needs though. I’d only go that route if you really need to be aware of other instances or share data between plugin instances whether they’re in the same process of not.

2 Likes