PDA

View Full Version : Table as Argument in Lua Function Call



dsmes
February 19th, 2007, 03:55 PM
When you pass regular variables as arguments to a function, what is passed becomes a local variable within the scope of the function.
function test1(aa, bb)
-- variables aa and bb can be manipulated w/o affecting x & y
end;

-- now call the function
x, y = 11, 22
test1(x, y)But when you pass a table as below, I think you are really passing a reference to the original table (not a copy of the table). That is, changes to the passed table in the function actually change the original table (I haven't verified this). Is this a true statement? See below for why I ask.
function test2(aa, bb, cc)
-- variables aa and bb can be manipulated w/o affecting x & y
cc.g = 777 -- this changes z.g as well (I think)
end;

-- now call the function
x, y = 11, 22
z = {e=5, f=6, g=7}
test2(x, y, z)Presently, I'm sending a table to a worker function like test2 as a separate thread. I find that table cc within that thread changes when table z is changed outside the thread (after the new thread is launched). How do I avoid this behavior? How do I make the table passed as an argument be local to the function to which it is passed?

toml0006
February 19th, 2007, 06:00 PM
I'm no lua expert, but it would make sense that passing integer values does not alter the originals. If x and y were strings, and you altered them in the function you would see a similar result as with the tables. If you want to alter a copy of the table data, do just that, copy the table then pass it to the function.

Rob H
February 20th, 2007, 01:57 AM
Almost correct Tom. This only applies to tables, but does not apply to strings, strings in Lua are unique and unmutable, ie every string is only referenced once and once only, when you 'change' a string it creates a new string (or references an existing one if the contents are the same).

You are right about copying the table. There's a handy function provided in Additions.lua called table.copy that makes this easy and also copes with circular references.

dsmes
February 20th, 2007, 03:06 AM
My calls to a function in a new thread will be in a loop. Will table.copy work in this case? Or will the newly copied table intended for my new thread overwrite the table I copied in the previous loop? I suppose I could put a loop counter number in the new table name or is there an easier way?

Rob H
February 20th, 2007, 03:26 AM
Can you post a bit of code to illustrate what you're doing? It's always easier to comment on actual code.

dsmes
February 20th, 2007, 04:57 AM
This is the main thread of execution:
local ltd = {}
ltd = dcf.now() -- this loads the table
mutex = thread.newmutex()
alt = 12345
... -- other code
for testday = 1, 30 do
...
thread.newthread(dcf.FindMoonrise, {mutex, testday, alt, ltd})
...
ltd.x = 789 + testday -- this will change table xyz in the above
ltd.y = 987 - testday -- thread which is NOT what I want!
end
The intent is to have many of these worker functions running in parallel:
function dcf.FindMoonrise(mutex, day, alt, xyz)
...
_,_,_,alt,az,pa,JDf = dcf.MoonPos(xyz)
...
end;Note that the worker function calls another function and sends it the table as well. Likewise, the dcf.MoonPos function calls other functions that need this table. This last part should be fine as-is because they are not launched in new threads so table xyz remains intact. I just need to make sure table xyz doesn't change when table ltd is changed in the main execution thread.

Maybe I should unpack table ltd and only send the variables that change in each loop. Within the dcf.FindMoonrise thread and dcf.MoonPos function, I could reference constant values from the global table ltd. Thoughts?

If there's not a better way, can multiple threads read constant values from a global table at the same time or must each thread grab the mutex:lock() before reading the table (even though there is no danger of the table being written to while being read)?

Rob H
February 20th, 2007, 08:38 AM
I'd probably change the thread creation as follows

thread.newthread(dcf.FindMoonrise, {mutex, testday, alt, table.copy(ltd)})

That way each thread will have its own copy of the table.

dsmes
February 20th, 2007, 08:12 PM
Rats... Additions.lua hasn't migrated to NR yet so table.copy throws an error (a require statement didn't work either). So, until I migrate this to G, do you see any issues with my other idea? And what of the mutex question?

Rob H
February 21st, 2007, 12:16 AM
If the table is small then that I can see no reason not to unpack the values.

And, providing you can guarantee that a variable is not going to change for the lifetime of a thread then you should be fine accessing it without using a mutex.

I hope you're not running this on a PPC though.

dsmes
February 21st, 2007, 03:09 AM
Thanks Rob, I'll go down that path and see how it goes. And thanks for the clarification on mutex... I've always wondered about that.

No, ultimate target is not a PPC, but rather a Fujitsu C-500 slate running at 500MHz. On a 3GHZ desk machine, the number crunching of determining moon rise, moon set, and the phases of the moon for a year takes 90 seconds. I expect it will take over 10 minutes on the slate. NR appears to be locked up while this number crunching takes place. When I added coroutines to the script with a timer wait-state before the coroutine resume, screen updates and the ability to navigate in NR returned while the script was running. I'm hoping with threads, I can ultimately set their priority lower and gain even more responsiveness back... especially when this migrates to a the slow C-500. Long term, Girder can do this, but when on travel, I want the ability for NR to run stand-alone.

Rob H
February 21st, 2007, 04:27 AM
I would probably suggest limiting the number of threads to at most 5 - have a pool of worker threads and a work queue maintained by another thread. When a thread has finished with a work item have it signal the control thread which can assign it a new task from the queue.

dsmes
February 21st, 2007, 04:41 AM
I would probably suggest limiting the number of threads to at most 5 That's good to know. I'm curious why?

Rob H
February 21st, 2007, 04:59 AM
You'll have to play around with the number of threads most likely. I'm assuming that these threads are all CPU-bound, ie they won't be performing any IO (disk/network etc), so all you will achieve in having more threads is more overhead in switching between them, taking CPU away from the work that the threads are doing.

If I'm wrong about them being CPU-bound then you can get away with more threads.

You may find that you only want two worker threads.