safely buffering Lua data between (system) threads & lua_States?

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

safely buffering Lua data between (system) threads & lua_States?

Graham Wakefield
Hi,

I have a multi-threaded application, with Lua code executing in a main (system) thread. I'd like to be able to interact with another (system) thread, ideally with its own Lua state, all in shared memory. Thread2's lua_State * has a flag to indicate which thread current owns (may interact with) it. When any thread finds that the lua_State * is assigned to it, it calls functions / adds values etc. to it, then switches ownership of the lua_State * to the other thread; all fine so and thread-safe so far I think.

Unfortunately, this means that there are times at which the main thread can't access thread2, which I would like to avoid. I had the idea to 'double buffer' the lua_State *; that is, to have two input lua_State *s for thread2, and alternately swap which ones are assigned to the main thread; then in thread2 copy items from whichever of these input lua_States is locked into thread2's real lua_State.

So it's a general conceptual question - is this crazy? Is it workable? How much can I copy Lua data around between distinct states (assuming thread safety is managed correctly)? Is there a smarter algorithm for this kind of work?

Pseudocode:

main thread:
	L

main thread loop:
	lua_State * L = get_first_available_thread2_luastate()
	if (L) {
		lua_pushthings(L); //etc
		setownership(L, thread2);	// locks L
	}

thread2:
	lua_State * L;	// thread's main lua state, only accessible here
lua_State * L1, L2; // input states used to receive Lua data from another thread

thread2 loop:
	L = thread2.lua_State;
	lua_State * Linput = get_first_locked_thread2_luastate()
	if (Linput) {
		lua_copythingsintoL(Linput, L); // etc
		setownership(Linput, main);	// unlocks L
	}
	lua_callthings(L); //etc


get_first_locked_thread2_luastate() {
	return L1.locked ?  L1 : L2.locked ? L2 : 0;
}

get_first_available_thread2_luastate() {
	return !L1.locked ?  L1 : !L2.locked ? L2 : 0;
}

Graham

Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Luiz Henrique de Figueiredo
> How much can I copy Lua data around between distinct  
> states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).

Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Javier Guerra Giraldez
On Friday 15 September 2006 7:15 pm, Luiz Henrique de Figueiredo wrote:
> > How much can I copy Lua data around between distinct
> > states (assuming thread safety is managed correctly)?
>
> If you have got your locking right, you can use lua_xmove if the Lua
> states are related (ie, children of the same Lua top state).

it would be great if there was any way to do that for unrelated Lua states.  i 
guess it's not a trivial thing, unfortunately.

in fact, each time i try to think how would that work, it's hard to even 
define a 'desired' specification (how deeply to copy, what to do with 
userdata, metatables, upvalues, etc)

thinking "the other way", a finer-grained lock for better concurrency between 
related states seems more promising in the long run, but that's far far away 
from my comfort zone.

a third approach is to have some communications API between states, hopefully 
something better than single values.  ideally, some way to define 'shared' 
data.  then the problem becomes how 'contagious' should the 'shariness' be... 
still no good ideas at the horizon.

-- 
Javier

Attachment: pgpTo9poiczHZ.pgp
Description: PGP signature

Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Graham Wakefield
In reply to this post by Luiz Henrique de Figueiredo
On Sep 15, 2006, at 5:15 PM, Luiz Henrique de Figueiredo wrote:

How much can I copy Lua data around between distinct  
states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).

Aha! I didn't realise what lua_newthread did (I thought it was something to do with coroutines).  OK.  So if I understand correctly, I can have a root lua_State in one thread, create new lua_States from it using lua_newthread(), which will be managed in other threads, but create thread-safe interchanges between them using lua_xmove.  

So lua_xmove is really creating copies from one stack to another.  How are references resolved - are they deep copies?

lua_newthread retains references to global data; how can this be thread safe if both the old & new threads (running in different system threads) can access them?  Are they also independent copies?

Sorry if I'm not understanding something here.

According to the manual:

lua_newthread

lua_State *lua_newthread (lua_State *L);

Creates a new thread, pushes it on the stack, and returns a pointer to a lua_State that represents this new thread. The new state returned by this function shares with the original state all global objects (such as tables), but has an independent execution stack.

There is no explicit function to close or to destroy a thread. Threads are subject to garbage collection, like any Lua object.

Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Graham Wakefield
In reply to this post by Luiz Henrique de Figueiredo
OK I found this: http://lua-users.org/wiki/ThreadsTutorial

