lua-users home
lua-l archive

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



I did this years back for SDL Audio callbacks, worked like this:

- callbacks (C side) push data onto a C-side event queue
- Lua side repeatedly (did this as part of the SDL event loop Lua binding) checks whether that queue has anything and
- calls Lua side callback for each event

I found this working, adequate, and safe. The locking solution might work, but do you really want to pend in a C-side callback (until the lock would be released)? Depends on the library you're using if this would cause problems.

Another solution is to use Lanes or some other genuine multithreading solution.

-asko



John Dunn kirjoitti 4.4.2009 kello 21:49:

I'm attempting to write an asynchronous library that uses callbacks. From script, the callbacks look like

foo = Async.New()

function asyncCallback( async, data )
  print( "optional data is "..data )
  result, e = async:EndCall()
end

foo:BeginCall(asyncCallback, "optional test data")

Internally, I'll have a global table which will hold lightuserdata:table. The lightuserdata is a pointer to an internal structure and it's value is a table which contains the Async object, the callback function and the data argument. When the C++ function calls back, it looks up the lightuserdata in the table and calls the function, passing the object and the data as parameters.

Ignoring some big holes ( like I'll need to protect the lua_State when the callback comes back on another thread ) is there anything fundamentally wrong with the concept, or the following code?

const char* AsyncClass = "Async";
const char* AsyncInfoTable = "AsyncInfoTable";
const char* AsyncField = "Async";
const char* FunctionField = "Function";
const char* DataField = "Data";

class LuaAsync;

struct AsyncInfo
{
  lua_State* L;
};

class LuaAsync
{
protected:
  void CallAsync(AsyncInfo* info)
  {
    // this would actually do something here and get called back
    lua_getglobal(info->L, AsyncInfoTable);
    lua_pushlightuserdata(info->L,info);
    lua_gettable(info->L,-2);
    // remove our userdata entry from the table
    lua_pushlightuserdata(info->L,info);
    lua_pushnil(info->L);
    lua_settable(info->L,-3);
// get the function and call it, passing in the table as the argument
    lua_getfield(info->L,-1,FunctionField);
    lua_getfield(info->L,-2,AsyncField);
    lua_getfield(info->L,-3,DataField);
    if( lua_pcall(info->L,2,0,0) )
    {
      const char* msg = lua_tostring(info->L, -1);
      lua_pop(info->L, 1);
      if( msg ) std::cout << "error : " << msg << std::endl;
      else std::cout << "error : NULL"<< std::endl;
    }
  }
public:
  int BeginCall( lua_State* L )
  {
    AsyncInfo* info = new AsyncInfo();
    info->L = L;
    lua_getglobal(L, AsyncInfoTable);
    // key is light user data
    lua_pushlightuserdata(L,info);
    // info table is
    lua_newtable(L);
    // function
    lua_pushvalue(L,2);
    lua_setfield(L,-2,FunctionField);
    // async
    lua_pushvalue(L,1);
    lua_setfield(L,-2,AsyncField);
    // data
    lua_pushvalue(L,3);
    lua_setfield(L,-2,DataField);
    lua_settable(L,-3);
    CallAsync(info);
    return 1;
  }

  int EndCall( lua_State* L )
  {
// this would return a result or data, for right now return nothing...
    return 0;
  }
};