20080405 - beta quality snapshots of asok.lua are available.
Download
Download ztact-lua.zip
View asok.lua
The asok.lua module is an all-purpose asynchronous socket I/O library for use with Lua-threads (i.e. coroutines). asok.lua requires LuaSocket and ztact.lua.
Features
| Feature | asok | copas |
| TCP support | yes | yes |
| UDP support | yes | no |
| Socket wrappers | complete | minimal |
| Asynchronous timeouts | yes | no |
| Per-thread sleep | yes | no |
| I/O throttling | yes | no |
| Atomic I/O | yes | no |
| Library size | 7.5K | 9.9K |
General notes
Wherever possible (which is almost always), asok functions mimic their
corresponding LuaSocket equivalents with 100% accuracy with the
following very useful exception: threads calling asok functions will
wait for I/O by calling coroutine.yield() instead of
blocking. This allows other threads to run "in the background" while
the yielding thread sleeps or waits for I/O availability.
Consequently, multi-threaded asynchronous programming with asok should be just like single-threaded blocking programming with LuaSocket, provided you call the asok functions in place of their LuaSocket equivalents.
You may, as a convenience, create asok wrappers around LuaSocket
sockets via the asok.wrap() function. An asok wrapper
should behave just like underlying LuaSocket socket in every way,
again with the useful exception that the asok wrapper will call
coroutine.yield() instead of blocking while sleeping or
waiting for I/O availability.
To start a thread, simply call coroutine.create()
followed by coroutine.resume(), as normal. The thread
will yield as soon as it sleeps or waits for I/O availability inside
an asok function. Subsequent calls to asok.select() will
resume waiting threads on I/O availability (or, in the case of
sleeping threads, when they are fully rested).
Known bugs
If multiple co-routines are reading (and/or writing?) from the same
socket at the same instant the socket is closed, those co-routines
will never be resumed. This bug is due to LuaSocket's design choice
to ignore closed sockets in socket.select(). The problem
is two-fold: a) how to determine that a socket has been closed
(probably by polling getsockname()?) and b) how often to
check all waiting sockets to see if they have been closed. Another
approach would be an asok.close() function for closing
sockets. This would avoid polling, but would only work if people
called asok.close() (or used wrappers) and would also add
another function to the asok API.
asok.select (rset, wset, timeout)asok.select() is typically called in the main thread.
Important note: rset and wset are currently
not used, so please call asok.select (nil, nil, timeout).
In the future asok.select() may be extended to allow the
main thread to also select across sockets.
At present, asok.select() always returns nil,
'timeout' after timeout seconds have elapsed.
asok.sleep (seconds)asok.sleep() will cause the calling thread to sleep (i.e.
yield) for seconds seconds. This will allow other
threads to resume.
asok.xpcall (f, err, ...)asok.xpcall() is an asok compatible implementation of the
standard Lua function xpcall(), with the added benefit
that you can pass in arguments a la the standard Lua function
pcall(). err can be nil, in
which case the default error handler will be used.
Calling coroutine.yield() from inside a call to
pcall() or xpcall() (or, for that matter,
from inside any C function) results in an error due to the fundamental
(and reasonable) nature of the ANSI C setjmp() and
longjmp().
Since asok regularly yields (instead of blocking) while waiting for
I/O inside of coroutines, using pcall() or
xpcall() would result in errors.
In the following socket functions, sok may be a LuaSocket
socket or an asok socket wrapper obtained via
asok.wrap(). The functions behave identically in either
case.
If sok is an asok socket wrapper, then the functions may
be called as sok:func (...) instead of asok.func
(sok, ...).
asok.accept (sok)sok. On error returns nil and an error
message.
asok.receive (sok, ...)receive functions.
With tcp sockets: asok.receive (sok, [pattern, [prefix]])
With udp sockets: asok.receive (sok, [size])
asok.receivefrom (sok, [size])receivefrom functions.
asok.send (sok, ...)send functions.
With tcp sockets: asok.send (sok, data, [i, [j]])
With udp sockets: asok.send (sok, datagram)
asok.sendto (sok, datagram, ip, port)sendto function for use with
udp sockets.
asok.settimeout (sok)sok's asynchronous timeout. The asynchronous timeout
is distinct from the LuaSocket timeout. The asynchronous timeout is
the maximum number of seconds that a thread will yield while waiting
for I/O on sok before resuming and returning nil,
'timeout'.
asok.throttle_receive (sok, bps)sok's input rate to bps bytes per
second.
asok.throttle_send (sok, bps)sok's output rate to bps bytes per
second.
asok.wrap (sok, [timeout])sok.
asok.wrap() also sets sok's LuaSocket
timeout to timeout (or the default of 0). You probably
always want to use the default timeout of 0, otherwise I/O activity on
sok will block instead of yielding, thereby preventing
other threads from resuming for the duration of timeout.