lua-users home
lua-l archive

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


Hi,

attached is a patch against Lua-5.1-work2 to add 'true' C coroutines to the
Lua core. This was inspired by the recent discussion about the pcall/yield
problem and by Edgar's suggestion (and code snippet) to add C coroutines.

In fact it was simpler than I thought! The changes to the core are minimal
and well localized. The patch looks big, but this is just because lcstack.h
contains a giant #ifdef mess. Don't be scared away because of it -- it
already covers a fairly large number of common platforms (and it is pretty
simple to add new ones).


How It Works
------------

The way this works is that you can selectively allocate an extra C stack
for any coroutine. Once the extra C stack is set up, you can yield from
anywhere inside this coroutine, even across the C stack (i.e. across
pcall()). You can even yield from within a C function and get the arguments
from the resume, without leaving the C function.

By default coroutines do not get an extra C stack and everything runs on
a single C stack. This is exactly like the 'classic' behaviour. Of course
you don't get the advantages of 'true' C coroutines, then. There should
not be any noticeable difference for execution speed or memory usage
against the unpatched Lua core.

The idea is that you (as an application writer) can freely choose which
coroutines get a C stack and which don't need one. You can even modify
the C stack size according to your needs.

I have attached a simple Lua script that tests several variations of
using coroutines and shows what happens (please note: some error messages
are intentional -- e.g. for pcall/yield without a C stack).


How to Compile It
-----------------

The patch comes with HAVE_C_CORO defined in include/luaconf.h. However
this only works when you compile it with a plain C compiler and *not*
a C++ compiler. This is because C++ exception handling will probably
get royally confused by stack switching. I haven't even tried it.

