home / software

asok.lua


Status

20080405 - beta quality snapshots of asok.lua are available.

Download

Download ztact-lua.zip
View asok.lua

Overview

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.


Reference


Non socket functions


asok.select (rset, wset, timeout)

resumes all waiting threads on I/O availability, allowing them to return from the asok I/O functions where they yielded. 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.


Socket functions

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)

Accepts and returns a client LuaSocket socket from server socket sok. On error returns nil and an error message.


asok.receive (sok, ...)

A wrapper around LuaSocket's receive functions.

With tcp sockets: asok.receive (sok, [pattern, [prefix]])
With udp sockets: asok.receive (sok, [size])


asok.receivefrom (sok, [size])

A wrapper around LuaSocket's receivefrom functions.


asok.send (sok, ...)

A wrapper around LuaSocket's send functions.

With tcp sockets: asok.send (sok, data, [i, [j]])
With udp sockets: asok.send (sok, datagram)


asok.sendto (sok, datagram, ip, port)

A wrapper around LuaSocket's sendto function for use with udp sockets.


asok.settimeout (sok)

Set 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)

Throttle sok's input rate to bps bytes per second.


asok.throttle_send (sok, bps)

Throttle sok's output rate to bps bytes per second.


asok.wrap (sok, [timeout])

Creates and returns an asok socket wrapper around LuaSocket socket 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.