Handover of messages in Handler chain not working

I am having a hard time with a central piece of Haiku’s magic and I want to get this right.
It seems the message distribution among BHandler’s is not working as documented, and I could not find out why so far (will inspect the source next).

I have a central BApplication process and want to handover domain specific messages to application “services” implemented as BHandler processes. This seems like a nice and natural architecture for a Haiku application, and should work according to the docs, like this:

  • in your Application, add the Handlers via AddHandler (I can see this works by checking CountHandlers)
  • in the Application’s MessageReceived, pass on any message you don’t (want to) handle centrally to the superclass via BApplication::MessageReceived (or should it be BHandler or BLooper? tried all to no avail)
  • the base class should then call the next handler in the chain, until one either accepts the message, or, after the last one is called, drop it eventually.

In my case, the message is silently dropped before reaching any of the attached Handlers.
I can only see it’s accepted in the main Application thread, but then it’s lost.

What frightens me is that even the test included in Haiku sources (HandlerLooperMessageTest.cpp) does not work as advertised and shows the same symptoms (I’ve added logging to see which is called, Handler or Looper, and then extended the test to send 2 different messages, where one should reach the Looper, and the other should reach a Handler).

Is this really a bug in one of the centerpieces of Haiku? Seems unlikely, since message passing and forwarding seems to work for Windows and their attached controls. But then the test should also work…

I’ve had to resort to many SetTarget() calls in AttachedToWindow() to get the Handler chain working properly, never dug deeper since there were other bugs to deal with, and yes, it suprised me too that it was needed. The thing with BWindow messages are that some messages are sniffed and redirected to hook functions before reaching Default Handler. Are there intermediary handlers in your app which if they dont process a specific message, do they call the default handler - that ensures messages are passed down the chain? Each message can override the target BHandler, are you sure that is not disrupting things. A set of tests may help see whats happening, and you also have the source to Haiku to see exactly whats going on. At the end of the day, it might be done on purpose as a BeOS compatibility thing, even if it no longer complies to the original API docs.

The chain should work, but I think it should work in the opposite direction from what you have in mind. If you run this and send message “0” to it, it will post a message to its extra handler, and that message will travel up the chain.

#include <stdio.h>
#include <app/Application.h>
#include <app/Handler.h>
#include <app/Message.h>

class T: public BHandler {
public:
	T(): BHandler("T") {}
	void MessageReceived(BMessage *msg) {
		printf("T: 0x%x\n", msg->what);
		BHandler::MessageReceived(msg);
	}
};

class H: public BApplication {
public:
	T *t;
	H(): BApplication("application/x-test") {
		t = new T();
	}
	void ReadyToRun() {
		AddHandler(t);
	}
	void MessageReceived(BMessage *msg) {
		printf("H: 0x%x\n", msg->what);
		if (msg->what == 0x30) {
			PostMessage(0x77, t);
		} else {
			BApplication::MessageReceived(msg);
		}
	}
};

int
main(int argc, char **argv)
{
	H h;
	h.Run();
	return 0;
}
1 Like

Hi,

First of all, the notion of a “chain” of handlers is a bit misleading. It’s more complicated than that.

There is no automatic building of a chain of handlers. What happens is, a message is first delivered to its designated target (the one you used when calling SendMessage/PostMessage or set using SetTarget, the exact process is detailed in https://www.haiku-os.org/legacy-docs/bebook/TheApplicationKit_Messaging.html in “Finding a handler”).

The handler chain is only used if you chained some handlers together using https://www.haiku-os.org/legacy-docs/bebook/BHandler.html#BHandler_SetNextHandler

Then, there is indeed something to forward the message to. But it isn’t really a chain, either, since multiple handlers can have the same “next” one. For example, in a window with views, each view will set the “next” handler to its parent view, and the message delivery path will look more like a reverse tree, with starting point in each leaf view, and eventually converging to the window itself. You can see the SetNextHandler call done in https://git.haiku-os.org/haiku/tree/src/kits/interface/View.cpp#n5848 when the view is attached to a view hierarchy in a window.

So, there is no magic. If you want to make use of this, you need to explicitly set up the order in which BHandlers will be delivered a message.

The other part of this is, how does the propagation stop? How do we know a handler has processed a message? The way this is done is that in your MessageReceived() implementation, if you handle the message, you just stop there (return from the function without doing anything). If you want the message to continue propagating, you instead call your superclass’ MessageReceived. Eventually, the superclass will call its own superclass MessageReceived, and so on, until BHandler::MessageReceived is called. It’s the one responsible for checking if there is a next handler, and forwarding the message to it.

1 Like

Thanks @PulkoMandy for your always on spot detailed explanations:=)
You made my day, I’ll try this out after a walk (finally the weather is inviting to go out again).