The alternative is to use the plain C exception handling even when
compiling with C++ (modify the #ifdef __cplusplus in include/luaconf.h).
This should work ok, unless your C++ code leaks exceptions. But then
you have a problem, anyway ...

If your platform is not supported, then please have a look at your system's
setjmp.h and try to find out how to set the stack pointer and the program
counter (instruction pointer). Often this can be guessed from the
definitions or by dumping the contents of jmp_buf after a _setjmp() at
several stack depths or code locations. If you've come up with a working
solution, please share your modifications with the list or send them on
to me.

You get a more or less original Lua core when HAVE_C_CORO is not defined.
The new C API functions are still present, but throw errors.

This is the list of currently supported platforms:

* WIN32: MSVC
* WIN32: mingw32
* WIN32: Cygwin

* i386-linux-glibc2
* i386-linux-libc5
* i386-FreeBSD
* IA32-NetBSD
* x86_64-linux-glibc2
* powerpc*-linux-glibc2
* ppc-os-x
* ARM-linux-glibc2
* mips*-linux-glibc2
* hppa-linux-glibc2
* alpha-linux-glibc2
* alpha-FreeBSD
* s390*-linux-glibc2
* m68k-linux-glibc2
* vax-linux-glibc2

Most of them are untested, so please report success or failure. Thank you!


C API Extensions
----------------

  LUA_API int  lua_cyield (lua_State *L, int nresults);
  LUA_API void lua_setcstack (lua_State *L, int size);

The 'classic' lua_yield() can only be used as the return expression of
a C function, because of the required C stack unwinding (triggered by the
special return value -1). This also means that you can neither get the
results returned by the resume nor continue processing in your C function.

The 'new' lua_cyield() *can* yield across the C stack. It returns the number
of arguments from the resume. The passed results on the Lua stack are
replaced by the arguments from the resume. Everything below on the Lua stack
is still intact (i.e. there is no requirement for lua_gettop() == nargs).
The C stack and the current C function context are still intact, too
(i.e. lua_cyield() can be called from deeply nested C functions).

As an added bonus lua_yield() has been extended to use lua_cyield() when
there is an intervening C call on the stack. Mixing coroutines and pcall()
is now painless (provided you allocate a C stack for every coroutine).

The API function lua_setcstack() (re)allocates the C stack for a coroutine.
This works as long as the coroutine has not been started yet. Usually
you want to set the C stack size once, immediately after lua_newthread().

The stack size must be large enough to accomodate the deepest C call
nesting in your application. Do not forget to include the stack space used
by C libraries in your calculation. Try to avoid (deep) C stack recursion
when using coroutines (pure Lua recursion is not affected).

My recommendation is to use anything from 256K up to 2M for the C stack size
if you don't have to worry about memory usage. Use 32K or 64K if you can
restrain yourself from calling problematic C libraries. You may go as low
as 4K if you know exactly what you are doing (and need it -- e.g. with
tons of coroutines). 4K just happens to be the (arbitrary) lower limit
defined in lcstack.h.


Lua API Extensions
------------------

coroutine.create and coroutine.wrap have grown a second optional parameter
that specifies the C stack size:

  coroutine.create(func [, cstack] )
  coroutine.wrap(func [, cstack] )

The default is zero, i.e. the classic behaviour.

The patch also contains a new function coroutine.cyield() that directly
calls lua_cyield(). This is *only intended for testing*. It does not make
sense to have an extra Lua API for this, since lua_yield() falls back on
lua_cyield() if required.

Note that the behaviour is subtly different: coroutine.yield() unwinds
the C stack if possible, but coroutine.cyield() never does that.


TODO
----

Please regard this patch as a first proof of concept. It works reasonably
well for me, but I have not (re)written any large applications for it (yet).

A few things are still missing:

- Verify all platform-specific definitions. Many of them are untested.
- Use mmap() for C stack allocation with guard pages and/or do an
  (imperfect) check against C stack overflow in luaD_call().
- Add more checks against (unintentional) misuse of the C API.
- Add more assertions.
- Write a complete test suite.
- Write some more documentation.

Of course I welcome any and all comments on how to improve the patch.
Thanks in advance!

Bye,
     Mike
local function cofunc(yy, ...)
  print("==>", ...)
  for i=1,2 do
    print("-->", yy("yield  "..i))
  end
  return "return"
end

local function copc(...)
  local ok, ret = pcall(cofunc, ...)
  if not ok then error(ret) end
  return ret
end

local function test(f, s, y)
  local function yn(x) return x and "yes" or "no " end

  if not coroutine.cyield and y then return end
  local ok, co = pcall(coroutine.create, f and copc or cofunc, s and 65536 or 0)
  if not ok then return end
  local yy = y and coroutine.cyield or coroutine.yield

  print("***", "pcall = "..yn(f), "C stack = "..yn(s), "cyield = "..yn(y))
  print()
  local ok, res = coroutine.resume(co, yy, "call")
  for i=1,3 do
    print("<--", res)
    if not ok or i == 3 then break end
    ok, res = coroutine.resume(co, "resume "..i)
  end
  print()
end

test(false, false, false)
test(false, false, true)
test(false, true,  false)
test(false, true,  true)
test(true,  false, false)
test(true,  false, true)
test(true,  true,  false)
test(true,  true,  true)

diff -urN lua-5.1-work2/include/lua.h lua-5.1-work2-ccoro/include/lua.h
--- lua-5.1-work2/include/lua.h	2004-09-21 02:28:03.000000000 +0200
+++ lua-5.1-work2-ccoro/include/lua.h	2004-10-22 08:37:51.000000000 +0200
@@ -211,6 +211,8 @@
 LUA_API int  lua_yield (lua_State *L, int nresults);
 LUA_API int  lua_resume (lua_State *L, int narg);
 LUA_API int  lua_threadstatus (lua_State *L);
+LUA_API int  lua_cyield (lua_State *L, int nresults);
+LUA_API void lua_setcstack (lua_State *L, int size);
 
 /*
 ** garbage-collection function and options
diff -urN lua-5.1-work2/include/luaconf.h lua-5.1-work2-ccoro/include/luaconf.h
--- lua-5.1-work2/include/luaconf.h	2004-09-10 19:30:46.000000000 +0200
+++ lua-5.1-work2-ccoro/include/luaconf.h	2004-10-22 08:37:51.000000000 +0200
@@ -215,10 +215,13 @@
 #ifndef __cplusplus
 /* default handling with long jumps */
 #include <setjmp.h>
-#define L_THROW(c)	longjmp((c)->b, 1)
-#define L_TRY(c,a)	if (setjmp((c)->b) == 0) { a }
+#define L_THROW(c)	_longjmp((c)->b, 1)
+#define L_TRY(c,a)	if (_setjmp((c)->b) == 0) { a }
 #define l_jmpbuf	jmp_buf
 
+/* define this if your platform has support for C coroutines (see lcstack.h) */
+#define HAVE_C_CORO
+
 #else
 /* C++ exceptions */
 #define L_THROW(c)	throw(c)
diff -urN lua-5.1-work2/src/lstate.h lua-5.1-work2-ccoro/src/lstate.h
--- lua-5.1-work2/src/lstate.h	2004-09-15 22:39:42.000000000 +0200
+++ lua-5.1-work2-ccoro/src/lstate.h	2004-10-22 08:37:51.000000000 +0200
@@ -120,6 +120,12 @@
   GCObject *gclist;
   struct lua_longjmp *errorJmp;  /* current error recover point */
   ptrdiff_t errfunc;  /* current error handling function (stack index) */
+#ifdef HAVE_C_CORO
+  lu_byte *cstack;
+  int cstacksize;
+  void *cstackjmpin;
+  struct lua_longjmp *cstackjmpout;
+#endif
 };
 
 
