Designing a Nice Lua Implementation like Aardwolf's

Posted by Kasji on Wed 05 Sep 2012 07:33 AM — 26 posts, 111,811 views.

#0
I've been wanting to add Lua into SWR (a branch of Smaug) for years now, but after seeing what Aardwolf has done I think it's time to quit stalling.

I am not very good with Lua's C API as of yet, but I am getting better. The whole stack thing was confusing at first, but I think I've got a solid grasp of the mechanics now. However, I still don't know enough. I would like to write an implementation similar to Aardwolf's, but I am unsure of how to do something like:

name = ch.name
level = ch.level
...

Where it doesn't keep 2 copies of the data, but rather retrieves the data from C on the fly. That seems cleaner than Nick's original method of something like:

level = mud.getlevel(ch)
...

Initially I intend to use just one Lua state. So the other issue I'm up against is how to separate scripts properly, such as having say, two functions called "on_enter" for two different room vnums. The Aardwolf fellow mentioned something about using a map to track all the various scripts. Would that work with "luaL_loadbuffer"?

Also, just as an FYI, my intent is to use Lua for not only hooks in various places, but also as mob spec_funs, and localized commands attached to rooms, objects, mobs.

So I guess to get to the point: What C code is needed to allow you to do something like "level = ch.level"? Do you have any recommendations on tracking individual scripts? Is "luaL_loadbuffer" good for loading separate scripts stored in "const char *"? Though it might be better to just load files instead. Not sure.
USA Global Moderator #1
Quote:
how to do something like:

name = ch.name
level = ch.level
...

Where it doesn't keep 2 copies of the data

metatables?
Australia Forum Administrator #2
Indeed. Example here:

http://www.gammon.com.au/forum/?id=4904

That uses a table called var which uses metatables to both read to and write from MUSHclient variables without keeping two copies.
#3
Thanks guys. Would this be right for doing a metatable in C?

lua_createtable(L, 0, 0);
lua_createtable(L, 0, 1);
lua_pushcfunction(L, lua_chGetter);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "ch");

I am just guessing, but in "lua_chGetter", it would accept the "lua_State" variable as an argument, and you would have to get the property being accessed from the Lua stack?

My next question will be, what is the best way to know what character needs to be accessed? Should it be pushed onto the Lua stack as a userdata item, and included in the hook function's arguments? Such as this:

// Call function 'on_enter', pass 'ch' as argument.
lua_getfield(L, LUA_GLOBALSINDEX, "on_enter")
lua_pushlightuserdata(L, (void *) ch);
lua_call(L, 1, 1);

I don't think this is right since in this case ch wouldn't be a global? I am just not sure how to make Lua keep track of who ch is.
USA Global Moderator #4
Kasji said:
My next question will be, what is the best way to
In my experience, questions about "the best way" are always misguided.
#5
Well, 'best' may be subjective rather than objective.

Let me elaborate. What is a way to pass Lua information that will result in fewer headaches and less bloated code?

I am also concerned about how to keep track of what character/room/object/mob is involved in a script if I were to use co-routines. What would happen if the entity were destroyed while the co-routine is paused? If the metatable does not hold the information, but just the method of looking up the information, then the script could obviously raise an error.
USA Global Moderator #6
Quote:
fewer headaches and less bloated code
Than what?

Quote:
What would happen if the entity were destroyed while the co-routine is paused?
You only have a problem if you let it resume.

Quote:
If the metatable does not hold the information, but just the method of looking up the information
If it held the information rather than a method for looking up the information, then it would just be called a table.
Amended on Thu 06 Sep 2012 01:16 PM by Fiendish
#7
I think I've got how I want to do it. Rather than looking up a Lua function name to call as a hook, I am thinking I will do the name of the hook as a string separate from the script itself.
struct lua_prog_trigger
{
  char * hook;
  char * filename;
}
struct lua_prog_data
{
  char * filename;
  char * script;
  char * byte_code;

  ROOM_INDEX_DATA ** room;
  CHAR_DATA ** self;
  CHAR_DATA ** ch;
  OBJ_DATA ** obj;
};

