Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 Oct 2004 17:19:03 -0700
From:      "Ronald F. Guilmette" <rfg@monkeys.com>
To:        Andre Oppermann <andre@freebsd.org>
Cc:        Garrett Wollman <wollman@khavrinen.lcs.mit.edu>
Subject:   Re: aio_connect ? 
Message-ID:  <74506.1098231543@monkeys.com>
In-Reply-To: Your message of Tue, 19 Oct 2004 22:56:18 %2B0200. <41757F72.A36AD263@freebsd.org> 

next in thread | previous in thread | raw e-mail | index | archive | help

In message <41757F72.A36AD263@freebsd.org>, you wrote:

>What other events than aio_connect() you are interested in?

Well, I'm glad that you asked, because after my last message in this thread
I realized that (just for the sake of symmetry, if for no other reason) it
would also be swell to have a kernel function called aio_accept().

That's it for now... just aio_connect() and aio_accept().  If I think of
something else, I'll let you know.

>Feel free to submit updates for the man pages.

I shall, as time permits.  Thanks.

>AFAIK there are certain problems with AIO in FreeBSD

Could you be a little bit more specific please?  I'd like to know about
any such problems.

>I still don't understand what exactly you are trying to achieve (The Big
>PictureTM).

I would just like to be able to write network clients and/or network servers
using the kind of radically different programming style that the aio_*
functions (with their signal-generating asynchronous interrupt capability)
would seem to permit one to use.

Basically, I have a mental model of how asynchronous events (in particular,
various kinds of I/O completion events) really _should_ be handled.  And
the mental model I have has nothing at all to do with any kind of synchronous
polling (which I view as inherently ugly and typically inefficient, both
from the point of view of machine cycles, and also, perhaps more importantly,
from the point of view of code clarity and straightforwardness, or lack
thereof).

Note that select(), poll(), and even kqueue()/kevent() are really all just
different ways of doing _polling_ for a set of events.  The kqueue/kevent
stuff is definitely more efficient and more flexible than either select()
or poll(), but at base, it is still just a different interface to a kernel-
implemented polling mechanism.  And as I have said, I simply do not like
polling. Never have, and never will.

(I could perhaps illustrate the basis for my fear and loathing of polling,
in general, by relating a story from my own ancient past.   Long long ago,
in a valley not far away, I worked as a compiler software engineer for one
of the many goofy little SillyCone Valley companies that, back 20 years ago
or more, made money by selling their own special proprietary-design computers
with CPUs constructed out of entire circuit boards, larger than your head.
I don't even remember the name of the company anymore... I'm sure that they
have long since gone out of business... but anyway, shortly after I started
work there, the lead developer explained one of the fundamental aspects of
the compiler for the one and only higher-level langauge... Enhanced BASIC...
that was supported on the company's proprietary hardware.  In a nutshell,
whoever had designed the company's CPU boards had apparently tried... and
failed... to actually implement hardware interrupts on/for the company's
proprietary CPU.  The dolt just couldn't get hardware interrupts to work.
DUH!  But the company _had_ to ship _something_ so they shipped what they
had, which was systems with CPUs that simply could not field interrupts,
e.g. for I/O device completions.  The result was the most incredible kludge
I have even heard about, either before or since.  Unbelievable as it may
sound today, the burden imposed by the lack of functioning hardware inter-
rupts was foisted off onto the software.  The BASIC compiler was implemented
in such a way that in generated a call to an I/O completion polling routine
just prior to each and every backwards jump in the code, e.g. at the ends of
loops, etc.  Thus, as long as the customer(s) never programmed in anything
other than the company-approved BASIC langauage... using the company's own
proprietary BASIC compiler... all was well, and the system(s) mostly functioned
acceptably well, albeit as a rather remarkably low level of performance,
relative to what they could have done, if they had only had working hardware
interrupts, rather than this idiotic compiler-dependent polling kludge.
And of course, the machines would often and frequently lock up, for no
obvious or apparent reason, and would thence have to be power-cycled in
order to get them back online... a fact which I personally assumed was due
to the fact that large amounts of support code... libraries, kernel, etc...
had been implemented by the company itself in _assembly_, rather than in
BASIC, implying that these hunks of code were probably littered with lots
of backwards jumps, i.e. loops, in which no manual polling for I/O com-
pletions occured.  Anyway, I count my brief stint at this particular com-
pany as one of high points of my early practical education in the field of
software.  It certainly taught me the clear advantages of being interrupted
when something happens, as opposed to always having to check, over and
over again, to see if something has happened.)