diff -urN lua-5.1-work2/src/ldo.c lua-5.1-work2-ccoro/src/ldo.c
--- lua-5.1-work2/src/ldo.c	2004-09-15 22:39:42.000000000 +0200
+++ lua-5.1-work2-ccoro/src/ldo.c	2004-10-22 08:37:51.000000000 +0200
@@ -29,6 +29,9 @@
 #include "lvm.h"
 #include "lzio.h"
 
+#ifdef HAVE_C_CORO
+#include "lcstack.h"
+#endif
 
 
 
@@ -380,6 +383,75 @@
 }
 
 
+#ifdef HAVE_C_CORO
+static lua_State *cstack_pass_L;
+static int cstack_pass_nargs;
+
+static int cstack_jmpout (lua_State *L) {
+  jmp_buf buf;
+  int n;
+  lua_assert(L->stackjmpin == NULL);
+  lua_assert(L->stackjmpout != NULL);
+  if ((n = _setjmp(buf)) == 0) {
+    L->cstackjmpin = buf;
+    _longjmp(L->cstackjmpout->b, 1);
+  }
+  L->cstackjmpin = NULL;
+  return n - 1;
+}
+
+
+static int cstack_yield (lua_State *L, int nresults) {
+  int nargs;
+  ptrdiff_t base = savestack(L, L->base);
+  L->base = L->ci->base = L->top - nresults;
+  L->status = LUA_YIELD;
+  nargs = cstack_jmpout(L);
+  L->status = 0;
+  L->base = L->ci->base = restorestack(L, base);
+  return nargs;
+}
+
+
+static void cstack_main (void) {
+  lua_State *L = cstack_pass_L;
+  int nargs = cstack_pass_nargs;
+  for (;;) {
+    resume(L, &nargs);
+    nargs = cstack_jmpout(L);
+  }
+}
+
+
+static void cstack_enter (lua_State *L, int nargs) {
+  jmp_buf buf;
+  _setjmp(buf);
+  SETJMP_PATCH(buf, cstack_main, ALIGNED_CSTACK(L->cstack, L->cstacksize));
+  cstack_pass_L = L;
+  cstack_pass_nargs = nargs;
+  _longjmp(buf, 1);
+}
+
+
+static int cstack_resume (lua_State *L, int nargs) {
+  struct lua_longjmp *lj = L->cstackjmpout;
+  if (L->cstackjmpin) {
+    if (setjmp(lj->b) == 0)
+      _longjmp(L->cstackjmpin, nargs + 1);
+  } else {
+    lj->status = 0;
+    lj->previous = NULL;
+    if (setjmp(lj->b) == 0) {
+      L->errorJmp = lj;
+      cstack_enter(L, nargs);
+    }
+    L->errorJmp = NULL;
+  }
+  return lj->status;
+}
+#endif
+
+
 static int resume_error (lua_State *L, const char *msg) {
   L->top = L->ci->base;
   setsvalue2s(L, L->top, luaS_new(L, msg));
@@ -401,7 +473,12 @@
       return resume_error(L, "cannot resume non-suspended coroutine");
   }
   old_allowhooks = L->allowhook;
-  status = luaD_rawrunprotected(L, resume, &nargs);
+#ifdef HAVE_C_CORO
+  if (L->cstack)
+    status = cstack_resume(L, nargs);
+  else
+#endif
+    status = luaD_rawrunprotected(L, resume, &nargs);
   if (status != 0) {  /* error? */
     L->status = status;  /* mark thread as `dead' */
     seterrorobj(L, status, L->top);
@@ -413,12 +490,40 @@
 }
 
 