So basically, lua_prog_trigger will be a list on rooms, objects, and mobs to be iterated over in the same way mobprogs does. lua_prog_data will be either stored as a list globally, or a list per area. I will probably do some indexing at load time to make fast access possible, or just use an STL container.

I will do something like this to open the mud to Lua:
prog->ch = (CHAR_DATA**)lua_newuserdata(L, sizeof(CHAR_DATA**));
(*prog->ch) = ch;
luaL_getmetatable(L, "ch");
lua_setmetatable(L, -2);


Checking that the involved entities still exist whenever there's a possibility of extraction. Paused co-routines, functions that destroy entities, etc.
Australia Forum Administrator #8
Kasji said:

What would happen if the entity were destroyed while the co-routine is paused? If the metatable does not hold the information, but just the method of looking up the information, then the script could obviously raise an error.


I think you have to pass some sort of identifier that remains valid. That is why, in my Lua implementation, I passed down things like a room vnum, or a character name. Now the vnum may not exist, in which case you raise an error, but it can't be "invalid" in the sense that a pointer might be if it used to point to something and now points to something else.
#9
Yeah, definitely want to avoid accessing invalid memory.

My knowledge of pointers isn't great, so can you tell me if this works? If you have a pointer to a pointer, when the object is deleted and (presumably) the pointer is set to NULL, if you dereference the pointer to the pointer, can you check to see if it's been set NULL, thus being able to know that the object has been deleted? Again, presuming the pointer has been set to NULL. Forgive me if I am completely wrong about how that works. I've never had a reason to use those ever.

I read a little bit about how David Haley did his system, and he seems to have implemented a fairly sophisticated memory management layer. I wonder, maybe smart pointers would work well for this kind of situation?

My other thought would be to kill off any running/paused script whenever a related entity is destroyed, though that might be pretty expensive. Not sure.

Your way definitely makes memory management easy, having a lua_State for each player, but due to my wanting to use Lua in the mud's main update loop, it probably isn't a good way to go.
Australia Forum Administrator #10
Kasji said:

My knowledge of pointers isn't great, so can you tell me if this works? If you have a pointer to a pointer, when the object is deleted and (presumably) the pointer is set to NULL, if you dereference the pointer to the pointer, can you check to see if it's been set NULL, thus being able to know that the object has been deleted?


Absolutely not. That doesn't happen.

The operator:


delete foo;


Does not alter the contents of foo. It now points to memory that has been freed and it is an error to dereference foo now.

You might do this:


delete foo;
foo = NULL;


But that doesn't help if you made a copy of foo previously (the copy won't be altered).
Australia Forum Administrator #11
It's a non-trivial problem. There is a thread about it here:

http://www.gammon.com.au/forum/?id=3079
#12
I could almost kiss you. Once I get these pieces put together, I will release the basic Lua implementation. It should be enough for others to follow along.
USA Global Moderator #13
Nick Gammon said:

It's a non-trivial problem. There is a thread about it here:

http://www.gammon.com.au/forum/?id=3079


Does that just implement C++11's weak_ptr?
#14
I've implemented your solution on my CHAR_DATA struct so far. It's working like a charm.

File: objectmap.c
#include <stdio.h>
#include <string.h>
#include "mud.h"

extern tPointerOwner<CHAR_DATA> char_map;

template <class ITER, class F>
F safe_for_each_ch (CHAR_DATA * ch, ITER first, ITER last, F func)
  {
  while (first != last)
    func (ch, *first++);
  return func;
  }

void list_char_map(CHAR_DATA * ch, const std::pair <tId<CHAR_DATA>,CHAR_DATA*> item)
{
   if (item.second->pIndexData)
      pager_printf(ch, "&g| &W%-10d &g|| &W%-30s &g|| &W%-10d &g|\n\r", item.first.Value(), item.second->name, item.second->pIndexData->vnum);
   else
      pager_printf(ch, "&g| &W%-10d &g|| &W%-30s &g|| &W%-10s &g|\n\r", item.first.Value(), item.second->name, " ");
   return;
}

