thread state and dynamically loadable library

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

thread state and dynamically loadable library

Doug Currie
There are times when a dynamically loadable library needs to maintain
a per thread state. For example, I am working on a port of decNumber
to Lua as a loadable library; it needs a numeric context (rounding,
precision, etc.) that should be "global" to the thread, but not shared
between threads. [There is the option of requiring all operations on
decNumbers to take a context argument. This is the approach the C
library uses, but seems contrary to a Lua user's expectation of, e.g.,
infix operators working on decNumbers.]

Cursory reading of the Lua 5.1 manual leads one to believe that there
is a "thread environment" accessible using LUA_GLOBALSINDEX. But
looking at the source code leads me to conclude that all threads share
the same global table by default. If so, this is useless as a
per-thread storage mechanism for the library. Although it may be
possible to construct tables for each thread that defer to the global
global table using a metatable, this approach would depend on
mechanisms outside the library's control, e.g., at thread creation time.

If the library was statically linked with Lua, there is the
LUAI_EXTRASPACE mechanism to allocate some per-thread storage. This
would suit the purpose perfectly, except that it doesn't work for
dynamically loaded libraries and stock Lua.

What's needed is dynamically allocated per thread storage. Can any one
identify mechanisms to do this that I haven't considered above?

An approach that might work is to create a per-thread table for thread
local storage and put it in the registry keyed on the thread's
identity (i.e., L). This would work until some other library tried to
put something else in the registry keyed on L. I could construct a
"more unique key" such as L concatenated with the address of some
object in my library, but this either runs the risk of running out of
bits in LUA_NUMBER, or taking lots of time in re-constructing the key
every time the context is needed.

It would be really nice to have a LUA_THREADREGISTRYINDEX that
provided access to a gc'd slot in the thread state (initially nil or
an empty table). Then dynamically loadable libraries could store per
thread context in that table.

e



Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

D Burgess-4
I think you have thought of most of the alternatives. I had a similar
problem and used lazy initialization which tests if the thread
LUA_GLOBALSINDEX is the same table as the main thread
LUA_GLOBALSINDEX, if they are the same I create a thread
globals table that is metatabled (__index) to the global table
of the main thread.

DB

Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

Doug Currie
Monday, September 4, 2006, 11:26:06 PM, D Burgess wrote:

> I think you have thought of most of the alternatives. I had a similar
> problem and used lazy initialization which tests if the thread
> LUA_GLOBALSINDEX is the same table as the main thread
> LUA_GLOBALSINDEX, if they are the same I create a thread
> globals table that is metatabled (__index) to the global table
> of the main thread.

OK. What technique is used to decide what table is the main thread
LUA_GLOBALSINDEX? I suppose you could use a rawget to see if the
loadable library's table is in the LUA_GLOBALSINDEX, and if it is,
conclude that that table must be the top level table. But if the
loadable library's table isn't there, can you conclude that this table
is unique to the thread?

What happens when a thread that has already been lazily allocated a
thread globals table spawns sub-threads? These will inherit the
library created thread globals table. How do you know to create yet
another such table in the child threads?

I suppose you could create a key in each thread globals table that
refers to its thread (L), and check this every time you use it (along
with the check that the loadable library's table isn't there). This
would prevent tables from being inherited. But, yikes, this is getting
to be an awful lot of overhead just to get a pointer to a thread
context.

e

-- 
Doug Currie
Londonderry, NH


Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

D Burgess-4
But, yikes, this is getting
to be an awful lot of overhead just to get a pointer to a thread
context.

I have no simple solution.

DB

Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

Doug Currie
Tuesday, September 5, 2006, 3:04:10 AM, D Burgess wrote:

>> But, yikes, this is getting
>> to be an awful lot of overhead just to get a pointer to a thread
>> context.

> I have no simple solution.

Thanks for helping me kick this around.

I will try this...

1. Create a table in the registry owned by the loadable library

2. Enter contexts in this table keyed the the thread address (L)

3. Cache the most recently used context in the library to avoid the
hit of two table lookups for very operation. Caching is safe per:
http://thread.gmane.org/gmane.comp.lang.lua.general/21690/focus=21706

Regards,

e

-- 
Doug Currie
Londonderry, NH


Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

D Burgess-4
Table lookups are PDQ, until you measure the performance one
never knows. I think the strategy is reasonable. One option is to
wait until the Lua conference is over and get some input from
those more knowledgable than me.

DB

Doug Currie wrote:
I will try this...

1. Create a table in the registry owned by the loadable library

2. Enter contexts in this table keyed the the thread address (L)