+LUA_API int lua_cyield (lua_State *L, int nresults) {
+#ifdef HAVE_C_CORO
+  int nargs;
+  lua_lock(L);
+  if (!L->cstack)
+    luaG_runerror(L, "attempt to C yield and no C stack allocated");
+  nargs = cstack_yield(L, nresults);
+  lua_unlock(L);
+  return nargs;
+#else
+  lua_lock(L);
+  luaG_runerror(L, "C yield not supported");
+  lua_unlock(L);
+  return 0;
+#endif
+}
+
+
 LUA_API int lua_yield (lua_State *L, int nresults) {
   CallInfo *ci;
   lua_lock(L);
-  ci = L->ci;
-  if (L->nCcalls > 0)
+  if (L->nCcalls > 0) {
+#ifdef HAVE_C_CORO
+    int nargs;
+    if (!L->cstack)
+      luaG_runerror(L, "attempt to yield across metamethod/C-call boundary (and no C stack allocated)");
+    nargs = cstack_yield(L, nresults);
+    lua_unlock(L);
+    return nargs;
+#else
     luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
+#endif
+  }
+  ci = L->ci;
   if (!f_isLua(ci)) {  /* usual yield */
     if (L->top - nresults > L->base) {  /* is there garbage in the stack? */
       int i;
@@ -433,6 +538,28 @@
 }
 
 
+LUA_API void lua_setcstack (lua_State *L, int size) {
+#ifdef HAVE_C_CORO
+  lua_lock(L);
+  if (G(L)->mainthread == L)
+    luaG_runerror(L, "attempt to modify C stack of main thread");
+  if (L->status != 0 || L->ci != L->base_ci)
+    luaG_runerror(L, "attempt to modify C stack of a running thread");
+  if (size <= 0) size = 0;
+  else if (size <= MIN_CSTACK) size = MIN_CSTACK;
+  L->cstack = cast(lu_byte *, luaM_realloc(L, L->cstack, cast(lu_mem, L->cstacksize), cast(lu_mem, size)));
+  L->cstacksize = size;
+  L->cstackjmpin = NULL;
+  /* put jmp_buf at the end of the stack to ensure a crash on overwrite */
+  L->cstackjmpout = ALIGNED_LUA_LONGJMP(L->cstack, L->cstacksize);
+  lua_unlock(L);
+#else
+  if (size > 0)
+    luaG_runerror(G(L)->mainthread, "multiple C stacks not supported");
+#endif
+}
+
+
 int luaD_pcall (lua_State *L, Pfunc func, void *u,
                 ptrdiff_t old_top, ptrdiff_t ef) {
   int status;
diff -urN lua-5.1-work2/src/lstate.c lua-5.1-work2-ccoro/src/lstate.c
--- lua-5.1-work2/src/lstate.c	2004-09-15 22:39:42.000000000 +0200
+++ lua-5.1-work2-ccoro/src/lstate.c	2004-10-22 08:37:51.000000000 +0200
@@ -69,6 +69,9 @@
 static void freestack (lua_State *L, lua_State *L1) {
   luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
   luaM_freearray(L, L1->stack, L1->stacksize, TValue);
+#ifdef HAVE_C_CORO
+  if (L1->cstack) luaM_free(L, L1->cstack, L1->cstacksize);
+#endif
 }
 
 
@@ -118,6 +121,12 @@
   L->base_ci = L->ci = NULL;
   L->errfunc = 0;
   setnilvalue(gt(L));
+#ifdef HAVE_C_CORO
+  L->cstack = NULL;
+  L->cstacksize = 0;
+  L->cstackjmpin = NULL;
+  L->cstackjmpout = NULL;
+#endif
 }
 
 
diff -urN lua-5.1-work2/src/lgc.c lua-5.1-work2-ccoro/src/lgc.c
--- lua-5.1-work2/src/lgc.c	2004-09-15 22:38:15.000000000 +0200
+++ lua-5.1-work2-ccoro/src/lgc.c	2004-10-22 08:37:51.000000000 +0200
@@ -304,6 +304,9 @@
       black2gray(o);
       traversestack(g, th);
       return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
+#ifdef HAVE_C_CORO
+                                 th->cstacksize +
+#endif
                                  sizeof(CallInfo) * th->size_ci;
       break;
     }
diff -urN lua-5.1-work2/src/lib/lbaselib.c lua-5.1-work2-ccoro/src/lib/lbaselib.c
--- lua-5.1-work2/src/lib/lbaselib.c	2004-09-15 22:39:42.000000000 +0200
+++ lua-5.1-work2-ccoro/src/lib/lbaselib.c	2004-10-22 08:37:51.000000000 +0200
@@ -577,9 +577,11 @@
 
 
 static int luaB_cocreate (lua_State *L) {
+  int cstack = luaL_optint(L, 2, 0);
   lua_State *NL = lua_newthread(L);
   luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
     "Lua function expected");
+  if (cstack > 0) lua_setcstack(NL, cstack);
   lua_pushvalue(L, 1);  /* move function to top */
   lua_xmove(L, NL, 1);  /* move function from L to NL */
   return 1;
@@ -634,6 +636,11 @@
 }
 
 
