lua-users home
lua-l archive

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




On Wed, Dec 2, 2009 at 1:04 PM, Jim Pryor <lists+lua@jimpryor.net> wrote:
On Wed, Dec 02, 2009 at 11:03:39AM -0800, Chris Gagnon wrote:
> Â ÂOn Wed, Dec 2, 2009 at 10:16 AM, Jim Pryor <[1]lists+lua@jimpryor.net>
> Â Âwrote:
>
> Â Â On Wed, Dec 02, 2009 at 09:33:47AM -0800, Chris Gagnon wrote:
>
> Â Â > M = thread/co routine manager
> Â Â > S = some system that has registered a C interface for lua scriptsto use
> Â Â > This may yield, i'll use the specific example of a system that
> Â Â > impliments a Delay(fTime) function for scripts to use.
> Â Â > L = a co routine
> Â Â > M creates L and could create MyStateWrapper then starts L
> Â Â >
> Â Â > L calls Delay(1.0) S gets a call to its registered function of signature
> Â Â > int function(lua_State *L) and yields L
> Â Â > ...1 second later...
> Â Â > S wants to Resume L however the resume requires MyStateWrapper so that is
> Â Â > can properly clean up if the L ends or errors.
> Â Â > Now i must look up in a map that associates L with MyStateWrapper.
>
> Â Â Are you using pre-emptive threads? If so, are you using one of the
> Â Â existing Lua frameworks for this or have you rolled your own?
>
> Â ÂThis is a completely homegrown framework, using pretty much vanilla Lua.
> Â ÂCompletely cooperative threading, I then to use "thread" and "coroutine"
> Â Âinterchangeably which probably doesn't help when I'm explaining things,
> Â Âbut I blame Lua itself for this since it's confused as to what it wants to
> Â Âcall them ;o)
>
> Â Â If you're only using cooperative threads, then would the model be that S
> Â Â yields the thread that called it back to a parent Lua thread, such as M,
> Â Â and that later someone (looks from this model like it should be M, but
> Â Â that doesn't sound like what you have in mind) decides that the
> Â Â suspended thread should be resumed. So other cooperative Lua threads can
> Â Â be run in the meantime. In that case are you using some patch (RVM?
> Â Â Coco? to enable you to yield across the C-call boundary?
>
> Â Â Or is the model that S suspends the whole Lua universe for one second
> Â Â (other Lua universes rooted in other parent lua_States can run, but the
> Â Â two universes wouldn't share any memory). In that case you wouldn't need
> Â Â to be doing any yielding inside the Lua universe that's being suspended,
> Â Â you just don't return from the C-call to S until you're ready to go on.
>
> Â ÂWhen any System( S )ï Yields it only Yields the calling L.
> Â ÂAt anytime there could be hundreds of L's yielded waiting on things like
> Â Âdelays, loads or others things each implemented by a different system.
> Â ÂEach of these systems manage when they should resume any L that they
> Â Âyielded.
>
> Â ÂSo there are multiple entry points for resuming, as well as the entry
> Â Âpoints for creating new L's. ( new L's are created by M)
> Â ÂAll resumes go through the M->Resume(L) such that there isn't any code
> Â Âduplication for the resume and cleanup code but once again somehow i need
> Â Âto get at MyStateWrapper to be able to cleanup.


It's still opaque to me. Can you write down the simplest possible prototype?

I'm assuming something like the following is involved, though in C
instead, and you're relying on the built-in luaL_ref, luaL_unref...

ThreadTable = {[0]=0}

function luaL_ref(tbl, val)
 Âlocal ref = tbl[0]
 Âif ref == 0 then
       Â-- no free slots, grow table
   Âref = #tbl + 1
 Âelse
   Â-- use free slot, move free slot it pointed to to tbl[0]
   Âtbl[0] = tbl[ref]
 Âend
 Âtbl[ref] = val
 Âreturn ref
end

function luaL_unref(tbl, ref)
 Âlocal val = tbl[ref]
   Â-- mark ref as first free slot
   Âtbl[ref],tbl[0] = tbl[0],ref
   Âreturn val
end

function makethread(funct, args)
 Âlocal L = coroutine.create(funct)
 Âlocal ref = luaL_ref(ThreadTable, L)
 Âreturn ref, coroutine.resume(L, args)
end

local function L1(args)
 Âprint("goodnight",args)
 Âlocal retval = call_c_sleep_function(10)
 Âprint("good morning", args)
end

local function L2(args)
 Âprint("goodnight",args)
 Âlocal retval = call_a_different_c_sleep_function(10)
 Âprint("good morning", args)
end

local ok, retval1, retval2, ref1, ref2

ref1, ok, retval1 = makethread(L1, 100)
if ok then
 Âref2, ok, retval2 = makethread(L2, 200)
 Âif ok then
   Â-- use retval1 to decide when it's ok to resume thread1 again
   Â-- and whether to resume thread1 before thread2, etc.
   Âok, retval1 = coroutine.resume(ThreadTable[ref1])
   Âif ok then ...


Is that the flow control? If it's cooperative threading then there's
some kind of round-robin manager that the c functions are yielding to,
and it has to decide, based on the retvalues they yield, when to resume
thread1, whether to resume thread1 before thread2, and so on. This is
also the manager that decides when the threads have crashed or finished,
and when to make them GC-able. Right? Does that represent your basic
flow control?

--

Your very close I'm just going to go ahead and explain it in code because the problem really is an implementation one.

LuaManager::StartCoroutine(char * func)
{
ÂÂÂ ÂÂÂ lua_getfield(MainState, LUA_GLOBALSINDEX, "ThreadTable");
ÂÂÂ ÂÂÂ lua_State *thread = lua_newthread(MainState);
ÂÂÂ ÂÂÂ int refID = luaL_ref(MainState, -2);
ÂÂÂ ÂÂ
ÂÂÂ ÂÂÂ // start func on the new thread some setup removed for brevity
ÂÂÂ ÂÂÂ int iRet = lua_resume(m_pLuaState, n);
ÂÂÂ ÂÂÂ if (iRet == 0)
ÂÂÂ ÂÂÂ {
ÂÂÂ ÂÂÂ ÂÂÂ // clean up i have refID in this case all is good
ÂÂÂ ÂÂÂ }ÂÂÂ ÂÂÂ
ÂÂÂ ÂÂÂ else if (iRet == LUA_YIELD)
ÂÂÂ ÂÂÂ {
ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ -> i have a refID what do i do with it?
ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ ->ÂÂ Attempting to avoid creating a map because I feel like I should be able to pack it with thread.
ÂÂÂ ÂÂÂ }
ÂÂÂ ÂÂÂ else
ÂÂÂ ÂÂÂ {
ÂÂÂ ÂÂÂ ÂÂÂ // error
ÂÂÂ ÂÂÂ }
}

int SleepSystem::call_c_sleep_function(lua_State * L)
{
ÂÂÂ ÂÂÂ // save off L and an associated time
ÂÂÂ ÂÂÂ return lua_yield(L);
}

int SleepSystem::process(...)
{
ÂÂÂ ÂÂÂ // realizes that L should be resumes since it's sleep time is up
ÂÂÂ ÂÂÂ int iRet = lua_resume(L);
ÂÂÂ ÂÂÂ if (iRet == 0)
ÂÂÂ ÂÂÂ {
ÂÂÂ ÂÂÂ ÂÂÂ // clean up i have refID in this case all is good
ÂÂÂ ÂÂÂ }ÂÂÂ ÂÂÂ
ÂÂÂ ÂÂÂ else if (iRet == LUA_YIELD)
ÂÂÂ ÂÂÂ {
ÂÂÂ ÂÂÂ ÂÂÂ ÂÂÂ -> i cannot clean up because i don't have refID to do luaL_unref(...)
ÂÂÂ ÂÂÂ }
ÂÂÂ ÂÂÂ else
ÂÂÂ ÂÂÂ {
ÂÂÂ ÂÂÂ ÂÂÂ // error
ÂÂÂ ÂÂÂ }
}

- Chris