aboutsummaryrefslogtreecommitdiff
path: root/deep_userdata_example/deeptest.lua
blob: 6ef4b74f1d93fa5e3520d762dde006ffd93849c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
local lanes = require("lanes").configure{ with_timers = false}
local l = lanes.linda "my linda"

local table_unpack = table.unpack or unpack -- Lua 5.1 support

-- we will transfer userdata created by this module, so we need to make Lanes aware of it
local dt = lanes.require "deep_userdata_example"

-- set DEEP to any non-false value to run the Deep Userdata tests. "gc" selects a special test for debug purposes
DEEP = DEEP or true
-- set CLONABLE to any non-false value to run the Clonable Userdata tests
CLONABLE = CLONABLE or true

-- lua 5.1->5.2 support a single table uservalue
-- lua 5.3->5.4 supports an arbitrary type uservalue
local test_uvtype = (_VERSION == "Lua 5.4") and "function" or (_VERSION == "Lua 5.3") and "string" or "table"
-- lua 5.4 supports multiple uservalues
local nupvals = _VERSION == "Lua 5.4" and 3 or 1

local makeUserValue = function( obj_)
	if test_uvtype == "table" then
		return {"some uservalue"}
	elseif test_uvtype == "string" then
		return "some uservalue"
	elseif test_uvtype == "function" then
		-- a function that pull the userdata as upvalue
		local f = function()
			return "-> '" .. tostring( obj_) .. "'"
		end
		return f
	end
end

local printDeep = function( prefix_, obj_, t_)
	print( prefix_, obj_)
	for uvi = 1, nupvals do
		local uservalue = obj_:getuv(uvi)
		print ("uv #" .. uvi, type( uservalue), uservalue, type(uservalue) == "function" and uservalue() or "")
	end
	if t_ then
		local count = 0
		for k, v in ipairs( t_) do
			print( "t["..tostring(k).."]", v)
			count = count + 1
		end
		-- we should have only 2 indexed entries with the same value
		assert(count == 2 and t_[1] == t_[2])
	end
	print()
end

local performTest = function( obj_)
	-- setup the userdata with some value and a uservalue
	obj_:set( 666)
	obj_:setuv( 1, makeUserValue( obj_))
	if nupvals > 1 then
		-- keep uv #2 as nil
		obj_:setuv( 3, "ENDUV")
	end

	local t =
	{
		-- two indices with an identical value: we should also have identical values on the other side (even if not the same as the original ones when they are clonables)
		obj_,
		obj_,
		-- this one won't transfer because we don't support full uservalue as keys
		[obj_] = "val"
	}

	-- read back the contents of the object
	printDeep( "immediate:", obj_, t)

	-- send the object in a linda, get it back out, read the contents
	l:set( "key", obj_, t)
	-- when obj_ is a deep userdata, out is the same userdata as obj_ (not another one pointing on the same deep memory block) because of an internal cache table [deep*] -> proxy)
	-- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier
	local _n, _val1, _val2 = l:get( "key", 2)
	assert(_n == (_val2 and 2 or 1))
	printDeep( "out of linda:", _val1, _val2)

	-- send the object in a lane through argument passing, the lane body returns it as return value, read the contents
	local g = lanes.gen(
		"package"
		, {
			required = { "deep_userdata_example"} -- we will transfer userdata created by this module, so we need to make this lane aware of it
		}
		, function( arg_, t_)
			-- read contents inside lane: arg_ and t_ by argument
			printDeep( "in lane, as arguments:", arg_, t_)
			-- read contents inside lane: obj_ and t by upvalue
			printDeep( "in lane, as upvalues:", obj_, t)
			-- read contents inside lane: in linda
			local _n, _val1, _val2 = l:get( "key", 2)
			assert(_n == (_val2 and 2 or 1))
			printDeep( "in lane, from linda:", _val1, _val2)
			return arg_, t_
		end
	)
	h = g( obj_, t)
	-- when obj_ is a deep userdata, from_lane is the same userdata as obj_ (not another one pointing on the same deep memory block) because of an internal cache table [deep*] -> proxy)
	-- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier
	printDeep( "from lane:", h[1], h[2])
end

if DEEP then
	print "================================================================"
	print "DEEP"
	local d = dt.new_deep(nupvals)
	if type(DEEP) == "string" then
		local gc_tests = {
			thrasher = function(repeat_, size_)
				print "in thrasher"
				-- result is a table of repeat_ tables, each containing size_ entries
				local result = {}
				for i = 1, repeat_ do
					local batch_values = {}
					for j = 1, size_ do
						table.insert(batch_values, j)
					end
					table.insert(result, batch_values)
				end
				print "thrasher done"
				return result
			end,
			stack_abuser = function(repeat_, size_)
				print "in stack_abuser"
				for i = 1, repeat_ do
					local batch_values = {}
					for j = 1, size_ do
						table.insert(batch_values, j)
					end
					-- return size_ values
					local _ = table_unpack(batch_values)
				end
				print "stack_abuser done"
				return result
			end
		}
		-- have the object call the function from inside one of its functions, to detect if it gets collected from there (while in use!)
		local testf = gc_tests[DEEP]
		if testf then
			local r = d:invoke(gc_tests[DEEP], REPEAT or 10, SIZE or 10)
			print("invoke -> ", tostring(r))
		else
			print("unknown test '" .. DEEP .. "'")
		end
	else
		performTest(d)
	end
end

if CLONABLE then
	print "================================================================"
	print "CLONABLE"
	performTest( dt.new_clonable(nupvals))
end

print "================================================================"
print "TEST OK"