3. Cache the most recently used context in the library to avoid the
hit of two table lookups for very operation. Caching is safe per:
http://thread.gmane.org/gmane.comp.lang.lua.general/21690/focus=21706


Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

Thomas Lavergne
In reply to this post by Doug Currie
A solution could be to use lua_pushthread to push the current thread on
the stack and use this value to index a table which associate each
thread with the value you need.

You can store this table in the registry, so your module have access to
a thread-private space. It's slower than storring directly these
key-value pairs in the registry but don't forget that the registry is
shared between all the modules and your own are probalby not the only
one who want a per-thread storage.

Tom

On Mon, Sep 04, 2006 at 11:04:41PM -0400, Doug Currie wrote:
> There are times when a dynamically loadable library needs to maintain
> a per thread state. For example, I am working on a port of decNumber
> to Lua as a loadable library; it needs a numeric context (rounding,
> precision, etc.) that should be "global" to the thread, but not shared
> between threads. [There is the option of requiring all operations on
> decNumbers to take a context argument. This is the approach the C
> library uses, but seems contrary to a Lua user's expectation of, e.g.,
> infix operators working on decNumbers.]
> 
> Cursory reading of the Lua 5.1 manual leads one to believe that there
> is a "thread environment" accessible using LUA_GLOBALSINDEX. But
> looking at the source code leads me to conclude that all threads share
> the same global table by default. If so, this is useless as a
> per-thread storage mechanism for the library. Although it may be
> possible to construct tables for each thread that defer to the global
> global table using a metatable, this approach would depend on
> mechanisms outside the library's control, e.g., at thread creation time.
> 
> If the library was statically linked with Lua, there is the
> LUAI_EXTRASPACE mechanism to allocate some per-thread storage. This
> would suit the purpose perfectly, except that it doesn't work for
> dynamically loaded libraries and stock Lua.
> 
> What's needed is dynamically allocated per thread storage. Can any one
> identify mechanisms to do this that I haven't considered above?
> 
> An approach that might work is to create a per-thread table for thread
> local storage and put it in the registry keyed on the thread's
> identity (i.e., L). This would work until some other library tried to
> put something else in the registry keyed on L. I could construct a
> "more unique key" such as L concatenated with the address of some
> object in my library, but this either runs the risk of running out of
> bits in LUA_NUMBER, or taking lots of time in re-constructing the key
> every time the context is needed.
> 
> It would be really nice to have a LUA_THREADREGISTRYINDEX that
> provided access to a gc'd slot in the thread state (initially nil or
> an empty table). Then dynamically loadable libraries could store per
> thread context in that table.
> 
> e
> 
> 

-- 
Thomas Lavergne                       "Le vrai rêveur est celui qui rêve
                                       de l'impossible."  (Elsa Triolet)
[hidden email]                           http://reveurs.org

Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

Edgar Toernig
In reply to this post by Doug Currie
Doug Currie wrote:
>
> An approach that might work is to create a per-thread table for thread
> local storage and put it in the registry keyed on the thread's
> identity (i.e., L). This would work until some other library tried to
> put something else in the registry keyed on L. I could construct a
> "more unique key" such as L concatenated with the address of some
> object in my library, but this either runs the risk of running out of
> bits in LUA_NUMBER, or taking lots of time in re-constructing the key
> every time the context is needed.

Module private registries are easy:

    #define MY_REGISTRY lua_upvalueindex(1)

and then make sure that you put the registry table into the first
upvalue of each function of your module.

Ciao, ET.

Reply | Threaded
Open this post in threaded view
|

Re: thread state and dynamically loadable library

Doug Currie
Tuesday, September 5, 2006, 2:29:39 PM, Edgar Toernig wrote:

> Module private registries are easy:

>     #define MY_REGISTRY lua_upvalueindex(1)

> and then make sure that you put the registry table into the first
> upvalue of each function of your module.

Yes, I see. That would be a bit faster and safer than using the global
registry, at the cost of an extra slot in each of the closures.

But wait! Lua 5.1's LUA_ENVIRONINDEX can create a shared environment
among the C functions without adding any closure slots. This is not
too well documented (at least I couldn't find anything that made it
clear to me until I read the source code).

I finally found Mike Pall's example; it is instructive despite being
primarily concerned with userdata environments, "the example shows how
to use the new C function environment, too"
http://lua-users.org/lists/lua-l/2005-04/msg00213.html

I also now appreciate Luis Carvalho's suggestion in
http://lua-users.org/lists/lua-l/2006-08/msg00505.html
"For more examples of LUA_ENVIRONINDEX, check liolib.c."

Thanks!

e

-- 
Doug Currie
Londonderry, NH