It seems it is ok to create a Message and post it multiple times. Is it so?

From a realtime thread, sometimes I need to issue notifications towards the message thread.

To avoid allocation of a new Message instance for every notification, is it ok if I allocate beforehand just one instance of the Message I want to send, and post() that same instance on the message queue whenever I need to send the notification?

That, of course, could happen multiple times during the lifetime of my program, and the doc for message says:

Always create a new instance of a Message object on the heap, as it will be deleted automatically after the message has been delivered.

… but the Message classes are reference counted anyway, so I thought to give it a try and it just works.

I have tested with this simple class:

struct DebugMessage : public juce::Message
{
    DebugMessage (const juce::String& t) : text (t)
    {
        DBG ("DebugMessage (\"" + text + "\")");
    }
    
    ~DebugMessage () override
    {
        DBG ("~DebugMessage (\"" + text + "\")");
    }
    
    void messageCallback () override
    {
        DBG ("messageCallback (\"" + text + "\")");
    }
    
    juce::String text;
};

Of which I create two instances as members of a MainComponent in a test application, which I keep track of in reference counted pointers:

juce::Message::Ptr msg1 {new DebugMessage ("msg1")};
juce::Message::Ptr msg2 {new DebugMessage ("msg2")};

At this point, I can post both messages several times:

msg1->post();
msg2->post();
msg1->post();
msg2->post();

And the outcome is exactly what I would have expected:

  • The constructor for each message is called exactly once
  • The callback for each message is called as many times I have posted the message, in the correct order
  • The destructor for each message is also called exactly once, at program exit

Given the fact that it currently already works as expected, my question is: can this be relied upon?

If you have tested this and it works as expected why wouldn’t it always work?
I’m just wondering why you would bother reusing the same messages, are you posting from the audio thread?
I would be more worried wether that is actually a good idea, because you might be accessing non realtime/blocking functionality in the process in posting the message itself. The MessageQueue uses locks for instance.

Because the doc says otherwise:

Always create a new instance of a Message object on the heap, as it will be deleted automatically after the message has been delivered.

and the fact that it is working now is currently due to an implementation detail that is not, so to say, “in the contract”, and may change without notice later, breaking things.

Yes, exactly

That is true, in fact the ideal would be that also the act of posting were guarantee to be realtime safe. For now it is not, but still I feel that avoiding a new allocation for each Message whenever I need to post one, at least mitigates the issue and it’s one step in the right direction

All little bits help indeed. But i can imagine at some point you start adding specific data to a message and you’ll will still need to allocate a new instance of a message anyway.

I’m not an Audio expert, but maybe there are other mechanisms you can use here to inform other threads of events that happen on the Audio thread? Just all depends what you are trying to do here of course. You can maybe set an atomic flag that another thread can pick it up to actually send the message. Because it is a Message your goal is probably not make it sample accurate.

My intent is to deal with these notifications so that all these requirements are satisfied:

  1. notification handling must happen on the message thread
  2. the order of their handling must reflect the order of their generation
  3. there must be no “coalescing” of notifications

Unfortunately, the above requirements rule out the atomic flag approach: suppose I have two types of notifications, for event A and event B, and that they happen in this order on the audio thread:

A → set the flag for “event A happened”
B → set the flag for “event B happened”
B → the flag for “event B happened” is already set, thus nothing changes
A → the flat for “event A happened” is already set, thus nothing changes

If at this point the message thread picks up the notifications, the “second times” both events happened are completely missed out, i.e. the message thread cannot distinguish between the ABBA sequence and a simple AB that may have happened instead.

That’s why I was looking at the Message class because that satisfies all of the above requirements out of the box, without the need for FIFOs or other machinery, if it weren’t for their non-realtime-safety.