Pārlūkot izejas kodu

threads now are real Lua objects, subject to garbage collection

Roberto Ierusalimschy 23 gadi atpakaļ
vecāks
revīzija
96e15b8501
12 mainītis faili ar 246 papildinājumiem un 204 dzēšanām
  1. 19 1
      lapi.c
  2. 42 34
      lbaselib.c
  3. 16 10
      ldo.c
  4. 48 41
      lgc.c
  5. 29 38
      lobject.h
  6. 52 51
      lstate.c
  7. 20 11
      lstate.h
  8. 3 4
      ltests.c
  9. 6 5
      ltests.h
  10. 2 2
      ltm.c
  11. 3 2
      lua.h
  12. 6 5
      lvm.c

+ 19 - 1
lapi.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lapi.c,v 1.212 2002/08/30 19:09:21 roberto Exp roberto $
+** $Id: lapi.c,v 1.213 2002/09/20 17:01:24 roberto Exp roberto $
 ** Lua API
 ** See Copyright Notice in lua.h
 */
@@ -115,6 +115,18 @@ LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
 }
 
 
+LUA_API lua_State *lua_newthread (lua_State *L) {
+  lua_State *L1;
+  lua_lock(L);
+  L1 = luaE_newthread(L);
+  setthvalue(L->top, L1);
+  api_incr_top(L);
+  lua_unlock(L);
+  lua_userstateopen(L1);
+  return L1;
+}
+
+
 /*
 ** basic stack manipulation
 */
@@ -322,6 +334,12 @@ LUA_API void *lua_touserdata (lua_State *L, int index) {
 }
 
 
+LUA_API lua_State *lua_tothread (lua_State *L, int index) {
+  StkId o = luaA_indexAcceptable(L, index);
+  return (o == NULL || !ttisthread(o)) ? NULL : thvalue(o);
+}
+
+
 LUA_API const void *lua_topointer (lua_State *L, int index) {
   StkId o = luaA_indexAcceptable(L, index);
   if (o == NULL) return NULL;

+ 42 - 34
lbaselib.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lbaselib.c,v 1.99 2002/09/16 19:49:45 roberto Exp roberto $
+** $Id: lbaselib.c,v 1.100 2002/10/22 19:41:08 roberto Exp roberto $
 ** Basic library
 ** See Copyright Notice in lua.h
 */
@@ -362,6 +362,9 @@ static int luaB_tostring (lua_State *L) {
     case LUA_TLIGHTUSERDATA:
       sprintf(buff, "userdata: %p", lua_touserdata(L, 1));
       break;
+    case LUA_TTHREAD:
+      sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1));
+      break;
     case LUA_TNIL:
       lua_pushliteral(L, "nil");
       return 1;