void do_objectmaps(CHAR_DATA * ch, const char * argument)
{
   pager_printf(ch, "&g##############################################################\n\r");
   pager_printf(ch, "&g|     &WID     &g||              &WName              &g||    &WVnum    &g|\n\r");
   pager_printf(ch, "&g##############################################################\n\r");
   safe_for_each_ch(ch, char_map.begin(), char_map.end(), list_char_map);
   pager_printf(ch, "&g##############################################################\n\r");
   return;
}


The command do_objectmaps yields output like so:
##############################################################
|     ID     ##              Name              ##    Vnum    |
##############################################################
| 1          || The Coruscant Mall             || 3          |
| 2          || Puff                           || 1          |
| 3          || supermob                       || 3          |
| 4          || supermob                       || 3          |
| 5          || supermob                       || 3          |
| 6          || supermob                       || 3          |
| 7          || demon imp                      || 2          |
| 8          || demon imp                      || 2          |
| 9          || demon imp                      || 2          |
| 10         || demon imp                      || 2          |
| 11         || Grotans                        || 131        |
| 12         || Protocol Droid                 || 126        |
| 13         || General Rock                   || 116        |
| 14         || Lowbacca                       || 117        |
| 15         || An Ewok                        || 118        |
| 16         || Engineer                       || 127        |
| 17         || Engineer                       || 128        |
| 18         || Drall                          || 120        |
| 19         || Dorsk                          || 119        |
| 20         || Commander Targ                 || 121        |
| 21         || Contraband Weapons Dealer      || 122        |
(C)ontinue, (R)efresh, (B)ack, (Q)uit: [C] 

| 22         || Barlan                         || 123        |
| 23         || Molbakka                       || 124        |
| 24         || Dorak                          || 129        |
| 25         || store manager                  || 132        |
| 26         || Colonel Jentric                || 150        |
| 27         || Admiral Coran                  || 125        |
| 28         || astromech droid r2             || 209        |
| 29         || astromech droid r2             || 208        |
| 30         || technician                     || 206        |
| 31         || technician                     || 206        |
| 32         || technician                     || 206        |
| 33         || technician                     || 206        |
| 34         || spaceport security guard       || 204        |
| 35         || spaceport security guard       || 204        |
| 36         || begger                         || 210        |
| 37         || begger                         || 210        |
| 38         || human                          || 207        |
| 39         || human                          || 207        |
| 40         || spaceport security guard       || 203        |
| 41         || storekeeper hopkeeper droid    || 201        |
| 42         || toll droid ticket token        || 200        |
| 43         || HG Guard                       || 228        |
| 44         || hunters guild trooper          || 236        |
| 45         || Ammuntion Dispenser            || 230        |
(C)ontinue, (R)efresh, (B)ack, (Q)uit: [C] 

...


Now I just need to add checks into the Lua interface.
Australia Forum Administrator #15
Fiendish said:

Does that just implement C++11's weak_ptr?


No, not as such. It was written (August 2003) before I had heard of C++11.

It uses a STL map to hold a integer-to-pointer conversion, where each pointer is assigned an incrementing "pointer ID".

Deleting the pointer removes it from the map, so if you then attempt to get it, you get back NULL.

The idea is you store this ID (where you would normally store a pointer) and convert it back at the last moment. The IDs are not re-used, and being a 64-bit number you can use a lot of them before you run out.

I don't necessarily know if this is the best solution, as I say, the general problem is non-trivial.
USA Global Moderator #16
Quote:
No, not as such. It was written (August 2003) before I had heard of C++11.
well, yes, I suspect 2003 was a long time before 2011. :D
But I think this (non-owning null-verifying pointers) can be accomplished by weak pointers now with modern c++.
#17
Well I've got command hook setup inside of interpret(), and all is well. I am using stat() and S_ISDIR() to verify the file exists and is not a directory, then interpret() calls my function lua_command().

Relevant parts from my lua.c file:
extern "C" int luaCHSend(lua_State * L);

static const luaL_reg luaMudlib[] =
{
        {"send_to_char", luaCHSend},
        {NULL, NULL}
};