Makes a bit more sense; I should implement lua_lock and lua_unlock to ensure thread safety.

Is this all still true for 5.1?

Graham


On Sep 15, 2006, at 5:15 PM, Luiz Henrique de Figueiredo wrote:

How much can I copy Lua data around between distinct
states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).


Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Graham Wakefield
In reply to this post by Graham Wakefield
Hmm. I'm not sure if this is going to work for me. My main (system) thread is spending almost all of its time jumping in and out of Lua, occasionally wanting to send data to the secondary (system) thread. The secondary thread needs to read this input data and behave accordingly, and ideally this would all happen in Lua, or at least as much as possible. Not only do I not need my secondary lua_State to have global references to the first, I'd actually have to ensure that it is not possible, since both will be concurrently active.

Using lua_lock / lua_unlock seems an unnecessary burden for my situation, since communication is essentially one-way (and efficiency is very important).

I guess I'll need to write my own message send / receive methods in C to implement a simple message queue between lua states.

Q: if a thread created using lua_newthread modifies 'global' data, is this modifying the global data in the original thread, or modifying copied data in the new thread?

On Sep 16, 2006, at 2:07 AM, [hidden email] wrote:

On Sep 15, 2006, at 5:15 PM, Luiz Henrique de Figueiredo wrote:

How much can I copy Lua data around between distinct
states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).

Aha! I didn't realise what lua_newthread did (I thought it was something to do with coroutines). OK. So if I understand correctly, I can have a root lua_State in one thread, create new lua_States from it using lua_newthread(), which will be managed in other threads, but create thread-safe interchanges between them using lua_xmove.

So lua_xmove is really creating copies from one stack to another. How are references resolved - are they deep copies?

lua_newthread retains references to global data; how can this be thread safe if both the old & new threads (running in different system threads) can access them? Are they also independent copies?

Sorry if I'm not understanding something here.

According to the manual:

lua_newthread

lua_State *lua_newthread (lua_State *L);
Creates a new thread, pushes it on the stack, and returns a pointer to a lua_State that represents this new thread. The new state returned by this function shares with the original state all global objects (such as tables), but has an independent execution stack.

There is no explicit function to close or to destroy a thread. Threads are subject to garbage collection, like any Lua object.


Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Javier Guerra Giraldez
In reply to this post by Graham Wakefield
On Saturday 16 September 2006 4:07 am, [hidden email] wrote:
> Aha! I didn't realise what lua_newthread did (I thought it was
> something to do with coroutines).  OK.  So if I understand correctly,

i think it does something to do with coroutines, one is built on top of the 
other... not sure how.

> I can have a root lua_State in one thread, create new lua_States from
> it using lua_newthread(), which will be managed in other threads, but
> create thread-safe interchanges between them using lua_xmove.

not exactly, lua_newthread() doesn't create a 'new' lua_State, it's 
a 'related' lua_State, it shares global storage with the original state; 
therefore it's important not to let two of them run at the same time with 
preemptive OS threads.  that's why you have to implement lua_lock and 
lua_unlock macros.  if done correctly it lets two related thread share a lock 
to manage the shared global storage (see LuaThread, by Diego Nehab)

> So lua_xmove is really creating copies from one stack to another.
> How are references resolved - are they deep copies?

lua_xmove just copies the value held at the stack.  if it's a reference (to a 
table, function, etc), it just copies the reference; just like parameter 
passing

> lua_newthread retains references to global data; how can this be
> thread safe if both the old & new threads (running in different
> system threads) can access them?  Are they also independent copies?

it's not thread safe unless you implement lua_lock/unlock; they're not copies


-- 
Javier

Attachment: pgpeZnkaEG29q.pgp
Description: PGP signature

Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Asko Kauppi
In reply to this post by Javier Guerra Giraldez

I'll be needing this soon, writing sort of multitasking framework, where each state would deal with its own area, and communicate via messages.

I thought to do it / use a module that does it as follows: ([] marking optional params/return values)

h= open( myid_str ) -- establish a postbox for this state, with the given name (unique) bool= h:send( toid_str, msg_id [, ...] ) -- send data (_not_ userdata) with default priority (0) bool= h:send_prio( prio_int, toid_str, msg_id [, ...] ) -- place ahead of queue, if prio > 0 [msg_id [, ...]]= h:poll( [msg_id] ) -- poll if there's a message (of specific id) [msg_id [, ...]]= h:receive( [msg_id] ) -- read out a message, to this state
  h:close()					-- thanks for the Fish!

