Page 1 of 1
message passing implementation
Posted: Sun Feb 01, 2004 5:07 am
by Adek336
I just wondered, would this work:
Code: Select all
int ksend(mailbox_t *dest, message_t *msg);
int kreiceive(mailbox_t *src, message_t *msg);
or
Code: Select all
int ksend(thread_t *dest, message_t *msg);
int kreiceive(thread_t *src, message_t *msg);
so each thread has got one mailbox. A message would contain of int cmd and int arg. Or should it support variable length messages? But arg could be a pointer to a data struct, understandable only for the receiver/sender.
What happens if process P1 requests kreceive(P2, &msg), but P2 doesn't send anything, and P3 sends so much messages that P1's mailbox gets full?
But mostly, how can "fat12_driver" know what is the mailbox of "fdc_driver" and should I create threads for each of them?
[glow=red,2,300]Cheers,
Adrian/glow]
Re:message passing implementation
Posted: Sun Feb 01, 2004 8:46 am
by Schol-R-LEA
This is, IIRC, similar to the underying message passing system in Minix (If I am wrong, well, it' s been a few years since I've had a copy of OS:D&I), AmigaExec (again, this is from memory) and QNX. My impression is that this design works very efficiently for between local threads, or in a system with no hardware protection, but runs into shared memory issues when used for IPC between processes (or between machines); passing a pointer across memory spaces won't work. You would have to either,
- use this mechanism only for thread-local IPC, and have a separate IPC mechansm for non-local messages; or,
- have a shared memory space in all processes for IPC messaging; or,
- have the kernel IPC system copy the message from the sending process's memory space to that of the recipient.
The first option the most efficient, but means that you would have at least two separate IPC mechanisms. The second is reasonably effiecent, but involves juggling paging so that every process maps the IPC common area the same way, and introduces the risk of a process scribbling over an important system memory area; furthermore, it would not be usable for remote IPC. The last approach has the most overhead, but is the most flexible, and is reasonably secure.
Another objection is that tying the message passing mechanism directly to threading leads to a loss of flexibility; a system based on mailboxes or message ports, in which more than one thread can read a mbox and a thread can have more than one mbox, is more general, and separates the message abstraction from the threads themselves. One advantage of this is that it solves the problem of how to determine where to send a given message to; rather than having to get the thread ID, there would simply be a mbox assigned ahead of time to receive those messages; the sending thread need not know about the receiver, and vis versa. Furthermore, it means that if a thread as to be halted and restarted, the mbox for the service it provides doesn't get trashed.
As for the issue of a thread deadlocking on messages from a specific sender, well, most implementations of message passing don't queue separately based on sender; those which do, general use a polling rather than waiting message receiver function to avoid the problem you mention (that is to say, a call to kreceive() would immediately return false if there are no messages from the recipient, rather than waiting until a message arrives). Or at least that's the impression I have of it.
C&CW.
Re:message passing implementation
Posted: Sun Feb 01, 2004 9:07 am
by Adek336
Hm, so how about an IPC-fs. To create a "mailbox", which is readonly for the owner and write-only for everybody other:
Code: Select all
mailbox = fs_open("/system/ipc/unique_name_here", "w");
listen_mailbox(&mailbox);
to open it for writing:
Code: Select all
mailbox = fs_open("/system/ipc/unique_name_here", "ro");
the operations on the mailbox:
Code: Select all
ksend(&mailbox, &message, sizeof(message));
or
fs_write(&mailbox, &message, sizeof(message));
kreceive(&mailbox, &message);
or
fs_read(&mailbox, &message);
Then just pray that two processes won't try to listen_mailbox() the same file.
I am not sure if this would be very efficient, though
Re:message passing implementation
Posted: Sun Feb 01, 2004 9:43 am
by Therx
Your first idea (initial post) could work well accross processes if you made the function ksend copy the message to system memory. And then krecieve moves it to the other processes memory.
Re:message passing implementation
Posted: Sun Feb 01, 2004 12:09 pm
by Adek336
I suppose, I will use the first implementation for thread-to-thread communication, sending 8 bytes at once (uint32 cmd and uint32 arg). This is very fast.
For inter-process communication, I will create the ipc-fs. This is slow but fortunately I don't think I'll be using much IPC in user level.
Or perhaps I'll use an ipc-fs which does not depend on mailbox names, but ids? like
vs.
. It would be an implementation of sockets, though.
Cheers,
Adrian.
Re:message passing implementation
Posted: Mon Feb 02, 2004 8:21 am
by distantvoices
hmmm ...
In BlueIllusion, each and every process/thread has its own 'messagebox'
all the kernel does upon send() is: take the message, store it in a dedicated message heap and attach it to the receiver's message list.
upon receive() the first message is picked from the message list and delivered to the process.
One could call this "store and forward message passing". It is kinda asynchronous..
These two system calls (send, receive) are the basic means of requesting services.
You should think for some important things: do you need the message or is it pretty ok to have the process stroll by again to recheck? In first case, you need a means to tell the message routing system: hey, I wanna receive a message from xy and no one else; sort out, how the system has to handle messages in such a case: requested message at the front of the list? Under any circumstances the message router is required to suspend the receiving process until the requested message has arrived in such a case. When the message has arrived finally, the message router shall wake up the receiving process.
that's all about it. Whether you do it via message ports or via
getthreadid, is your decision.
Hehe ... I have a kind of threadid function which makes the system services accessible via their name. If you want to know the id of mm-service, you just say: find_service("mmsrv"); and it returns the Process id of the mm-service (is needed for the message passing.
Re:message passing implementation
Posted: Tue Feb 03, 2004 4:30 pm
by Pype.Clicker
i haven't found the time to read all your stuff yet, but here comes one thing that forced me to reconsider message passing ...
We do not want to keep busy waiting on an empty message box, right ? so the "receive_msg(msg_box)" is likely to be a blocking call when the msg_box is empty ... The problems come when you try to listen to several message boxes at the same time. This will typically be the case when several datasources (mouse events, timeouts, completed asynchronous calls, etc) are awaited by a single thread ...
do you have encountered such design troubles ?
Re:message passing implementation
Posted: Wed Feb 04, 2004 1:11 am
by BI lazy
well, I intend (not have yet) to assign a listener thread to the application, which fetches and processes all the events that come along from the gui service (which receives the input).
to achieve this, the listener thread needs an ability to fetch the message, stroll throu a list of registered handlers (register handler) which need to be of a predefined declaration (definition and the underlying implementation are for the programmer) and eventually trigger the required action (if the event is registered).
As for async IO --- what's that about, isn't this about assigning a thread to this IO and continue work? I'd do it the thread way, and each thread has it's own messagebox at kernel level - where only threads are known.*ggg*
receive() can be a blocking call. In fact, service and driver threads have to block upon receive() on an empty mailbox. But I wanna have another policy too --> for services which just have to pass by, check for a message and continue what ever they do - some calculation, processing tcp/ip packets and so forth...
stay safe...
Re:message passing implementation
Posted: Wed Feb 04, 2004 5:15 am
by Pype.Clicker
hmm .. yes, so each thread is bound to a dedicated mailbox and each mailbox is owned by a single thread ...
In my design, i had in mind to be able to set different queues for differernt classes of events (for instance, to avoid an important event to be dropped or blocked because of a too long list of GUI events) ... As i don't trust all my send()ers equivalently, i don't want that a single misbehaved send()er prevent important messages from being delivered on time ...
Perhaps i'm pushing things too far (as usual)
Re:message passing implementation
Posted: Wed Feb 04, 2004 6:09 am
by BI lazy
No, thats completely ok. Reminds me of my dilemma with the block cache (not the best solution how I am doing it ... what about mmap and so .. didn' tconsider them, damn).
As for the jam of gui events: this consideration has come into my mind too:
what about having the listener thread running at a higher priority than the user app, which has registered the listener:
the gui receives key stroke, Mouse move, Mouse click. the gui does know roughly about controls , but just this: Kind of control, eventual title and an unique id of the control (in relation to the window the control belongs to). the gui sends a message to the listener thread: Hey listener thread, clicked event in: control 1024, button!, xcoord: 123,ycoord 245.
The listener thread wakes up, fetches this message and processes it. Regardless whether the application is busy calculating something or blocks on io. The listener thread calls the events registered for an action and sets off to work again - fetches the next message.
this is, why action events like Clicked!, ButtonDown!, ButtonUp!, Notify!,... are usually of short and preferably non looping kind. Launching a Notification Messagebox with Modal! (application wide) shall not hinder the listener thread from setting off fetching other work ... It usually hinders the User form setting focus to something else in the application than the Message box.
as for the misbehaviour of senders: right you are. I for one have the problem: currently, If I set a "desired sender" to anything else than "any(=0)", nothing runs. BlueIllusion just stands blocked at MM-Init stage. Its surely an error in my message passing code, I know it. These two functions are small, but so crucial, even the smallest thinking error prevents the whole thing from running. And at the moment, since every one can send to each other a message, even the smallest mistake in Protocol leads to: WHAM %!*?@"''#.
And now, I set off to continue development of the clipping engine of my gui. This one starts becoming very interesting. *g*
Re:message passing implementation
Posted: Wed Feb 04, 2004 1:05 pm
by Neo
The whole point of linking the kernel to run at 3GB or some other high memory adress is because we want every other process created to have the kernel in its address space? isn't this so? So then when we create a new process we fill the upper 3GB of that process address space with the kernel. we do this so that we have a common way of accessing the kernel so isn't this the place where we should use the memory for IPC? I mean it would only be natural to use some address space say from 0xD000_0000 to 0xE000_0000 for IPC?
is this right or am i missing something?