lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


this is a small extension for Lua 5.1 to get true C coroutine
semantics. This means you can yield from a coroutine across
a C call boundary and resume back to it.

In particular, you can now:
- Yield across all metamethods (not advised for __gc).
- Yield across iterator functions (for x in func).
- Yield across callbacks (table.foreach(), dofile(), ...).
- Yield across protected callbacks (pcall(), xpcall(), ...).
- Yield from C functions and resume back to them (by using
  lua_yield() like lua_call() -- the C stack is preserved).

Best of all, you don't need to change your Lua or C sources
and still get all the benefits. It's fully integrated into the
Lua core, but tries to minimize the required changes. The
first pre-release targets Lua 5.1-work6 only. Upgrading to
the next release shouldn't be overly difficult.

Coco is available for download at:

Note that Coco needs some machine-specific features which are
inherently non-portable -- but the coverage is pretty good.
I've tested it on ~20 CPU/OS combinations (x86, x64, PPC,
IA64, Alpha, HPPA on Linux, *BSD, Windows etc.).

It relies on three different machine-specific mechanisms for
allocating a C stack and switching contexts: modifying the setjmp
buffer, POSIX ucontext and Windows Fibers (automatically selected
at compile time).

Revisiting the old arguments against coroutines with a C stack,
I've made it easy to select the stack size on a per-coroutine
basis. You can freely mix coroutines with C stacks (of different
sizes) and coroutines without C stacks.

Really, the C stack allocation 'problem' may be a non-issue:

First, Lua needs very little stack space. You can get away with
1-2K (the parser may need a bit more -- but as Roberto pointed
out to me: why put this in a coroutine?). The required C stack
size mostly depends on the C functions you intend to call.

And second, I've successfully created 1.000.000 coroutines
(_with_ C stacks) on a 64 bit box ... was pretty fast, too.
Well, how many do you really need?

If you've been following the mailing list for some time, you may
be wondering whether I'm going full circle here? Yes, I did.

The difference to my first approach to the coroutine problem from
October 2004 is that Coco features a clean separation from the
Lua core and has better portability.

The difference to RVM is that Coco is an order of magnitude easier
to maintain because the changes are much smaller.

And, more importantly for everyone, Coco makes it a lot easier to
yield from a C function (even from deep inside your C call stack).

Umm, I didn't mention performance, yet: this should be almost
identical between Coco, RVM and the standard Lua core. Anything
other than synthetic benchmarks is not going to show a difference.

Context switch times are in the sub-microsecond range (!) on a
modern CPU, so there is little reason to worry about.

Thread creation with Coco is a bit slower due to the additional
memory allocations needed (but I intend to tune this). Still,
it's only a few microseconds. Most of this can be attributed to
the speed of your memory allocator. And you can recycle coroutines,
if you really need to.

I was going to post a detailed comparison table between Coco and
RVM, but I found hardly anything that's against Coco: portability
is quite good and the C stack issue is manageable. Other than
that, Coco seems to be the cleaner/simpler approach.

Oh, and it has a cool name, too (compared to my other offerings). :-)

I really need one of these solutions for an upcoming project
of mine (LuaJIT -- please be patient). Right now I'm leaning
more towards Coco than RVM. But I've not made up my mind, yet.

Comments welcome!