extern "C" int luaRegisterMudlib(lua_State * L)
{
        int i = 0;
        while (luaMudlib[i].name != NULL && luaMudlib[i].func != NULL)
        {
                lua_register(L, luaMudlib[i].name, luaMudlib[i].func);
                i++;
        }
        return RET_OK;
}

int luaOpen()
{
        // Create a new Lua state.
        g_luaState = luaL_newstate();
        if (!g_luaState)
        {
                log_lua("luaOpen: Failed to create lua_State!");
                return RET_ERR;
        }
        // Load standard libraries.
        luaL_openlibs(g_luaState);
        // Load mud library.
        lua_pushcfunction(g_luaState, luaRegisterMudlib);
        lua_call(g_luaState, 0, 0);
// None of these methods work?
//      luaRegisterMudlib(g_luaState);
//      luaL_register(g_luaState, "mud", luaMudlib);
        // Load mud interfaces.
//      luaRegisterCH(g_luaState);
//      luaRegisterRoom(g_luaState);
//      luaRegisterObj(g_luaState);

        lua_settop(g_luaState, 0);

        return RET_OK;
}

void lua_command(CHAR_DATA * ch, const char * command, const char * argument)
{
   char path[MAX_INPUT_LENGTH];
   char func[MAX_INPUT_LENGTH];
   int err;

   if (!g_luaState)
   {
      log_lua("lua_command: FATAL error: g_luaState == NULL!");
      return;
   }

   snprintf(path, MAX_INPUT_LENGTH, "%sdo_%s.lua", LUA_CMD_DIR, command);
   if ((err = luaL_loadfile(g_luaState, path)) != 0)
   {
      log_lua("lua_command: luaL_loadfile() error %d: %s", err, lua_tostring(g_luaState, -1));
      return;
   }

   snprintf(func, MAX_INPUT_LENGTH, "do_%s", command);
   lua_getglobal(g_luaState, func); // Get name of Lua func
   lua_pushlightuserdata(g_luaState, (void*)ch->GetId().Value()); // Push character ID
   lua_pushstring(g_luaState, argument); // Push argument
   if ((err = lua_pcall(g_luaState, 3, 0, 0)) != 0)
   {
      log_lua("lua_command: lua_pcall() error: %d: %s", err, lua_tostring(g_luaState, -1));
      return;
   }

   return;
}

And lua_funs.c:
extern "C" int luaCHSend(lua_State * L)
{
        CHAR_DATA * ch;
        const char * arg;
        char buf[MAX_STRING_LENGTH];
        uint64 id;
bug("luaCHSend"); // Just to see if we ever make it here, which we aren't...
        if (!L)
        {
                log_lua("luaCHSend: FATAL error: L == NULL");
                return 0;
        }

        if (lua_gettop(L) != 2)
        {
                log_lua("luaCHSend: Invalid number of arguments.");
                luaL_error(L, "luaCHSend: Invalid number of arguments.");
                return 0;
        }

        id = (uint64) lua_touserdata(L, -2);
        ch = char_map[id];
        if (ch == NULL)
        {
                log_lua("luaCHSend: NULL ch.");
                luaL_error(L, "luaCHSend: NULL ch.");
                return 0;
        }

        if (IS_NPC(ch))
                return 0;

        arg = lua_tostring(L, -1);
        if (arg == NULL)
        {
                log_lua("luaCHSend: NULL argument.");
                luaL_error(L, "luaCHSend: NULL argument.");
                return 0;
        }

        snprintf(buf, MAX_STRING_LENGTH, "%s\n\r", arg);

        send_to_char(buf, ch);

        return 0;
}

Continued in next post...
Amended on Wed 12 Sep 2012 09:09 PM by Kasji
#18
And here is ../lua/commands/do_lua_test.lua:
--- do_lua_test, function to test the lua implementation

function do_lua_test(ch, argument)
    --- NEITHER of these don't get called. At least one should be?
    mud.send(ch, "This is a test!")
    send(ch, "Another test!")
    return