I think it would make sense to clarify this in the docs, it’s not really clear from that.
Even from the legacy docs you referenced, I’d suppose this is not needed if I’m happy with the default order, as it says it’s just for reordering handlers in an existing chain…

SetNextHandler() reorders the objects in the handler chain so that handler follows this BHandler . This BHandler and handler must already be part of the same chain[…] By default handlers are placed in the order that they’re added (through BLooper::AddHandler() ).

I was actually reading through all docs I could gather, including this one, and from the description I thought the chain was implicitly set up by adding multiple handlers to the same Looper (precisely, Application in my case).

So there is no chain created at all unless I set it up explicitly through SetNextHandler ?
This seems weird since it makes no sense to add Handlers via AddHandler then as they will never be called, or is this really only intended to set up potential targets to be explicitly specified on PostMessage() ?

I guess that would be something to tackle in Haiku v1.x / v2, to follow a “sensible defaults” pattern…

Well, let’s check the sourcecode to make sure :slight_smile:

The initial chaining is done here:
https://git.haiku-os.org/haiku/tree/src/kits/app/Looper.cpp#n409

As you can see it’s very simple: each handler is set so its “next” is the looper it is attached to. So again, this does not really build a single chain, but many very short ones all pointing to the looper.

Then, for this chain to be useful, you have to start from some handler and then follow it up. If you post a message directly to the looper, it gets directly to the looper and that’s it. The looper does not have a “next” handler by default. But, you can change that initial delivery by using SetPreferredHandler (that you could point to the nexthandler list head), or by targetting a given message at a specific handler.

So, yes, the docs in the Be Book talk about a linked list of BHandlers, but in Haiku this is not the case, it is more a reverse tree. Unless you carefully use SetNextHandler to put all your handlers neatly in a list.

Another note: in an application I’m working on, I was not too satisfied with this way of doing things, so I instead used the StartWatching/SendNotices calls, which allow for a handler to register for specific message constants and be notified when the corresponding SendNotices is called. In my case it fitted best with what I was doing.

1 Like

It makes sense (to me anyway) as the basis for view/window message handling, with BView a subclass of BHandler. A Button receives various messages as events transpire in its graphic extent. One may be relevant to itself; another not, so that one is handed back to its “parent”, as established by AddChild() which is the GUI version of AddHandler(), and so forth as this parent/child relationship follows screen geography also - having identified the visible child view, you have the whole set of possibly valid recipients of the event, just going up the parent hierarchy.

Pardon me if this is obvious, as it probably is, but my point is that it works great when you have an external message source (like the server responsible for graphic input) that has a way to address messages to a relevant child handler, and where that relevance in some respect follows the parent/child relationship.

Thanks @PulkoMandy for the insights, I was also thinking of using the MessageFilter but thought the chain was easier, will try that, too.
Using a MessageFilter (if this what you mean?) follows more the Observer pattern, which would be also a good match in my case.
I have my code running nicely with a manual built Handler chain, though.

@Donn good point with the View analogy, I supposed it was modeled after that, but in UI-less applications you don’t have this parent/child relationship but more a separation of concerns, with several “services” (I’m mostly in Java/SpringBoot land these days;) or however you want to call components of your app that handle a single purpose/subdomain.
I was hoping the Handlers would be chained automatically when Adding() them, to fit a more generic use, but alas I was wrong.

I don’t mean the message filter, I mean what I wrote: thereare StartWatching() and SendNotices() methods in BHandler. You can register an observer using StartWatching (or StartWatchingAll, if you want to get all notifications), and then send a message to registered observers using SendNotices(). This results in messages with “what” set to B_OBSERVER_NOTICE_CHANGE.

ah I see, thought you were talking about Filters - didn’t know BHandler supports the Observer pattern natively, very cool - though probably not quite what I need, seems better suited for inotify like update notifications, not for selective Message processing. For that, Filters and (manual) Handler chains in a peer-to-peer like fashion seem more apt.