[Date Prev][Date Next][Thread Prev][Thread Next]
- Subject: Re: A Batteries API specification (was Re: LuaDEAL - Lua DEad Alive Libraries)
- From: Sean Conner <sean@...>
- Date: Thu, 6 Feb 2020 00:00:18 -0500
It was thus said that the Great Sean Conner once stated:
> I will admit a bias here---I think the API I have is probably the best of
> the lot and with some minor modifications to the error reporting this is the
> one I'll be promoting in this thread.
I've been doing some reading up on Windows IOCP and ... wow ... they are
the near mirror image of POSIX events. The issue is order of operations.
For instance, reading data:
User: Can I read this file descriptor yet?
Kernel: Now you can.
User: Read the data from the file descriptor.
User: Read the data from the file handle
User: Is the read operation done yet?
Kernel: Now it is.
And the same for writing. On POSIX, you ask if you can, while on Windows,
you do it and ask if it's done. There are also some questions I have that I
don't have answers yet because I don't program on Windows. For instance, on
POSIX, a read() operation can return less data than you asked for. Is this
the same for Windows IOCP? If I start a read of 1000 bytes on a network
socket, does it wait for all 1000 to come in? How do Windows IOCP ports and
sockets interact on Windows? I ask because on POSIX, you can actually
receive individual packets on a TCP connection with recvfrom(), which may be
more or less data than you want (I have code that wraps a TCP socket into
something that works like a file object from io.open(), which has to deal
with partial reads and what not).
I also think that Solaris event ports work similarly to Windows IOCP, but
I'll have to read up on those, but at least I have access to Solaris
machines and it's a familiar platform.
Ah, the joys of coming up with a platform independent API. I'm thinking
this is why libuv is more a framework than a library, just due to this
difference between POSIX and Windows.
> Usage: events[,errs,erri] = set:events([timeout])
> Desc: Return list of events
> Input: timeout (number/optional) timeout in seconds, if not given, function will block until an event is received
> Return: events (table) array of events (nil on error), each entry is a table:
> * read (boolean) read event
> * write (boolean) write event
> * priority (boolean) priority data event
> * error (boolean) error event
> * obj (?) value registered with set:insert()
> errs (strint) system error message
> erri (integer) system error value
In describing how I've dealt with select() and epoll() with pocomane, I
realized that the above is a bit wasteful of CPU---the code ends up going
through the event list twice. I just did some reworking of my own event
driver code (org.conman.pollset, in a private branch for now) where I
changed it up to traverse only once over the list. It now looks like:
local events,err = set:events()
if not events then
-- handle error
for event in events do
-- handle event
Here, events is a closure that will return the next event each time it's
called. It wasn't that bad of a change, probably less than 100 lines of
code (I modified the select(), poll(), epoll() and kqueue() implementations,
plus my event framework) and it does work. I'm just wondering what people
think of this approach. I don't want this to throw an error even though it
might be a very rare thing indeed. It might be tempting to do:
for event in set:events() do
-- handle event
but the only way to signal an error is to throw one. I suppose another
approach might be to pass in a function to handle the events:
okay,errs,erri = set:events([timeout],function(event)
--- handle event
That too, could work as a way to not throw an error (but return an error)
and still handle each event, albeit with a callback. And this still doesn't
fully solve the Windows IOCP issue. Hmmm ... still pondering on this ...