end

It never complains about anything. It executes do_lua_test I am guessing because lua_pcall() never complains, but nothing ever shows up and luaCHSend() is never reached. And of course, I don't expect both mud.send() and send() to work, but one of them should, depending on if lua_register() or luaL_register() is used.

As you can see from the comments in luaOpen(), I've tried different ways of making it work, but nothing does work.

From Nick's code, there is this:
static int RegisterLuaRoutines (lua_State *L)
  {

  lua_newtable (L);  /* environment */
  lua_replace (L, LUA_ENVIRONINDEX);

  /* this makes environment variable "character.state" by the pointer to our character */
  lua_settable(L, LUA_ENVIRONINDEX);

  /* register all mud.xxx routines */
  luaL_register (L, MUD_LIBRARY, mudlib);
  
  /* using interpret now
  RegisterLuaCommands (L);
  */

  luaopen_bits (L);     /* bit manipulation */

  return 0;
  
}  /* end of RegisterLuaRoutines */

void open_lua  ( CHAR_DATA * ch)
  {
  lua_State *L = luaL_newstate ();   /* opens Lua */
  ch->L = L;
  
  if (ch->L == NULL)
    {
    fprintf( stderr, "Cannot open Lua state\n");
    return;  /* catastrophic failure */
    }

  luaL_openlibs (L);    /* open all standard libraries */

  /* call as Lua function because we need the environment  */
  lua_pushcfunction(L, RegisterLuaRoutines);
  lua_pushstring(L, CHARACTER_STATE);  /* push address */
  lua_pushlightuserdata(L, (void *)ch);    /* push value */
  lua_call(L, 2, 0);
 
  /* run initialiation script */
  if (luaL_loadfile (L, LUA_STARTUP) ||
      CallLuaWithTraceBack (L, 0, 0))
      {
      const char * sError = lua_tostring(L, -1);
      
      fprintf( stderr, "Error loading Lua startup file:\n %s\n", 
              sError);
      
      if (IS_IMMORTAL(ch))
        {
        set_char_color( AT_YELLOW, ch );
        ch_printf (ch, "Error loading Lua startup file:\n %s\n", 
                  sError); 
        }  /* end of immortal */

      }

  lua_settop (L, 0);    /* get rid of stuff lying around */
        
  }  /* end of open_lua */


There are only two big differences between Nick's code and mine that I have noticed.
1) He's got some table stuff going on with LUA_ENVIRONINDEX.
2) He executes a startup script to bind his mudlib functions to global functions.

I'd appreciate any help with this. Also, what is the LUA_ENVIRONINDEX stuff going on here? I don't quite understand it.
Australia Forum Administrator #19
Template:codetag
To make your code more readable please use [code] tags as described here.


In particular the bit about "Convert Clipboard Forum Codes".
Amended on Sun 09 Sep 2012 08:53 PM by Nick Gammon
#20
Nice, thanks.

I found one error in my code.
   if ((err = lua_pcall(g_luaState, 3, 0, 0)) != 0)

Should be this:
   if ((err = lua_pcall(g_luaState, 2, 0, 0)) != 0)

Interestingly, this raises an error message now:
Lua: lua_command: lua_pcall() error: 2: attempt to call a nil value
   Path: ../lua/commands/do_lua_test.lua

At least, I think that's the way it should be? Based on the reference manual...
#21
I added a new check to lua_command(), and found the problem. Or at least closer to it.
   snprintf(func, MAX_INPUT_LENGTH, "do_%s", command);
   lua_getglobal(g_luaState, func);
   if (!lua_isfunction(g_luaState, -1))
   {
      lua_pop(g_luaState, -1);
      log_lua("lua_command: Function not found: %s", func);
      return;
   }

It catches on this. As you can see in the lua file above, the function is there. What am I missing...?
#22
Ok, I changed
   if ((err = luaL_loadfile(g_luaState, path)) != 0)
   {
      log_lua("lua_command: luaL_loadfile() error %d: %s\n\r   Path: %s", err, lua_tostring(g_luaState, -1), path);
      return;
   }

