Async message posting

I’m currently seeing a deadlock in my code. What is happening is that when the BWindow based class is shutting down in it’s destructor it’s waiting for a worker thread to finish. And the worker thread is trying to post a message to the owning BWindow class. Which is never going to unlock ever again, cause it’s shutting down. However the worker is waiting to lock the BLooper. I could just use the lock looper with timeout and then use an out of band way to tell the worker to quit trying to send messages. You’d get a stall for at least the “timeout” used, which is less than desirable. But ideally they’d be a more asynchronous way to post messages.

On windows I can just PostMessage to the window handle, which as an opaque type doesn’t need any form of locking. That’d be perfect. Is there something similar on Haiku?

Can you wait for the thread to finish from the BWindow::QuitRequested() method instead of the destructor? I’m not sure if that would help or not. I’m too tired to be responding to technical questions :stuck_out_tongue:

BLooper (superclass of BWindow) has set of PostMessage functions which do not require the looper to be locked in order to invoke them, and (I believe) work internally in such a way as to avoid deadlocks.

Is there any documentation to that effect? Reading the BLooper docs doesn’t really confirm which functions you’re talking about.

Otherwise I’d assume that status_t PostMessage (BMessage *message) would be one of them?

Yes, no lock is required to use that method. Similarly, no locks are required to use BMessenger objects to send messages to loopers (and I believe PostMessage is implemented using a BMessenger.)

Indeed, that page looks like it does not properly document which functions require the lock to be held and which do not. That looks like an oversight we should correct.

It appears the Be Book documentation for BLooper does clarify which functions require the lock to be held, but it also has a bunch of notes about restrictions to PostMessage which definitely do not apply on Haiku.

3 Likes

Asynchronous message sending was implemented on BeOS Dano Exp, but not R5.

For the process you just described, there are two ways to handle it.

  1. No waiting for the worker thread in BWindow’s deconstruction, then use BMessenger::IsValid () and BMessenger::LockTargetWithTimeout() to acquire the lock; once all this functions failed by timeout, it means the BWindow have been destroyed or deconstructing, the worker thread should not try to access the BWindow again.
  2. No locking BWindow in the worker thread, use BMessenger::SendMessage() (specify send_timeout if you want) instead of using BWindow::PostMessage(); but I must point out that BMessenger:: SendMessage() makes synchronous sending.

Which ones do you think don’t apply?

The main restriction for PostMessage is that the object may be deleted at any time if you don’t have a lock on it. So, using a pointer to a BWindow is basically unsafe because of that.

BMessenger uses a trick to avoid the problem: it checks for some "magic " value in the pointed object to make sure it is still there. BLooper clears that magic in its destructor. There is a risk if a new BLooper object is allocated at the same address, the message may end up delivered there.

BMessenger on old BeOS used team_id and port to identify Looper, that’s the reason it’s more stable…