@@ -535,26 +538,40 @@ static const luaL_reg base_funcs[] = {
 */
 
 
-static int luaB_resume (lua_State *L) {
-  lua_State *co = (lua_State *)lua_unboxpointer(L, lua_upvalueindex(1));
+static int luaB_auxresume (lua_State *L, lua_State *co) {
   int status;
-  lua_settop(L, 0);
+  int oldtop = lua_gettop(L);
   status = lua_resume(L, co);
-  if (status != 0)
-    return lua_error(L);
-  return lua_gettop(L);
+  return (status != 0) ? -1 : lua_gettop(L) - oldtop;
 }
 
 
+static int luaB_coresume (lua_State *L) {
+  lua_State *co = lua_tothread(L, 1);
+  int r;
+  luaL_arg_check(L, co, 1, "coroutine/thread expected");
+  r = luaB_auxresume(L, co);
+  if (r < 0) {
+    lua_pushboolean(L, 0);
+    lua_insert(L, -2);
+    return 2;  /* return false + error message */
+  }
+  else {
+    lua_pushboolean(L, 1);
+    lua_insert(L, -(r + 1));
+    return r + 1;  /* return true + `resume' returns */
+  }
+}
+
 
-static int gc_coroutine (lua_State *L) {
-  lua_State *co = (lua_State *)lua_unboxpointer(L, 1);
-  lua_closethread(L, co);
-  return 0;
+static int luaB_auxwrap (lua_State *L) {
+  int r = luaB_auxresume(L, lua_tothread(L, lua_upvalueindex(1)));
+  if (r < 0) lua_error(L);
+  return r;
 }
 
 
-static int luaB_coroutine (lua_State *L) {
+static int luaB_cocreate (lua_State *L) {
   lua_State *NL;
   int ref;
   int i;
@@ -562,20 +579,21 @@ static int luaB_coroutine (lua_State *L) {
   luaL_arg_check(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
     "Lua function expected");
   NL = lua_newthread(L);
-  if (NL == NULL) return luaL_error(L, "unable to create new thread");
   /* move function and arguments from L to NL */
-  for (i=0; i<n; i++) {
+  for (i = 1; i <= n; i++) {
+    lua_pushvalue(L, i);
     ref = lua_ref(L, 1);
     lua_getref(NL, ref);
-    lua_insert(NL, 1);
     lua_unref(L, ref);
   }
   lua_cobegin(NL, n-1);
-  lua_boxpointer(L, NL);
-  lua_pushliteral(L, "Coroutine");
-  lua_rawget(L, LUA_REGISTRYINDEX);
-  lua_setmetatable(L, -2);
-  lua_pushcclosure(L, luaB_resume, 1);
+  return 1;
+}
+
+
+static int luaB_cowrap (lua_State *L) {
+  luaB_cocreate(L);
+  lua_pushcclosure(L, luaB_auxwrap, 1);
   return 1;
 }
 
@@ -585,23 +603,13 @@ static int luaB_yield (lua_State *L) {
 }
 
 static const luaL_reg co_funcs[] = {
-  {"create", luaB_coroutine},
+  {"create", luaB_cocreate},
+  {"wrap", luaB_cowrap},
+  {"resume", luaB_coresume},
   {"yield", luaB_yield},
   {NULL, NULL}
 };
 
-
-static void co_open (lua_State *L) {
-  luaL_opennamedlib(L, LUA_COLIBNAME, co_funcs, 0);
-  /* create metatable for coroutines */
-  lua_pushliteral(L, "Coroutine");
-  lua_newtable(L);
-  lua_pushliteral(L, "__gc");
-  lua_pushcfunction(L, gc_coroutine);
-  lua_rawset(L, -3);
-  lua_rawset(L, LUA_REGISTRYINDEX);
-}
-
 /* }====================================================== */
 
 
@@ -625,7 +633,7 @@ static void base_open (lua_State *L) {
 
 LUALIB_API int lua_baselibopen (lua_State *L) {
   base_open(L);
-  co_open(L);
+  luaL_opennamedlib(L, LUA_COLIBNAME, co_funcs, 0);
   lua_newtable(L);
   lua_setglobal(L, REQTAB);
   return 0;

+ 16 - 10
ldo.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ldo.c,v 1.195 2002/10/08 18:46:08 roberto Exp roberto $
+** $Id: ldo.c,v 1.196 2002/10/09 13:42:01 roberto Exp roberto $
 ** Stack and Call structure of Lua
 ** See Copyright Notice in lua.h
 */
@@ -343,16 +343,22 @@ LUA_API int lua_resume (lua_State *L, lua_State *co) {
   int status;
   lua_lock(L);
   ci = co->ci;
-  if (ci == co->base_ci)  /* no activation record? ?? */
-    luaG_runerror(L, "cannot resume dead thread");
-  if (co->errorJmp != NULL)  /* ?? */
-    luaG_runerror(L, "cannot resume active thread");
-  status = luaD_rawrunprotected(co, resume, &numres);
-  if (status == 0)  
-    move_results(L, co->top - numres, co->top);
+  if (ci == co->base_ci) {  /* no activation record? ?? */
+    luaO_pushfstring(L, "cannot resume dead thread");
+    status = LUA_ERRRUN;
+  }
+  else if (co->errorJmp != NULL) {  /* ?? */
+    luaO_pushfstring(L, "cannot resume active thread");
+    status = LUA_ERRRUN;
+  }
   else {
-    setobj(L->top++, co->top - 1);  /* move error message to other stack */
-    co->ci = co->base_ci;  /* `kill' thread */
+    status = luaD_rawrunprotected(co, resume, &numres);
+    if (status == 0)  
+      move_results(L, co->top - numres, co->top);
+    else {
+      setobj(L->top++, co->top - 1);  /* move error message to other stack */
+      co->ci = co->base_ci;  /* `kill' thread */
+    }
   }
   lua_unlock(L);
   return status;

+ 48 - 41
lgc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 1.152 2002/10/08 18:46:08 roberto Exp roberto $
+** $Id: lgc.c,v 1.153 2002/10/22 17:58:14 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -99,6 +99,32 @@ static void markclosure (GCState *st, Closure *cl) {
 }
 
 
+static void checkstacksizes (lua_State *L, StkId max) {
+  int used = L->ci - L->base_ci;  /* number of `ci' in use */
+  if (4*used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
+    luaD_reallocCI(L, L->size_ci/2);  /* still big enough... */
+  used = max - L->stack;  /* part of stack in use */
+  if (4*used < L->stacksize && 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
+    luaD_reallocstack(L, L->stacksize/2);  /* still big enough... */
+}
+
+
+static void markstack (GCState *st, lua_State *L1) {
+  StkId o, lim;
+  CallInfo *ci;
+  markobject(st, gt(L1));
+  for (o=L1->stack; o<L1->top; o++)
+    markobject(st, o);
+  lim = o;
+  for (ci = L1->base_ci; ci <= L1->ci; ci++) {
+    lua_assert(ci->top <= L1->stack_last);
+    if (lim < ci->top) lim = ci->top;
+  }
+  for (; o<=lim; o++) setnilvalue(o);
+  checkstacksizes(L1, lim);
+}
+
+
 static void reallymarkobject (GCState *st, GCObject *o) {
   setbit(o->gch.marked, 0);  /* mark object */
   switch (o->gch.tt) {
@@ -115,46 +141,11 @@ static void reallymarkobject (GCState *st, GCObject *o) {
       st->tmark = &o->h;
       break;
     }
-  }
-}
-
-
-static void checkstacksizes (lua_State *L, StkId max) {
-  int used = L->ci - L->base_ci;  /* number of `ci' in use */
-  if (4*used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
-    luaD_reallocCI(L, L->size_ci/2);  /* still big enough... */
-  used = max - L->stack;  /* part of stack in use */
-  if (4*used < L->stacksize && 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
-    luaD_reallocstack(L, L->stacksize/2);  /* still big enough... */
-}
-
-
-static void traversestacks (GCState *st) {
-  lua_State *L1 = st->L;
-  do {  /* for each thread */
-    StkId o, lim;
-    CallInfo *ci;
-    if (ttisnil(gt(L1))) {  /* incomplete state? */
-      lua_assert(L1 != st->L);
-      L1 = L1->next;
-      luaE_closethread(st->L, L1->previous);  /* collect it */
-      continue;
-    }
-    markobject(st, gt(L1));
-    for (o=L1->stack; o<L1->top; o++)
-      markobject(st, o);
-    lim = o;
-    for (ci = L1->base_ci; ci <= L1->ci; ci++) {
-      lua_assert(ci->top <= L1->stack_last);
-      if (lim < ci->top) lim = ci->top;
+    case LUA_TTHREAD: {
+      markstack(st, &o->th);
+      break;
     }
-    for (; o<=lim; o++) setnilvalue(o);
-    checkstacksizes(L1, lim);
-    lua_assert(L1->previous->next == L1 && L1->next->previous == L1);
-    L1 = L1->next;
-  } while (L1 != st->L);
-  markobject(st, defaultmeta(L1));
-  markobject(st, registry(L1));
+  }
 }
 
 
@@ -292,6 +283,11 @@ static void freeobj (lua_State *L, GCObject *o) {
     case LUA_TFUNCTION: luaF_freeclosure(L, &o->cl); break;
     case LUA_TUPVAL: luaM_freelem(L, &o->uv); break;
     case LUA_TTABLE: luaH_free(L, &o->h); break;
+    case LUA_TTHREAD: {
+      lua_assert(&o->th != L && &o->th != G(L)->mainthread);
+      luaE_freethread(L, &o->th);
+      break;
+    }
     case LUA_TSTRING: {
       luaM_free(L, o, sizestring((&o->ts)->tsv.len));
       break;
@@ -389,13 +385,24 @@ void luaC_sweep (lua_State *L, int all) {
 }
 
 
+/* mark root set */
+static void markroot (GCState *st) {
+  lua_State *L = st->L;
+  markobject(st, defaultmeta(L));
+  markobject(st, registry(L));
+  markstack(st, G(L)->mainthread);
+  if (L != G(L)->mainthread)  /* another thread is running? */
+    reallymarkobject(st, cast(GCObject *, L));  /* cannot collect it */
+}
+
+
 static void mark (lua_State *L) {
   GCState st;
   Table *toclear;
   st.L = L;
   st.tmark = NULL;
   st.toclear = NULL;
-  traversestacks(&st); /* mark all stacks */
+  markroot(&st);
   propagatemarks(&st);  /* mark all reachable objects */
   toclear = st.toclear;  /* weak tables; to be cleared */
   st.toclear = NULL;

+ 29 - 38
lobject.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lobject.h,v 1.148 2002/10/16 20:40:58 roberto Exp roberto $
+** $Id: lobject.h,v 1.149 2002/10/22 17:18:28 roberto Exp roberto $
 ** Type definitions for Lua objects
 ** See Copyright Notice in lua.h
 */
@@ -13,7 +13,7 @@
 
 
 /* tags for values visible from Lua */
-#define NUM_TAGS	LUA_TUSERDATA
+#define NUM_TAGS	LUA_TTHREAD
 
 
 /*
@@ -23,23 +23,34 @@
 #define LUA_TUPVAL	(NUM_TAGS+2)
 
 
+/*
+** Union of all collectable objects
+*/
+typedef union GCObject GCObject;
+
+
 /*
 ** Common header for all collectable objects
 */
 typedef struct GCheader {
-  union GCObject *next;  /* pointer to next object */
+  GCObject *next;  /* pointer to next object */
   lu_byte tt;  /* object type */
   lu_byte marked;  /* GC informations */
 } GCheader;
 
 
+/*
+** common header in macro form, to be included in other objects
+*/
+#define CommonHeader	GCObject *next; lu_byte tt; lu_byte marked
+
 
 
 /*
 ** Union of all Lua values
 */
 typedef union {
-  union GCObject *gc;
+  GCObject *gc;
   void *p;
   lua_Number n;
   int b;
@@ -63,6 +74,7 @@ typedef struct lua_TObject {
 #define ttisfunction(o)	(ttype(o) == LUA_TFUNCTION)
 #define ttisboolean(o)	(ttype(o) == LUA_TBOOLEAN)
 #define ttisuserdata(o)	(ttype(o) == LUA_TUSERDATA)
+#define ttisthread(o)	(ttype(o) == LUA_TTHREAD)
 #define ttislightuserdata(o)	(ttype(o) == LUA_TLIGHTUSERDATA)
 
 /* Macros to access values */
@@ -75,6 +87,7 @@ typedef struct lua_TObject {
 #define clvalue(o)	check_exp(ttisfunction(o), &(o)->value.gc->cl)
 #define hvalue(o)	check_exp(ttistable(o), &(o)->value.gc->h)
 #define bvalue(o)	check_exp(ttisboolean(o), (o)->value.b)
+#define thvalue(o)	check_exp(ttisthread(o), &(o)->value.gc->th)
 
 #define l_isfalse(o)	(ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
 
@@ -101,6 +114,11 @@ typedef struct lua_TObject {
     i_o->value.gc=cast(GCObject *, (x)); \
     lua_assert(i_o->value.gc->gch.tt == LUA_TUSERDATA); }
 
+#define setthvalue(obj,x) \
+  { TObject *i_o=(obj); i_o->tt=LUA_TTHREAD; \
+    i_o->value.gc=cast(GCObject *, (x)); \
+    lua_assert(i_o->value.gc->gch.tt == LUA_TTHREAD); }
+
 #define setclvalue(obj,x) \
   { TObject *i_o=(obj); i_o->tt=LUA_TFUNCTION; \
     i_o->value.gc=cast(GCObject *, (x)); \
@@ -142,9 +160,7 @@ typedef TObject *StkId;  /* index to stack elements */
 typedef union TString {
   L_Umaxalign dummy;  /* ensures maximum alignment for strings */
   struct {
-    union GCObject *next;  /* pointer to next object */
-    lu_byte tt;  /* object type */
-    lu_byte marked;  /* GC informations */
+    CommonHeader;
     lu_byte reserved;
     lu_hash hash;
     size_t len;
@@ -160,9 +176,7 @@ typedef union TString {
 typedef union Udata {
   L_Umaxalign dummy;  /* ensures maximum alignment for `local' udata */
   struct {
-    union GCObject *next;  /* pointer to next object */
-    lu_byte tt;  /* object type */
-    lu_byte marked;  /* GC informations */
+    CommonHeader;
     struct Table *metatable;
     size_t len;
   } uv;
@@ -175,9 +189,7 @@ typedef union Udata {
 ** Function Prototypes
 */
 typedef struct Proto {
-  union GCObject *next;  /* pointer to next object */
-  lu_byte tt;  /* object type */
-  lu_byte marked;  /* GC informations */
+  CommonHeader;
   TObject *k;  /* constants used by the function */
   Instruction *code;
   struct Proto **p;  /* functions defined inside the function */
@@ -210,9 +222,7 @@ typedef struct LocVar {
 */
 
 typedef struct UpVal {
-  union GCObject *next;  /* pointer to next object */
-  lu_byte tt;  /* object type */
-  lu_byte marked;  /* GC informations */
+  CommonHeader;
   TObject *v;  /* points to stack or to its own value */
   TObject value;  /* the value (when closed) */
 } UpVal;
@@ -223,9 +233,7 @@ typedef struct UpVal {
 */
 
 typedef struct CClosure {
-  union GCObject *next;  /* pointer to next object */
-  lu_byte tt;  /* object type */
-  lu_byte marked;  /* GC informations */
+  CommonHeader;
   lu_byte isC;  /* 0 for Lua functions, 1 for C functions */
   lu_byte nupvalues;
   lua_CFunction f;
@@ -234,9 +242,7 @@ typedef struct CClosure {
 
 
 typedef struct LClosure {
-  union GCObject *next;  /* pointer to next object */
-  lu_byte tt;  /* object type */
-  lu_byte marked;  /* GC informations */
+  CommonHeader;
   lu_byte isC;
   lu_byte nupvalues;  /* first five fields must be equal to CClosure!! */
   struct Proto *p;
@@ -267,9 +273,7 @@ typedef struct Node {
 
 
 typedef struct Table {
-  union GCObject *next;  /* pointer to next object */
-  lu_byte tt;  /* object type */
-  lu_byte marked;  /* GC informations */
+  CommonHeader;
   lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ 
   lu_byte mode;
   lu_byte lsizenode;  /* log2 of size of `node' array */
@@ -298,19 +302,6 @@ typedef struct Table {
 #define sizearray(t)	((t)->sizearray)
 
 
-/*
-** Union of all collectable objects
-*/
-typedef union GCObject {
-  GCheader gch;
-  union TString ts;
-  union Udata u;
-  union Closure cl;
-  struct Table h;
-  struct Proto p;
-  struct UpVal uv;
-} GCObject;
-
 
 extern const TObject luaO_nilobject;
 

+ 52 - 51
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 1.106 2002/10/08 18:46:08 roberto Exp roberto $
+** $Id: lstate.c,v 1.107 2002/10/22 17:58:14 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -19,11 +19,30 @@
 #include "ltm.h"
 
 
+/*
+** macro to allow the inclusion of user information in Lua state
+*/
+#ifndef LUA_USERSTATE
+#define EXTRASPACE	0
+#else
+union UEXTRASPACE {L_Umaxalign a; LUA_USERSTATE b;};
+#define EXTRASPACE (sizeof(UEXTRASPACE))
+#endif
 
 
 static void close_state (lua_State *L);
 
 
+static lua_State *newthread (lua_State *L) {
+  lu_byte *block = (lu_byte *)luaM_malloc(L, sizeof(lua_State) + EXTRASPACE);
+  if (block == NULL) return NULL;
+  else {
+    block += EXTRASPACE;
+    return cast(lua_State *, block);
+  }
+}
+
+
 /*
 ** you can change this function through the official API:
 ** call `lua_setpanicf'
@@ -34,19 +53,19 @@ static int default_panic (lua_State *L) {
 }
 
 
-static void stack_init (lua_State *L, lua_State *OL) {
-  L->stack = luaM_newvector(OL, BASIC_STACK_SIZE + EXTRA_STACK, TObject);
-  L->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
-  L->top = L->stack;
-  L->stack_last = L->stack+(L->stacksize - EXTRA_STACK)-1;
-  L->base_ci = luaM_newvector(OL, BASIC_CI_SIZE, CallInfo);
-  L->ci = L->base_ci;
-  L->ci->state = CI_C;  /*  not a Lua function */
-  setnilvalue(L->top++);  /* `function' entry for this `ci' */
-  L->ci->base = L->top;
-  L->ci->top = L->top + LUA_MINSTACK;
-  L->size_ci = BASIC_CI_SIZE;
-  L->end_ci = L->base_ci + L->size_ci;
+static void stack_init (lua_State *L1, lua_State *L) {
+  L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TObject);
+  L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
+  L1->top = L1->stack;
+  L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;
+  L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
+  L1->ci = L1->base_ci;
+  L1->ci->state = CI_C;  /*  not a Lua function */
+  setnilvalue(L1->top++);  /* `function' entry for this `ci' */
+  L1->ci->base = L1->top;
+  L1->ci->top = L1->top + LUA_MINSTACK;
+  L1->size_ci = BASIC_CI_SIZE;
+  L1->end_ci = L1->base_ci + L1->size_ci;
 }
 
 
@@ -57,6 +76,7 @@ static void f_luaopen (lua_State *L, void *ud) {
   UNUSED(ud);
   /* create a new global state */
   L->l_G = luaM_new(L, global_State);
+  G(L)->mainthread = L;
   G(L)->GCthreshold = 0;  /* mark it as unfinished state */
   G(L)->strt.size = 0;
   G(L)->strt.nuse = 0;
@@ -103,31 +123,22 @@ static void preinit_state (lua_State *L) {
 }
 
 
-LUA_API lua_State *lua_newthread (lua_State *OL) {
-  lua_State *L;
-  lua_lock(OL);
-  L = luaM_new(OL, lua_State);
-  preinit_state(L);
-  L->l_G = OL->l_G;
-  OL->next->previous = L;  /* insert L into linked list */
-  L->next = OL->next;
-  OL->next = L;
-  L->previous = OL;
-  stack_init(L, OL);  /* init stack */
-  setobj(gt(L), gt(OL));  /* share table of globals */
-  lua_unlock(OL);
-  lua_userstateopen(L);
-  return L;
+lua_State *luaE_newthread (lua_State *L) {
+  lua_State *L1 = newthread(L);
+  luaC_link(L, cast(GCObject *, L1), LUA_TTHREAD);
+  preinit_state(L1);
+  L1->l_G = L->l_G;
+  stack_init(L1, L);  /* init stack */
+  setobj(gt(L1), gt(L));  /* share table of globals */
+  return L1;
 }
 
 
 LUA_API lua_State *lua_open (void) {
-  lua_State *L;
-  L = luaM_new(NULL, lua_State);
+  lua_State *L = newthread(NULL);
   if (L) {  /* allocation OK? */
     preinit_state(L);
     L->l_G = NULL;
-    L->next = L->previous = L;
     if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
       /* memory allocation error: free partial state */
       close_state(L);
@@ -139,14 +150,13 @@ LUA_API lua_State *lua_open (void) {
 }
 
 
-void luaE_closethread (lua_State *OL, lua_State *L) {
-  luaF_close(L, L->stack);  /* close all upvalues for this thread */
-  lua_assert(L->openupval == NULL);
-  L->previous->next = L->next;
-  L->next->previous = L->previous;
-  luaM_freearray(OL, L->base_ci, L->size_ci, CallInfo);
-  luaM_freearray(OL, L->stack, L->stacksize, TObject);
-  luaM_freelem(OL, L);
+void luaE_freethread (lua_State *L, lua_State *L1) {
+  luaF_close(L1, L1->stack);  /* close all upvalues for this thread */
+  lua_assert(L1->openupval == NULL);
+  luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
+  luaM_freearray(L, L1->stack, L1->stacksize, TObject);
+  luaM_free(L, cast(lu_byte *, L1) - EXTRASPACE,
+                sizeof(lua_State) + EXTRASPACE);
 }
 
 
@@ -160,24 +170,15 @@ static void close_state (lua_State *L) {
     luaZ_freebuffer(L, &G(L)->buff);
     luaM_freelem(NULL, L->l_G);
   }
-  luaE_closethread(NULL, L);
-}
-
-
-LUA_API void lua_closethread (lua_State *L, lua_State *thread) {
-  lua_lock(L);
-  if (L == thread) luaG_runerror(L, "cannot close only thread of a state");
-  luaE_closethread(L, thread);
-  lua_unlock(L);
+  luaE_freethread(NULL, L);
 }
 
 
 LUA_API void lua_close (lua_State *L) {
   lua_lock(L);
+  L = G(L)->mainthread;  /* only the main thread can be closed */
   luaC_callallgcTM(L);  /* call GC tag methods for all udata */
   lua_assert(G(L)->tmudata == NULL);
-  while (L->next != L)  /* then, close all other threads */
-    luaE_closethread(L, L->next);
   close_state(L);
 }
 

+ 20 - 11
lstate.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.h,v 1.97 2002/10/08 18:46:08 roberto Exp roberto $
+** $Id: lstate.h,v 1.98 2002/10/22 17:58:14 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -32,12 +32,6 @@
 #define lua_unlock(L)	((void) 0)
 #endif
 
-/*
-** macro to allow the inclusion of user information in Lua state
-*/
-#ifndef LUA_USERSTATE
-#define LUA_USERSTATE
-#endif
 
 #ifndef lua_userstateopen
 #define lua_userstateopen(l)
@@ -124,6 +118,7 @@ typedef struct global_State {
   lua_CFunction panic;  /* to be called in unprotected errors */
   TObject _registry;
   TObject _defaultmeta;
+  struct lua_State *mainthread;
   Node dummynode[1];  /* common node array for all empty tables */
   TString *tmname[TM_N];  /* array with tag-method names */
 } global_State;
@@ -133,7 +128,7 @@ typedef struct global_State {
 ** `per thread' state
 */
 struct lua_State {
-  LUA_USERSTATE
+  CommonHeader;
   StkId top;  /* first free slot in the stack */
   global_State *l_G;
   CallInfo *ci;  /* call info for current function */
@@ -150,15 +145,29 @@ struct lua_State {
   GCObject *openupval;  /* list of open upvalues in this stack */
   struct lua_longjmp *errorJmp;  /* current error recover point */
   ptrdiff_t errfunc;  /* current error handling function (stack index) */
-  lua_State *next;  /* circular double linked list of states */
-  lua_State *previous;
 };
 
 
 #define G(L)	(L->l_G)
 
 
-void luaE_closethread (lua_State *OL, lua_State *L);
+/*
+** Union of all collectable objects
+*/
+union GCObject {
+  GCheader gch;
+  union TString ts;
+  union Udata u;
+  union Closure cl;
+  struct Table h;
+  struct Proto p;
+  struct UpVal uv;
+  struct lua_State th;  /* thread */
+};
+
+
+lua_State *luaE_newthread (lua_State *L);
+void luaE_freethread (lua_State *L, lua_State *L1);
 
 #endif
 

+ 3 - 4
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 1.136 2002/10/22 17:18:28 roberto Exp roberto $
+** $Id: ltests.c,v 1.137 2002/10/22 18:07:55 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -403,7 +403,6 @@ static int doonnewstack (lua_State *L) {
   if (status == 0)
     status = lua_pcall(L1, 0, 0, 0);
   lua_pushnumber(L, status);
-  lua_closethread(L, L1);
   return 1;
 }
 
@@ -423,7 +422,7 @@ static int d2s (lua_State *L) {
 static int newstate (lua_State *L) {
   lua_State *L1 = lua_open();
   if (L1) {
-    *cast(int **, L1) = &islocked;  /* initialize the lock */
+    lua_userstateopen(L1);  /* init lock */
     lua_pushnumber(L, (unsigned long)L1);
   }
   else
@@ -724,7 +723,7 @@ static void fim (void) {
 
 
 int luaB_opentests (lua_State *L) {
-  *cast(int **, L) = &islocked;  /* init lock */
+  lua_userstateopen(L);  /* init lock */
   lua_state = L;  /* keep first state to be opened */
   luaL_opennamedlib(L, "T", tests_funcs, 0);
   atexit(fim);

+ 6 - 5
ltests.h

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.h,v 1.15 2002/07/17 16:25:13 roberto Exp roberto $
+** $Id: ltests.h,v 1.16 2002/10/08 18:45:07 roberto Exp roberto $
 ** Internal Header for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -42,10 +42,11 @@ void *debug_realloc (void *block, size_t oldsize, size_t size);
 
 /* test for lock/unlock */
 extern int islocked;
-#define LUA_USERSTATE	int *lock;
-#define lua_userstateopen(l) if (l != NULL) *cast(int **, l) = &islocked;
-#define lua_lock(L)     lua_assert((**cast(int **, L))++ == 0)
-#define lua_unlock(L)   lua_assert(--(**cast(int **, L)) == 0)
+#define LUA_USERSTATE	int *
+#define getlock(l)	(*(cast(int **, l) - 1))
+#define lua_userstateopen(l) if (l != NULL) getlock(l) = &islocked;
+#define lua_lock(l)     lua_assert((*getlock(l))++ == 0)
+#define lua_unlock(l)   lua_assert(--(*getlock(l)) == 0)
 
 
 int luaB_opentests (lua_State *L);

+ 2 - 2
ltm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltm.c,v 1.101 2002/08/30 19:09:21 roberto Exp roberto $
+** $Id: ltm.c,v 1.102 2002/09/19 20:12:47 roberto Exp roberto $
 ** Tag methods
 ** See Copyright Notice in lua.h
 */
@@ -19,7 +19,7 @@
 
 const char *const luaT_typenames[] = {
   "nil", "boolean", "userdata", "number",
-  "string", "table", "function", "userdata"
+  "string", "table", "function", "userdata", "thread"
 };
 
 

+ 3 - 2
lua.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lua.h,v 1.158 2002/10/22 17:18:28 roberto Exp roberto $
+** $Id: lua.h,v 1.159 2002/10/22 17:21:25 roberto Exp roberto $
 ** Lua - An Extensible Extension Language
 ** Tecgraf: Computer Graphics Technology Group, PUC-Rio, Brazil
 ** http://www.lua.org	mailto:[email protected]
@@ -70,6 +70,7 @@ typedef const char * (*lua_Chunkreader) (lua_State *L, void *ud, size_t *sz);
 #define LUA_TTABLE	5
 #define LUA_TFUNCTION	6
 #define LUA_TUSERDATA	7
+#define LUA_TTHREAD	8
 
 
 /* minimum Lua stack available to a C function */
@@ -104,7 +105,6 @@ typedef LUA_NUMBER lua_Number;
 LUA_API lua_State *lua_open (void);
 LUA_API void       lua_close (lua_State *L);
 LUA_API lua_State *lua_newthread (lua_State *L);
-LUA_API void       lua_closethread (lua_State *L, lua_State *t);
 
 LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);
 
@@ -142,6 +142,7 @@ LUA_API const char     *lua_tostring (lua_State *L, int idx);
 LUA_API size_t          lua_strlen (lua_State *L, int idx);
 LUA_API lua_CFunction   lua_tocfunction (lua_State *L, int idx);
 LUA_API void	       *lua_touserdata (lua_State *L, int idx);
+LUA_API lua_State      *lua_tothread (lua_State *L, int idx);
 LUA_API const void     *lua_topointer (lua_State *L, int idx);
 
 

+ 6 - 5
lvm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lvm.c,v 1.256 2002/09/19 20:12:47 roberto Exp roberto $
+** $Id: lvm.c,v 1.257 2002/10/08 18:46:08 roberto Exp roberto $
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -263,22 +263,23 @@ int luaV_equalval (lua_State *L, const TObject *t1, const TObject *t2) {
   switch (ttype(t1)) {
     case LUA_TNIL: return 1;
     case LUA_TNUMBER: return nvalue(t1) == nvalue(t2);
-    case LUA_TSTRING: return tsvalue(t1) == tsvalue(t2);
     case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2);  /* true must be 1 !! */
     case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
-    case LUA_TFUNCTION: return clvalue(t1) == clvalue(t2);
-    case LUA_TUSERDATA:
+    case LUA_TUSERDATA: {
       if (uvalue(t1) == uvalue(t2)) return 1;
       else if ((tm = fasttm(L, uvalue(t1)->uv.metatable, TM_EQ)) == NULL &&
                (tm = fasttm(L, uvalue(t2)->uv.metatable, TM_EQ)) == NULL)
         return 0;  /* no TM */
       else break;  /* will try TM */
-    case LUA_TTABLE:
+    }
+    case LUA_TTABLE: {
       if (hvalue(t1) == hvalue(t2)) return 1;
       else if ((tm = fasttm(L, hvalue(t1)->metatable, TM_EQ)) == NULL &&
                (tm = fasttm(L, hvalue(t2)->metatable, TM_EQ)) == NULL)
         return 0;  /* no TM */
       else break;  /* will try TM */
+    }
+    default: return gcvalue(t1) == gcvalue(t2);
   }
   callTMres(L, tm, t1, t2);  /* call TM */
   return !l_isfalse(L->top);