So, in case somebody actually happens to care (and I like using the forum for 'documentation'), I'm going to explain what I currently have, and how I'm going to change it. This is a big step for me, because once I'm happy with the messaging, I can move my attention (finally) to userspace things (like how am I going to run device drivers in userspace).
Currently, each thread has a messagelist for incoming messages. The service supports following calls:
- SendMessage(object, operation, w1, w2);
Send a message to object (stored in object owner's messagelist). Operation is 'nifgen' generated method hashcode, with two bits of type information, which allow "dynamic" typing of the <w1,w2> pair (immediates, long message, handle transfer, and a reserved transfer type).
SendMessage never blocks, but can fail if the messagelist is 'full', or some other nasty condition occurs (say, object invalidated).
- GetMessage(&message);
Copy (and remove) the first message from list. Wait for a message if none is available.
- PeekMessage(&message);
Same as GetMessage, but fails without blocking if none is available.
- WaitMessage(object, op, &message);
Same as GetMessage, but skips (and leaves into the list) messages not matching object and/or operation (there are wild cards for both, so GetMessage is really just WaitMessage(ANY, ANY, &message);
Then there's DispatchMessage(message), which handles message dispatching to object-specific handler function painlessly (look I got OO-messaging in kernel), if the receiver feels like using such a service.
Service thread thus looks something like this:
Code: Select all
while(!GetMessage(&message)) DispatchMessage(&message);
PANIC("GetMessage() failed, and I'm too stupid to deal with it.");
WaitMessage(...) currently used in one place, scheduler_sleep(), which specifically wants to wait for MSG_kernel_timer to wake it up.
Ofcourse there is just one timer per thread, and this is also a bit questionable, because the timer has to bypass the official interface, because scheduler_sleep() doesn't bother implementing a full object, so the whole thing is a hack. Finally, this means that since SendMessage calls malloc() to allocate a message object, sending a message from the timer interrupt handler is strictly speaking 'dubious', especially with all the trouble to have a (nearly) O(1) timer system.
----
So, here comes today's listen to your brain lesson:
If I remove WaitMessage(..), and implement the timer wakeups separately (probably needs about 10 lines of code at most), I can instead have a fixed sized circular buffer per thread, allocated at thread creation time, allowing me to calculate fixed per thread costs, which is nice.
But it also means I never have to allocate anything just to send a (small) message. It might fail, but it might fail in any case. It also means I don't need to trash my heap with tons of small message objects, and it makes it easier to kernel to track of resource usage (for big, explicitly allocated things like threads, it's easy to set limits based on memory size or whatever). Not to mention that SendMessage() becomes truly constant-time which is nicer.
I can think of one other use for WaitMessage in kernel though, and that's paging wait. But if that needs another 10 lines of special support, then I need about 20 lines more special purpose code total. As for user processes, that can be reimplemented easily in userspace.
Conclusion: Sometimes special purpose code seems to be worth it, if it simplifies the general case.