lua-users home
lua-l archive

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


--- In lua-l@egroups.com, "Russell Y. Webb" <rw20@c...> wrote:
> Here's what you can do.  Install all your tags, files, and objects 
through
> wrapper functions that record what is being installed as strings or
> filenames then the original thread can spawn another and pass it a 
string
> or filename to initialize itself.  Putting all the initialization 
into one
> file and just telling the new thread (state) to call dofile on it 
would
> seem to be cleanest.
> 
> Just a quick idea which is likely to have some flaws.
> 
> Russ

I have been using an approach much like the one Russ suggested 
to support calling thread functions. I require that the module
starting a thread contain a global variable 'main' with value
function. I modified lua.c to stash the name of the file it
executes, and to call the main() function if it exists.
When the user starts a thread with 'call_thread( func, arg_tbl )', 
I call lua_dofile() on the stashed filename to set up all 
of the global state, and then I invoke the function stored in 
global variable 'func'.

An example: file thread.lua [[
gbl = thread_global();
function foo( args )
    for i = 1,50 do
        print( args.text, gbl.n );
        gbl.n = gbl.n + 1;
    end
end 

function main( args )
    thread_call( foo, { text="hello" } );
    thread_call( foo, { text="goodbye" } );
end
]]

This sets up shared-nothing states. I use two mechanisms to share
data between states (both based on C transfer).
1.  I copy the 'args' table into the new state, and 
    pass it to the called function.
2.  I implemented a userdata that has gettable and settable
    tag methods; these look up the field in a hash table.
    To create the userdata, the app calls 
    thread_global( name ). The name (default nil) indicates which
    global table the threads would like to share. The global
    tables can only store strings and numbers. I think it can
    be extended easily to user data (provided the tags are created
    in all threads by global initialization). Storing user data
    would let you store other globals, which gives you something
    like global table semantics (just slightly harder to set up;
    maybe there's a nice way to do it though...haven't thought about
    it much).

I thought that this approach would be pretty cheesy, but it
seems fairly robust (at least for the way I use lua).
It gives clean, shared-nothing semantics by default, but
lets threads share common data explicitly.  It would be fairly
easy to break if a lua app changes the values of global
variables containing function values.  By the way, I control
access to 'dangerous' builtins (such as print)
by replacing them with functions that grab a mutex then 
call the original.

The main reason for choosing this approach is that I didn't need
to hit any of the lua source code (except for adding some
hooks to lua.c to store file name before doing a file).
I think this is important since 4.0 is only in Alpha right now.

The idea of copying tag methods and values to the new state
did occur to me, but I decided it is better to stick with 
requiring the lua file to do all that setup statically, and
then do it once in each thread.  I still need to extend
my approach to allow threads to be initiated from a lua_dobuffer()
call; unfortunately, I think I'll need to modify the lua
source slightly to do this (unless hooks for lua_dofile, 
lua_dostring and lua_douffer are added to the main distribution).

Overall, I'm very happy with lua; it lets me write simple,
multi-threaded programs safely. If I spend enough thought
implementing the C code, I don't need to worry very much 
about concurrency in the lua code.  To me, that's marvellous.

Regards,
- Ivan Bowman
http://plg.uwaterloo.ca/~itbowman