+static int luaB_cyield (lua_State *L) {  /* XXX for testing purposes only */
+  return lua_cyield(L, lua_gettop(L));
+}
+
+
 static const luaL_reg co_funcs[] = {
   {"create", luaB_cocreate},
   {"wrap", luaB_cowrap},
@@ -641,6 +648,7 @@
   {"yield", luaB_yield},
   {"status", luaB_costatus},
   {"current", luaB_cocurrent},
+  {"cyield", luaB_cyield},  /* XXX for testing purposes only */
   {NULL, NULL}
 };
 
diff -urN lua-5.1-work2/src/lcstack.h lua-5.1-work2-ccoro/src/lcstack.h
--- lua-5.1-work2/src/lcstack.h	1970-01-01 01:00:00.000000000 +0100
+++ lua-5.1-work2-ccoro/src/lcstack.h	2004-10-22 08:37:51.000000000 +0200
@@ -0,0 +1,214 @@
+/*
+** Support for C stack switching.
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcstack_h
+#define lcstack_h
+
+#include <setjmp.h>
+
+/* ---- The following has been taken from: */
+
+/* Minimalistic cooperative multitasking - E. Toernig. */
+/* Many thanks to Steve Dekorte who provided most */
+/* of the setup_jmpbuf implementations. */
+
+/* ---- Cleaned up and added some more entries. -- Mike Pall */
+
+
+#if defined(__MINGW32__) || defined(_MSC_VER)	/* mingw32, MSVC */
+
+#define SETJMP_PATCH(buf, func, stack) \
+  buf[4] = (int)(stack); \
+  buf[5] = (int)(func);
+
+#elif defined(__CYGWIN__)			/* Cygwin */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf[7] = (int)(stack); \
+  buf[8] = (int)(func);
+
+#elif defined(__x86_64__) || defined(__x86_64)	/* x86_64/AMD64/EM64T */
+
+#if defined(JB_RSP) && defined(JB_PC)		/* x86_64-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf[JB_RSP] = (long)(stack); \
+  buf->__jmpbuf[JB_PC] = (long)(func);
+
+#endif
+
+#elif defined(i386) || defined(__i386__)	/* x86/i386/IA32 */
+
+#if defined(JB_SP) && defined(JB_PC)		/* i386-linux-glibc2 */
+
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf[JB_SP] = (int)(stack); \
+  buf->__jmpbuf[JB_PC] = (int)(func);
+
+#elif defined(_I386_JMP_BUF_H)			/* i386-linux-libc5 */
+
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__sp = (stack); \
+  buf->__pc = (func);
+
+#elif defined(_JBLEN)				/* i386-FreeBSD, IA32-NetBSD */
+
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->_jb[2] = (long)(stack); \
+  buf->_jb[0] = (long)(func);
+
+#endif
+
+#elif defined(PPC) || defined(__ppc__) || defined(__PPC__) || defined(__powerpc__) || defined(__POWERPC__) || defined(_ARCH_PPC)
+
+#if defined(JB_GPR1) && defined(JB_LR)		/* powerpc*-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  memset((stack)-64, 0, 64); /* XXX is this required? */ \
+  buf->__jmpbuf[JB_GPR1] = (long)(stack)-64; \
+  buf->__jmpbuf[JB_LR] = (long)(func);
+
+#elif defined(_BSD_PPC_SETJMP_H_)		/* ppc-os-x */
+
+#define SETJMP_PATCH(buf, func, stack) \
+  memset((stack)-64, 0, 64); /* XXX is this required? */ \
+  buf[0] = (int)(stack)-64; \
+  buf[21] = (int)(func);
+
+#endif
+
+#elif defined(__arm__) || defined(__ARM__)
+
+#if defined(__JMP_BUF_SP)			/* ARM-linux-glibc2 (zaurus) */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf[__JMP_BUF_SP] = (int)(stack); \
+  buf->__jmpbuf[__JMP_BUF_SP+1] = (int)(func);
+
+#endif
+
+#elif defined(__mips__) || defined(__MIPS__)
+
+#if defined(JB_PC)				/* mips*-linux-glibc2 */
+
+/*UNTESTED*/
+#if _MIPS_SIM == _MIPS_SIM_ABI32
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf->__sp = (stack); \
+  buf->__jmpbuf->__pc = (func);
+#else
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf->__sp = (long long)(stack); \
+  buf->__jmpbuf->__pc = (long long)(func);
+#endif
+
+#endif
+
+#elif defined(__hppa__) || defined(__HPPA__)
+
+#define STACK_UPWARDS 1
+
+#if defined(JB_SP)				/* hppa-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  ((unsigned long *)(buf->__jmpbuf))[JB_SP] = (unsigned long)(stack); \
+  ((unsigned long *)(buf->__jmpbuf))[JB_SP+1] = (unsigned long)(func);
+
+#endif
+
+#elif defined(__alpha) || defined(__alpha__) || defined(__ALPHA__)
+
+#if defined(JB_SP) && defined(JB_PC)		/* alpha-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf[JB_SP] = (long)(stack); \
+  buf->__jmpbuf[JB_PC] = (long)(func);
+
+#elif defined(_JBLEN) && (_JBLEN == 81)		/* alpha-FreeBSD */
+
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->_jb[2] = (long)(func);     /* sc_pc */ \
+  buf->_jb[26+4] = (long)(func);  /* sc_regs[R_RA] */ \
+  buf->_jb[27+4] = (long)(func);  /* sc_regs[R_T12] */ \
+  buf->_jb[30+4] = (long)(stack);   /* sc_regs[R_SP] */
+
+#endif
+
+#elif defined(__s390__) || defined(__S390__)
+
+#if defined(__JB_GPR14) && defined(__JB_GPR15)	/* s390*-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf->__gregs[__JB_GPR15] = (long)(stack); \
+  buf->__jmpbuf->__gregs[__JB_GPR14] = (long)(func);
+
+#endif
+
+#elif defined(__m68k__) || defined(__mc68000__) || defined(__mc68020__)
+
+#if defined(__GLIBC__)				/* m68k-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__jmpbuf->__sp = (int *)(stack); \
+  buf->__jmpbuf->__aregs[0] = (int *)(func);
+
+#endif
+
+#elif defined(__vax__) || defined(__VAX__)
+
+#if defined(__GLIBC__)				/* vax-linux-glibc2 */
+
+/*UNTESTED*/
+#define SETJMP_PATCH(buf, func, stack) \
+  buf->__fp = (stack); \
+  buf->__pc = (func);
+
+#endif
+
+#endif
+
+
+#ifndef SETJMP_PATCH
+#error "HAVE_C_CORO defined, but no platform support in lcstack.h (yet)"
+#endif
+
+#ifndef MIN_CSTACK
+#define MIN_CSTACK      4096
+#endif
+
+#ifndef ALIGN_CSTACK
+#define ALIGN_CSTACK    16
+#endif
+
+#ifndef ALIGN_LUA_LONGJMP
+#define ALIGN_LUA_LONGJMP 16
+#endif
+
+#define PALIGN_D(t, ptr, align) \
+  ((t *)(((ptrdiff_t)(ptr)) & -(align)))
+#define PALIGN_U(t, ptr, align) \
+  ((t *)(((ptrdiff_t)(ptr) + (align) - 1) & -(align)))
+
+#ifdef STACK_UPWARDS
+#define ALIGNED_CSTACK(stack, size) \
+  PALIGN_U(lu_byte, stack, ALIGN_CSTACK)
+#define ALIGNED_LUA_LONGJMP(stack, size) \
+  PALIGN_D(struct lua_longjmp, (stack) + (size) - sizeof(struct lua_longjmp), ALIGN_LUA_LONGJMP)
+#else
+#define ALIGNED_CSTACK(stack, size) \
+  PALIGN_D(lu_byte, (stack) + (size), ALIGN_CSTACK)
+#define ALIGNED_LUA_LONGJMP(stack, size) \
+  PALIGN_U(struct lua_longjmp, (stack), ALIGN_LUA_LONGJMP)
+#endif
+
+#endif