To this:
   if ((err = luaL_dofile(g_luaState, path)) != 0)
   {
      log_lua("lua_command: luaL_dofile() error %d: %s\n\r   Path: %s", err, lua_tostring(g_luaState, -1), path);
      return;
   }

Now I'm getting this error:
Lua: lua_command: lua_pcall() error: 2: ../lua/commands/do_lua_test.lua:5: attempt to call global 'send' (a nil value)
   Path: ../lua/commands/do_lua_test.lua

So I'm getting the original issue of luaCHSend() not being registered I guess.
#23
Problem resolved. Just a bit of silliness on my part I guess. The original problem was not that luaCHSend() wasn't being called, but that do_lua_test() wasn't being called because the lua script has to not only be loaded, but also executed, in order to call individual functions in the script. While stumbling around in the dark with the problem, I happened to change luaCHSend()'s binding from send() to send_to_char(), but never updated the script, so I fixed the issue with do_lua_test() not being found, but created a new problem by changing the binding. All is well now. Lua is now working within the MUD, and once I add in a few more mud functions for Lua, and finish the basic metatables for ch (victim), self (mob), room, and obj, I will release my implementation.

This will require updating CHAR_DATA, ROOM_INDEX_DATA, and OBJ_DATA with the tObject<> class, and will also require the aforementioned structs to have constructors/destructors. CREATE() and DISPOSE() operations on the structs have to be replaced with new/delete operators. From there, all metamethods and functions callable from Lua only possess the ID of the entity, and have to look it up in the appropriate map.

I've already done this with CHAR_DATA, so I am one third done there.

Things are progressing nicely now. :)
#24
Alright, looks like things are pretty good, here's my test script:
-- do_lua_test, function to test the lua implementation

function send(char, ...)
    send_to_char(char.id, table.concat {...} .. "\n\r")
   return
end -- send

function do_lua_test(ch, argument)
   if argument then
      send(ch, "&RYou typed: " .. argument)
   end
   send(ch, "&GYour name is " .. ch.name .. ", and you are a " .. ch.gender .. " " .. ch.race .. ".")
   och = get_char_world(ch, "tech")
   if och then
      send(ch, "&YWe found: " .. och.name)
   end
   ch:say("Testing")
   return
end -- do_lua_test


And here is the output:
lua_test Some arg
You typed: Some arg
Your name is Kasji, and you are a male Noghri.
We found: a skittish verpine tech
You say, "Testing"


I'm ready to release this implementation as is. I am implementing this with other code (eg a custom combat system) and I don't want to release something full of stuff that doesn't apply to everyone else's codebases. Does anyone know of a place I can upload the code?
#25
I went ahead and reworked my system. I made a C++ wrapper class for coroutines, and I implemented a sleep function for Lua. I created a separate class for an updater that includes as well, a way to create coroutines that are then added to the update list. It's working well with one exception. I can create coroutines very quickly and it's fine, but if I spam creating them, say create 10 in one second, the code crashes. I don't really understand why it crashes, but it's crashing on lua_resume().

lua_coroutine.c:
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <setjmp.h>
#include "mud.h"
#include "lua.h"

extern lua_State * g_luaState;
extern jmp_buf g_luaEnv;
extern tPointerOwner<L_COROUTINE> coroutine_map;

DECLARE_LUA_FUN(luaPanic);

lua_coroutine::lua_coroutine(const char * type, uint64 id) : tObject<L_COROUTINE>(coroutine_map)
{
   clock_gettime(CLOCK_MONOTONIC, &(this->m_created));
   snprintf(this->m_owner_type, 10, "%s", type);
   this->m_owner_id = id;
   if (g_luaState)
   {
      this->m_L = lua_newthread(g_luaState);
      lua_atpanic(this->m_L, luaPanic);
   }
   else
      this->m_L = NULL;
   this->m_state = 0;
   this->m_started = FALSE;
   return;
}

lua_coroutine::~lua_coroutine()
{
}

