[haiku-3rdparty-dev] Re: Modal dialogs

  • From: Stephan Aßmus <superstippi@xxxxxx>
  • To: haiku-3rdparty-dev@xxxxxxxxxxxxx
  • Date: Wed, 12 Jun 2013 10:29:38 +0200

On 12.06.2013 09:19, Matthew Allen wrote:
From: Stephan Aßmus (superstippi@xxxxxx)
I don't understand why you need to lock the window that is already
blocked. You should probably not Unlock() it. Apparantly, your main
window is locked because it is processing an event, which causes it to
display a modal dialog, which makes it block until that dialog is done,
all the while it is still locked and processing the event in terms of
its call stack. So where and why do you need to lock that window from
the thread of the modal dialog?
[...]

Generally my apps work on the basis of being able to post messages at any given 
time. So having the main app window locked for any length of time is going to 
be bad in the long term unless I can post messages without locking the target.

Well... on other platforms, blocking on a modal dialog is sometimes (usually?) implemented by running a local event loop. So you can indeed send events and still have them processed. However, this can lead to unexpected behavior as I found out the hard way, since when your execution path is already somewhere in the middle of processing an event, you cannot just process "any" event like you can when you run the main event loop.

My message post function currently needs to lock the target's looper to call 
BMessenger::SendMessage. Unless there is some better way to post a message 
asynchronously?

But of course!! *Not* having to lock the target looper is the whole point of sending messages in the first place! However, your message sending code is bloated.

         status_t result = Wnd->LockLooperWithTimeout(1000000);
         if (result == B_OK)
         {
                 BMessage *Msg = new BMessage(Event);
                 if (Msg)
                 {
                         Msg->AddInt32("a", a);
                         Msg->AddInt32("b", b);
                         BMessenger m(Wnd);
                         Status = m.SendMessage(Msg) == B_OK;
                         DeleteObj(Msg);
                 }
                 Wnd->UnlockLooper();
                 break;
         }

BMessage Msg(Event);
Msg.AddInt32("a", a);
Msg.AddInt32("b", b);
BMessenger m(Wnd);
Status = m.SendMessage(&Msg) == B_OK;
break;

Note that the BMessenger constructor handles the passing of invalid BLooper pointers (it checks a global list of valid BLooper pointers, just like the LockLooper() call you used, which can also be used on invalid pointers)! The SendMessage() call will than fail with an appropriate error code. (Or you could check m.IsValid(), if you want.)

It uses LockLooperWithTimeout so that it can start printing out warnings if it 
can't lock the looper. More of a debugging thing.

If you want to debug, debug everything, your code doesn't handle all the ways it can fail.

And with proper error handling:

BMessage Msg(Event);
Status = Msg.AddInt32("a", a) == B_OK;
if (Status)
    Status = Msg.AddInt32("b", b) == B_OK;

if (Status) {
    BMessenger m(Wnd);
    Status = m.SendMessage(&Msg) == B_OK;
}
break;

But you may want to replace the simple Status flag with actual logging where the error happens, if you want to find every problem. However, those would be random allocation failures where it doesn't really make sense to log, except to log the fact that sending the message failed...

Best regards,
-Stephan



Other related posts: