I keep hitting one dead end after another with this, and would appreciate a helpful pointer or two. This is for a standalone application project.
I’ve got a class that’s a ValueTree listener, and I’m trying to have its valueTreePropertyChanged callback be able to open a GUI window. However, trying to make that call directly results in an assert, because valueTreePropertyChanged is not being called on the Message thread. Right.
So as one way to solve that issue, I tried having valueTreePropertyChanged instead call ApplicationCommandManager::invokeDirectly, triggering a command I set up to open the GUI window (since opening the window would eventually be something I’d want to make accessible from the menubar anyways). The application command works correctly if I call invokeDirectly from a button press – but again, I’m hitting an assert when calling it from valueTreePropertyChanged:
bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, bool asynchronously)
{
// This call isn't thread-safe for use from a non-UI thread without locking the message
// manager first..
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
...
asynchronously if false, the command will be performed before this method returns. If true, a message will be posted so that the command will be performed later on the message thread, and this method will return immediately.
And yes, I am calling it with the asynchronously parameter set to true… but it seems it can’t even get the command into the async queue…
Oh, so it’s best to fix it that far back up the chain of events?
Interesting… I guess I had the assumption that I should just find a way to deal with valueTreePropertyChanged being called on a background thread. You’re saying I should fix it by ensuring that it gets called on the Message thread? Thanks I’ll give that a try.
I believe lambdas are const by default, so anything passed in by value is const as well. I think you can mark the lambda as mutable and it will work? but I am not sure.
I’ve been thinking more about MessageManager::callAsync; it seems to me that you can’t rely on whatever is passed into the lambda still existing by the time the lambda executes.
That way the message object holds a reference to the underlying tree data even if the original tree object goes away before the message callback executes.
A reference on itself would not be safe.
But the first argument is a ValueTree, that keeps the wrapped SharedObject inside the ValueTree alive (by reference counting), so the property name reference and the var reference are safe as well, as I see it.
Is there a reason why your valueTreeProperyChanged callback isn’t happening on the message thread in the first place? In my experience, this is usually pretty bad news as they’re not thread-safe.
You’re usually better of making sure any interaction with the ValueTree is done on the message thread in the first place…
Yes, there is a reason - in this app, ValueTree updates were being triggered from a callback on a different thread.
Yes that has been the direction we are going in this thread, after Matt’s suggestion in the second post… moving the ValueTree::setProperty calls to the message thread, via a MessageManager::callAsync lambda. Is approach looking solid?
Thanks @daniel, it was my understanding that passing the ValueTree into the lambda would “count” towards keeping the ValueTree itself alive. Glad I had that right.
Yes, the ValueTree itself is a copy (beware, it loses the listeners), but the underlying SharedObject is the same, so all connections into the tree, to parent, children and properties stay intact.