int lua_coroutine::run(const char * funcname, int nargs)
{
   int jmp;

   jmp = setjmp(g_luaEnv);
   if (jmp == 1)
   {
      log_lua("Long jump successful.");
      return 0;
   }

   snprintf(this->m_func, 32, "%s", funcname);

   lua_getglobal(this->m_L, funcname);
   if (!lua_isfunction(this->m_L, -1))
   {
      log_lua("lua_coroutine::run: Function not found: %s", funcname);
//      lua_settop(g_luaState, 0);
      return 1;
   }
   if (nargs > 0)
      lua_insert(this->m_L, (-1 - nargs));

   this->m_state = lua_resume(this->m_L, nargs);
   this->m_started = TRUE;
   return this->m_state;
}

int lua_coroutine::resume()
{
   int jmp;

   if (this->m_started == FALSE || this->m_state != LUA_YIELD)
      return 0;

   jmp = setjmp(g_luaEnv);
   if (jmp == 1)
   {
      log_lua("Long jump successful.");
      return 0;
   }

   // If coroutine.yield returned any values, they need to be popped.
// while (lua_gettop(this->m_L) > (1 + this->m_nargs))
//    lua_pop(this->m_L, -1);

   this->m_state = lua_resume(this->m_L, 0);
   return this->m_state;
}

int lua_coroutine::status()
{
   if (this->m_L)
      this->m_state = lua_status(this->m_L);
   return this->m_state;
}

bool lua_coroutine::started()
{
   return this->m_started;
}

const timespec& lua_coroutine::created()
{
   return this->m_created;
}

uint64 lua_coroutine::owner_id()
{
   return this->m_owner_id;
}

const char * lua_coroutine::owner_type()
{
   return this->m_owner_type;
}

lua_State * lua_coroutine::L()
{
   return this->m_L;
}

const char * lua_coroutine::func()
{
   return this->m_func;
}

bool lua_coroutine::operator==(const lua_coroutine &rhs)
{
   return this->GetId().Value() == rhs.GetId().Value();
}


lua_update.c:
         {
            msg = lua_tostring(cor->L(), -1);
            log_lua("lua_updater::update(): %d: %s\n\r    Owner Type: %s  Owner ID: %llu", status, msg, cor->owner_type(), cor->owner_id());
         }
         it = this->killthread(it);
         continue;
      }
   }

   return;
}

L_COROUTINE * lua_updater::newthread(const char * type, uint64 id)
{
   L_COROUTINE * cor = new L_COROUTINE(type, id);
   this->m_coroutine_list.push_back(cor->GetId().Value());

   return cor;
}

void lua_updater::killthread(uint64 id)
{
   delete coroutine_map[id];
   this->m_coroutine_list.remove(id);
   return;
}

L_COR_IT lua_updater::killthread(L_COR_IT it)
{
   delete coroutine_map[(*it)];
   return this->m_coroutine_list.erase(it);
}

void lua_update()
{
   g_luaUpdater->update();

   return;
}


Here's a copy of the stack from a core dump:
#0  0x00002b935acbd999 in ?? () from /usr/lib64/liblua-5.1.so
#1  0x00002b935acbd137 in ?? () from /usr/lib64/liblua-5.1.so
#2  0x00002b935acbd31a in lua_resume () from /usr/lib64/liblua-5.1.so
#3  0x000000000060faa0 in lua_coroutine::resume (this=0xd0ff800) at lua_coroutine.c:84
#4  0x0000000000619dfe in lua_updater::update (this=0xcf19740) at lua_update.c:37
#5  0x000000000061a093 in lua_update () at lua_update.c:84
#6  0x000000000060c0cc in update_handler () at update.c:2775
#7  0x00000000004d18bb in game_loop () at comm.c:580
#8  0x00000000004d0912 in main (argc=5, argv=0x7fff6861d488) at comm.c:252


Edit:
Here's the function I use to yield scripts in Lua (mud:time() is basically just (tv_sec + (tv_usec/1000000)) from gettimeofday()):
function sleep(ch, seconds)
   local start = mud:time()

   while mud:time() - start < seconds do
      coroutine.yield()
   end
   return
end -- sleep