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