The message passing would use serialization (placing tables etc. into string format, extractable back in the other state). There would be an unlimited queue for each such postbox, and the state/thread would itself need to read incoming messages, normally as part of its main loop.

Has anyone got an implementation, already?  :)

-asko


Javier Guerra kirjoitti 16.9.2006 kello 5.28:

On Friday 15 September 2006 7:15 pm, Luiz Henrique de Figueiredo wrote:
How much can I copy Lua data around between distinct
states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).

it would be great if there was any way to do that for unrelated Lua states. i
guess it's not a trivial thing, unfortunately.

in fact, each time i try to think how would that work, it's hard to even
define a 'desired' specification (how deeply to copy, what to do with
userdata, metatables, upvalues, etc)

thinking "the other way", a finer-grained lock for better concurrency between related states seems more promising in the long run, but that's far far away
from my comfort zone.

a third approach is to have some communications API between states, hopefully something better than single values. ideally, some way to define 'shared' data. then the problem becomes how 'contagious' should the 'shariness' be...
still no good ideas at the horizon.

--
Javier


Reply | Threaded
Open this post in threaded view
|

Re: safely buffering Lua data between (system) threads & lua_States?

Daniel Quintela
Asko Kauppi escribió:

I'll be needing this soon, writing sort of multitasking framework, where each state would deal with its own area, and communicate via messages.

I thought to do it / use a module that does it as follows: ([] marking optional params/return values)

h= open( myid_str ) -- establish a postbox for this state, with the given name (unique) bool= h:send( toid_str, msg_id [, ...] ) -- send data (_not_ userdata) with default priority (0) bool= h:send_prio( prio_int, toid_str, msg_id [, ...] ) -- place ahead of queue, if prio > 0 [msg_id [, ...]]= h:poll( [msg_id] ) -- poll if there's a message (of specific id) [msg_id [, ...]]= h:receive( [msg_id] ) -- read out a message, to this state
  h:close()                    -- thanks for the Fish!

The message passing would use serialization (placing tables etc. into string format, extractable back in the other state). There would be an unlimited queue for each such postbox, and the state/thread would itself need to read incoming messages, normally as part of its main loop.

Has anyone got an implementation, already?  :)

-asko


Javier Guerra kirjoitti 16.9.2006 kello 5.28:

On Friday 15 September 2006 7:15 pm, Luiz Henrique de Figueiredo wrote:
How much can I copy Lua data around between distinct
states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).

it would be great if there was any way to do that for unrelated Lua states. i
guess it's not a trivial thing, unfortunately.

in fact, each time i try to think how would that work, it's hard to even
define a 'desired' specification (how deeply to copy, what to do with
userdata, metatables, upvalues, etc)

thinking "the other way", a finer-grained lock for better concurrency between related states seems more promising in the long run, but that's far far away
from my comfort zone.

a third approach is to have some communications API between states, hopefully something better than single values. ideally, some way to define 'shared' data. then the problem becomes how 'contagious' should the 'shariness' be...
still no good ideas at the horizon.

--Javier

It looks like register, post and receive functions of LuaTask.
But you are talking about separated processes instead of threads, isn't it?
The idea of having a common Lua state in shared memory "connected" to the threads/processes own states had been on my mind for a while. You only have to move something from your own state to the shared one to make it available to anybody.
But I don't know what is the best way to connect those unrelated Lua states.
Any suggestions will be welcome and appreciated.

Regards,
Daniel


Reply | Threaded
Open this post in threaded view
|

Too many modules? (Re: safely buffering Lua data between (system) threads & lua_States?)

Asko Kauppi

It looks like register, post and receive functions of LuaTask.
But you are talking about separated processes instead of threads, isn't it?

Not really. What I have in mind is similar in concept to the notion of processes, and an OS, but they will actually be run as threads in a single process. This way, they can use a C module (malloc/free) for data exchange.

The idea of having a common Lua state in shared memory "connected" to the threads/processes own states had been on my mind for a while. You only have to move something from your own state to the shared one to make it available to anybody.

Would be one way; I'm trying to make this as light as possible, and as async as possible. No shared state would be required in my module- planned approach; in fact I do have C code for doing this already, just checking if there's a standard solution I've missed (s.a. LuaTask could be).

But I don't know what is the best way to connect those unrelated Lua states.
Any suggestions will be welcome and appreciated.

