Standard Event Driven Object Library for D
Brian White
bcwhite at pobox.com
Tue Apr 1 04:12:08 PDT 2008
For the last 15 years, I've been working with a custom C++ library for
doing event-driven development. Neither of D's "standard" libraries
appear to support this kind of development and I was wondering if there
is any interested in working on one.
That's not to say that you cannot do event-driven development given the
current libraries, but it follows the form of:
- do a "select" (epoll, whatever)
- if socket "a" has a read event, do Xr
- if socket "a" has a write event, do Xw
- if socket "b" has a read event, do Yr
- etc.
- repeat
What I'm talking about is a library where all the objects handle events
and in turn generate their own. When you create a socket, it
automatically registers itself with an event loop for that thread. When
data comes in, the socket object gets the event with no "user code"
required. The socket does what it needs for tracking the connection and
then sends an event to (for example) the socket-stream. It does what it
needs and calls whatever object is controlling it.
In the end, it becomes easy to add self-contained services. A simple
HTTP server, for example, can be started with a set of files and
absolutely no support from "main". It doesn't have to be passed an open
connection, a listening socket, or even a select-server. In addition,
the HTTP server itself can make choices about how it receives events,
turning them on or off as necessary, or perhaps going so far as spawing
new threads. When a new connection comes in, the accepting of a new
socket bound to a connection-handler object means that the connection
handler is going to get the events associated with that socket.
Now it doesn't seem at first glance that this gains you much. If the
HTTP server is getting notices directly from a select-server, then it
can do a read (or write, as appropriate) to the correct socket and
process the results. That's self-contained, too. But when the events
always flow from the bottom-up, here are some other things you could get:
- Buffering: The socket stream (indeed, the base stream itself) can do
read/write bufferring with no support from the top. In the case of a
write buffer, in can enable/disable write events when needed with no
invervention from the controlling object. And, you only need to write
this code once to have it work with HTTP and all other socket services.
- Specialization: A select server gives you "read" and "write" events.
A socket can generate "read", "write", "connected", "closed", and
"broken" events. Why have each network service have to differentiate
these independently?
- Extensibility: A new feature of the HTTP service is now to accept
SIGHUP to reload a set of files. Without touching one line of "main",
you simply have the HTTPserver object make a request for events from the
global signal handler. Want to time-out connections? Have the socket
request events from a timer module and notify the server if a time-out
occurs.
- Parallelism: It becomes easier (not that multi-threading is ever
easy) to run different objects on different execute threads. Since
there is no longer a global event loop created/instantiated by "main",
the library can make its own decisions (with restrictions, of course).
In my C++ code, I did all this with callbacks instead of posted events
because, well, it's more efficient and makes better sense than a single
event handling function per object with a big switch statement. D's
delegates and interfaces should make this even easier.
Now it's not to say that this method of programming is without it's
difficulties. You can get loops. For example: a socket gets a read
event, passes it all the way up to the HTTPserver object which, for
whatever reason, decides to close the connection. The "close" call goes
down the stack, affects the underlying path, deregisters it from the
select-server, and returns... all the way up to the HTTPserver and then
back down the event callbacks to the select-server where it generated
the read event. The socket was open and valid when it started the
callback but now it's closed. You have to plan for this.
The other big problem is that you can't do this with an existing
library. It has be built-in from the ground up. Doing this in the D
library would require a new socket class, a new stream class, etc., etc.
It doesn't have to replace the existing one but it would be parallel.
What do others think of this?
-- Brian
More information about the Digitalmars-d
mailing list