Date: Thu, 6 Apr 2000 20:40:19 -0700 (PDT) From: Matthew Dillon <dillon@apollo.backplane.com> To: Jonathan Lemon <jlemon@flugsvamp.com> Cc: Archie Cobbs <archie@whistle.com>, Jonathan Lemon <jlemon@flugsvamp.com>, freebsd-arch@freebsd.org Subject: Re: RFC: kqueue API and rough code Message-ID: <200004070340.UAA93335@apollo.backplane.com> References: <200004070107.SAA97591@bubba.whistle.com> <200004070220.TAA92896@apollo.backplane.com> <20000406220454.J80578@prism.flugsvamp.com>
next in thread | previous in thread | raw e-mail | index | archive | help
:> short flags;
:> long data;
:> union {
:> int idata;
:> void *pdata;
:> } udata;
:> void *reserved2[2];
:> };
:
:Um. One problem with this: the structure just went from 12 bytes
:(on a x86) to 32 bytes. I'm trying to keep this as small as possible,
:since I really want to minimize both the overhead of copying data
:back and forth, and the amount of space used in the kernel. I can
:see adding one more field (to 16 bytes), but doubling the size seems
:to be just too much.
While this is relevant, not having important features just to keep
the structure small is only going to hurt the system call's functionality
and scaleability.
But if you are really that worried, then the most important field you
can add is 'udata'.
:This essentially moves the state into the kernel. My feeling is that
:it's not the kernel's job to track the data; if the user wants to
:associate more state with the identifier, then it can maintain that
:in user space. The (ident/filter) should be enough to identify the
:event.
Udata is *extremely* important. In order to associate user data with
an even in user space the user program must associate the data with the
file descriptor. Since you can have multiple events associated with
a single descriptor (e.g. read and write), this complicates matters even
more. Worse, by NOT having udata or dispatch capabilities you
essentially require that all event handling go through a single user-level
mechanism, which is every similar to what must be done with select() now.
With udata, especially when combined with a function dispatch of some
sort (either event-to-thread or a function-dispatch field which is to
be called on an event), various libraries in the program can all use the
kernel queue system calls in their own way without interfering with each
other.
In fact, I would go as far as to say that a function dispatch field should
be included in the event data structure as well. It's even more important
then thread dispatch fields.
It is VERY important that you be able to do this if you want to allow
third party libraries to use the kernel event queueing mechanism
without intefering with your own use of the mechanism.
:> thread: (future) Set thread to return event on, 0 to return the event
:> on any thread (or the main thread), -1 to create a new thread
:> on the fly to handle the event.
:
:The event is returned in the context of the calling thread; that is,
:the thread itself is what does the dequeing, so I'm not sure how this
:would be useful. Could you explain how this would work?
:...
:Jonathan
Consider a program that is linked against three or four third party
libraries... for example, consider a program linked against the X11
libraries.
Now lets say that the X11 libraries are multi-threaded and want to use
the kernel queue mechanism to handle events asynchronously in the
user space of the program using the library.
It is not possible for the X11 libraries to do this if they happen to use
a different dispatch mechanism then your main loop uses. In fact, if you
look at how X (and other non-embedded subsystems) are organized, it is
precisely this problem that leads to an inability to scale their
interfaces. i.e. you can't use your neat cool event mechanism if you
have to call another library's code that serves as the main loop for your
program rather then you being able to serve as the main loop for your
program.
How much thread programming or embedded work have you ever done? This
sort of stuff is bread and butter to those of us that do that.
Here is an example of what a kernel-managed dispatch system with
dispatch and udata could do:
/*
* Module implements an asynchronous write with timeout.
*/
somemodule_messing_with_some_tcp_connection(struct manage *m)
{
setdispatch(m->fd, m, module_write, priority, EVENT_WRITE);
setdispatch(m->fd, m, module_read, priority, EVENT_READ);
/* returns here */
}
module_write(int fd, struct manage *m, int r)
{
issues write and either clears the dispatch function
or allows it to remain, depending.
}
module_read(int fd, struct manage *m, int r)
{
issues read, deals with read data.
}
Notice a couple of things? Like for example the above code is
completely independant of any other module or library. It does not
require a central loop to read events nor does it have to use the
same support library that some other library might use to implement
event handling. But only if the event dispatch and user data
('m' in this case, which the kernel does not interpret but simply
passes to the event handler) are directly supported by the kernel.
-Matt
Matthew Dillon
<dillon@backplane.com>
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200004070340.UAA93335>