This message thread shows, in my opinion, a worrysome situation LuaLand has come to. There _are_ open source building blocks, but they're not entirely documented, or too 'well' documented, or... There's no clear place for shopping for the right module. We've in effect created an open source bazaar within the community, with lots of overlap. That is good, unless it brings to the "ah, there's N solutions, none really suiting, so.... I'll do my N+1".

We need LuaRocks to solve the arisen condition, and _uniform_ documentation of what a module actually promises to do, and what it's platform requirements are, how speedy it is, etc... :)

-asko


Regards,
Daniel


Daniel Quintela kirjoitti 17.9.2006 kello 17.30:

Asko Kauppi escribió:

I'll be needing this soon, writing sort of multitasking framework, where each state would deal with its own area, and communicate via messages.

I thought to do it / use a module that does it as follows: ([] marking optional params/return values)

h= open( myid_str ) -- establish a postbox for this state, with the given name (unique) bool= h:send( toid_str, msg_id [, ...] ) -- send data (_not_ userdata) with default priority (0) bool= h:send_prio( prio_int, toid_str, msg_id [, ...] ) -- place ahead of queue, if prio > 0 [msg_id [, ...]]= h:poll( [msg_id] ) -- poll if there's a message (of specific id) [msg_id [, ...]]= h:receive( [msg_id] ) -- read out a message, to this state
  h:close()                    -- thanks for the Fish!

The message passing would use serialization (placing tables etc. into string format, extractable back in the other state). There would be an unlimited queue for each such postbox, and the state/ thread would itself need to read incoming messages, normally as part of its main loop.

Has anyone got an implementation, already?  :)

-asko


Javier Guerra kirjoitti 16.9.2006 kello 5.28:

On Friday 15 September 2006 7:15 pm, Luiz Henrique de Figueiredo wrote:
How much can I copy Lua data around between distinct
states (assuming thread safety is managed correctly)?

If you have got your locking right, you can use lua_xmove if the Lua
states are related (ie, children of the same Lua top state).

it would be great if there was any way to do that for unrelated Lua states. i
guess it's not a trivial thing, unfortunately.

in fact, each time i try to think how would that work, it's hard to even define a 'desired' specification (how deeply to copy, what to do with
userdata, metatables, upvalues, etc)

thinking "the other way", a finer-grained lock for better concurrency between related states seems more promising in the long run, but that's far far away
from my comfort zone.

a third approach is to have some communications API between states, hopefully something better than single values. ideally, some way to define 'shared' data. then the problem becomes how 'contagious' should the 'shariness' be...
still no good ideas at the horizon.

--Javier

It looks like register, post and receive functions of LuaTask.
But you are talking about separated processes instead of threads, isn't it? The idea of having a common Lua state in shared memory "connected" to the threads/processes own states had been on my mind for a while. You only have to move something from your own state to the shared one to make it available to anybody. But I don't know what is the best way to connect those unrelated Lua states.
Any suggestions will be welcome and appreciated.

Regards,
Daniel




Reply | Threaded
Open this post in threaded view
|

Re: Too many modules? (Re: safely buffering Lua data between (system) threads & lua_States?)

Javier Guerra Giraldez
On Sunday 17 September 2006 11:28 am, Asko Kauppi wrote:
> Not really. What I have in mind is similar in concept to the notion
> of processes, and an OS, but they will actually be run as threads in
> a single process. This way, they can use a C module (malloc/free) for
> data exchange.

I think there's a continuum of separations when you think of Lua multitasking.  
some easy to recognise points are:

A- several processes accessing the same SQL DB server
B- having the DB server on the same machine as several (Lua) clients
C- use a memory-backed DB server.
D- a DB-like C API to access a POSIX shared memory space, using semaphores
E- a single process creates several separate LuaStates, each on an OS thread, 
accessing a POSIX shared memory
F- keep the DB-like API to access malloc/free blocks of memory instead of 
POSIX shared memory
G- add metatables to make the DB-like API look like Lua tables
...
...
...
M- related LuaStates, running one at a time
N- implement locking to run related LuaStates on OS Threads (LuaThread)
...
Q- finer-grained locks to allow more that one state some degree of simultanous 
access to the storage 
...
Y- coroutine-based cooperative multitasking

IMO, it shouldn't be too hard to implement F,G; but a nearly ideal would be 
Q... could that be a goal for Lua6??


-- 
Javier

Attachment: pgpHDw7YwYnRW.pgp
Description: PGP signature