Getting back to the topic at hand, as I have said, I think that the aio_*
functions, together with their signal-generating capabilities, could
support a rather dramatically different style of programming (e.g. for
clients and servers) when compared to the polling primitives select(),
poll(), and kevent().  Instead of starting up a whole bunch of (otherwise
unrelated) I/O operations and then having to come back to a central
``event loop'' to constantly check to see which ones of them have completed,
aio_*() operations (with signals for completions) could allow more of a
``set it and forget it'' sytle of programming where we just start each
operation and then go off to do other things, knowing that we will get
asynchronously forced into some signal handling function when/if there
is an I/O completion.  That signal handler could then perhaps start up
the next operation that needed to be done, e.g. for that specific socket
or file handle, and then just return.  Meanwhile, the ``mainline'' code
that we were executing before the signal arrived would be picked up right
where it left off, and that code would be none the wiser than anything
at all had ever even happened.

I personally like this more ``distributed'' (code-wise) model of handling
I/O completions a lot more than the traditional select()/poll()/kevent()
centralized ``event loop'' model wherein, for the purposes of polling, we
have to effectively stick a whole bunch of otherwise totally unrelated
and independent things (e.g. file handles) together into a single combined
vector argument just for the sake of select()/poll()/kevent().  Also, I
guess you could say that my personal tastes are influenced by my early
career experiences, and one in particular that I have described above.
Last but not least, it seems to me that quite a lot of the evolution of
various *NIX kernels and standards over the past many years have been
oriented towards providing more ``kernel like'' functionality in the
userland level.  Certainly, the aio_*() with their signaling capability
are a fine example of this.  Using aio_*() with completion signaling
gives almost the same look and feel to userland programs as the kernel
has when _it_ fields asynchronous I/O interrupts.  And I like that.  It
just ``feels'' like a more natural programming style to me.
 
>Ah, you want to be interrupt driven instead of being poll-event driven as
>with poll/select and kqueue?

Yes.  Please.

>What about extending kqueue with a async signaling mechanism?

What about it?

I'm sorry, but to be frank I have to say that that just sounds like a kludge
to me.

As I have said, select()/poll()/kevent() are all just different ways of doing
polling.  Any one of them could be hacked to make it generate signals instead,
but why bother when there is already a standardized and well-defined (and
mostly implemented) non-polling approach to detecting I/O completions, i.e.
aio*() with signals.

(The expression that comes immediately to mind is ``Putting lipstick on a
pig.''  Certainly, select() or poll() or kevent() could be enhanced with
``interrupt'' capabilities, but they were all designed and intended to
provide non-interrupt ``polling'' functionality.  So why change them?
I mean it's kinda like asking a qualified electrician to fix your broken
plumbing.  Yea, _maybe_ he could do it, but why not just hire a plumber
instead?)

>Would that make your stuff work?  I think doing this
>would be easier and more powerful than fixing all the aio stuff.

I ask again, What's wrong with the aio*() stuff?  Are you saying that you
stylistically don't like the programming model they give you, or are you
referring again to broken bits in the FreeBSD implementation?

>The kqueue
>will queue all events and then signal them via SIGwhatever so you can do
>one poll on the kqueue and dequeue all pending events.

It will do that??  I didn't know that.

Anyway, I would still like to be able to attach _different_ signal handler
functions to each different event completion that I am waiting for.  It
seems to me that the aio_*() functions would support this, whereas as I
have noted above, kevent() makes to mash everything that you are waiting
for together, even if they are otherwise unrelated.  (It would be kinda
nice, I think, to be able, for example, to put all of the code for handling
one kind of socket connection into one single source file, and then put
100% of the code for dealing with some different kinds of socket into its
onw provate source file also.  It seems to me that if I had a complete
(and fully working) familiy of aio_*() functions, then I could do this
exact sort of thing, whereas with select()/poll()/kevent(), you also have
to push or ``leak'' some knowledge of _all_ sockts/events throughout the
entire program back into your centralized non-distributed single central
event loop.

>I've added Jean-Mark Gurney to the CC list.  He has done some more involved
>work on kqueue recently.  He probably has the best overview if/how to make
>this work with kqueue.

That's fine, but see above with regards to my opinion of the wisdom of
even attempting that.

Lest anybody misundertstand me. allow me to say that kqueue()/kevent()
stuff is _very_ impressive, and when I first learned about it I was in
rapture over it for some time.  This is great stuff, and certainly is
a dramatic step forward with regards to _efficient_ kernel support for
handling large numbers of events/sockets.  But efficiency for my machines
is not equivalent to efficiency for me, and the two are sometimes in
conflict.  As a software engineer, I may often prefer a programming style
that will allow my code to be more modular, even if that makes it rather
less efficient.

>You can submit as many requests for enhancements as you whish as long as
>you attach the patch to implement it.

:-)

Yes, the GNU ethic... you can have whatever you want, as long as YOU
implement it. :-)

I am well and truly familiar with that.

It kinda reminds of what Colin Powell said to President Bush about Iraq...
``You break it, you own it.''

>Otherwise they will sit in GNATS
>for a long time.  AIO has been very much neglected and there is no active
>maintainer for it.

Sadness.
:-(



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?74506.1098231543>