Add support for copying circular table references.
* Add a maximum copy depth of 30. * Add stack overflow checks. Value copying now uses a cache table to detect when multiple references to the same table is being copied from one lua_State to another.master
parent
32378b2af4
commit
18df691f1f
@ -0,0 +1,134 @@
|
|||||||
|
-- Copyright (c) 2011 by Robert G. Jakabosky <bobby@sharedrealm.com>
|
||||||
|
--
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
-- of this software and associated documentation files (the "Software"), to deal
|
||||||
|
-- in the Software without restriction, including without limitation the rights
|
||||||
|
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
-- copies of the Software, and to permit persons to whom the Software is
|
||||||
|
-- furnished to do so, subject to the following conditions:
|
||||||
|
--
|
||||||
|
-- The above copyright notice and this permission notice shall be included in
|
||||||
|
-- all copies or substantial portions of the Software.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
-- THE SOFTWARE.
|
||||||
|
|
||||||
|
local llthreads = require"llthreads"
|
||||||
|
|
||||||
|
local sleep
|
||||||
|
local status, socket = pcall(require,"socket")
|
||||||
|
if status then
|
||||||
|
sleep = function(secs)
|
||||||
|
return socket.sleep(secs)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
sleep = function(secs)
|
||||||
|
os.execute("sleep " .. tonumber(secs))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local dump_code = [==[
|
||||||
|
local function dump_recur(seen, obj, depth)
|
||||||
|
local out
|
||||||
|
local t = type(obj)
|
||||||
|
-- if not a table just convert to string.
|
||||||
|
if t ~= "table" then
|
||||||
|
if t == "string" then
|
||||||
|
return '"' .. obj .. '"'
|
||||||
|
end
|
||||||
|
return tostring(obj)
|
||||||
|
end
|
||||||
|
-- check if this table has been seen already.
|
||||||
|
if seen[obj] then
|
||||||
|
return "Already dumped " .. tostring(obj)
|
||||||
|
end
|
||||||
|
seen[obj] = true
|
||||||
|
-- restrict max depth.
|
||||||
|
if depth >= 10 then
|
||||||
|
return "{... max depth reached ...}"
|
||||||
|
end
|
||||||
|
depth = depth + 1
|
||||||
|
-- output table key/value pairs
|
||||||
|
local tabs = string.rep(" ",depth)
|
||||||
|
local out = "{\n"
|
||||||
|
for k,v in pairs(obj) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
out = out .. tabs .. '[' .. dump_recur(seen, k, depth) .. '] = ' ..
|
||||||
|
dump_recur(seen, v, depth) .. ',\n'
|
||||||
|
else
|
||||||
|
out = out .. tabs .. '[' .. k .. '] = ' .. dump_recur(seen, v, depth) .. ',\n'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out .. tabs:sub(1,-3) .. "}"
|
||||||
|
end
|
||||||
|
|
||||||
|
local obj = ...
|
||||||
|
local seen = {}
|
||||||
|
return dump_recur(seen, obj, 0)
|
||||||
|
]==]
|
||||||
|
|
||||||
|
local dump = loadstring(dump_code)
|
||||||
|
|
||||||
|
local child_code = [==[
|
||||||
|
local dump = loadstring[[
|
||||||
|
]==] .. dump_code .. [==[
|
||||||
|
]]
|
||||||
|
local args = ...
|
||||||
|
|
||||||
|
print("Child thread args:", dump(args))
|
||||||
|
|
||||||
|
-- return all values.
|
||||||
|
return ...
|
||||||
|
]==]
|
||||||
|
|
||||||
|
local function test_thread_value_copying(...)
|
||||||
|
local args = {...}
|
||||||
|
print("Main thread args:", dump(args))
|
||||||
|
local thread = llthreads.new(child_code, args)
|
||||||
|
-- start joinable thread
|
||||||
|
assert(thread:start())
|
||||||
|
|
||||||
|
local status, results = thread:join()
|
||||||
|
print("Main thread results:", dump(results))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create some tables.
|
||||||
|
local a1 = { "a1" }
|
||||||
|
local a2 = { "a2" }
|
||||||
|
local a3 = { "a3" }
|
||||||
|
local a4 = { "a4" }
|
||||||
|
local b1 = { a1, a2, a3, a4 }
|
||||||
|
local b2 = { a1=a1, a2=a2, a3=a3, a4=a4 }
|
||||||
|
|
||||||
|
--
|
||||||
|
-- no loops
|
||||||
|
--
|
||||||
|
test_thread_value_copying(b1, b2)
|
||||||
|
|
||||||
|
local top = {}
|
||||||
|
-- self reference.
|
||||||
|
top.top = top
|
||||||
|
top[top] = top
|
||||||
|
-- nested reference.
|
||||||
|
top.sub1 = { sub2 = { sub3 = { top } } }
|
||||||
|
|
||||||
|
--
|
||||||
|
-- loops
|
||||||
|
--
|
||||||
|
test_thread_value_copying(top)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test max depth
|
||||||
|
--
|
||||||
|
local outer = {}
|
||||||
|
for n=1,100 do
|
||||||
|
outer = {outer}
|
||||||
|
end
|
||||||
|
local status, err = pcall(test_thread_value_copying,outer)
|
||||||
|
assert(not status, "Assertion failed: max depth test failed.")
|
||||||
|
|
||||||
Loading…
Reference in New Issue