aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <bnt period germain arrobase gmail period com>2014-06-17 16:34:31 +0200
committerBenoit Germain <bnt period germain arrobase gmail period com>2014-06-17 16:34:31 +0200
commit77630de350fc89038378c798cd482ed751280fc2 (patch)
treef804d15dab8cd6ba1d57508719e738c12327eba9
parent55e53f8a24ce42cadfd2887e50bf0248eb10d301 (diff)
downloadlanes-77630de350fc89038378c798cd482ed751280fc2.tar.gz
lanes-77630de350fc89038378c798cd482ed751280fc2.tar.bz2
lanes-77630de350fc89038378c798cd482ed751280fc2.zip
Deep userdata changes
* bumped version to 3.9.6 * separate deep userdata code in a dedicated file to allow external modules to implement Lanes-compatible deep userdata without requiring a binary dependency against the Lanes module. because of this linda_id function(eDO_metatable) must push 2 values on the stack: a metatable and a deep version string obtained from luaG_pushdeepversion()
-rw-r--r--CHANGES5
-rw-r--r--CMakeLists.txt2
-rw-r--r--docs/index.html6
-rw-r--r--lanes-3.9.6-1.rockspec (renamed from lanes-3.9.4-1.rockspec)6
-rw-r--r--src/Makefile2
-rw-r--r--src/deep.c524
-rw-r--r--src/deep.h1
-rw-r--r--src/lanes.c5
-rw-r--r--src/tools.c472
9 files changed, 545 insertions, 478 deletions
diff --git a/CHANGES b/CHANGES
index 31599c4..3d8015d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,10 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 113: BGe 17-Jun-14
4 * bumped version to 3.9.6
5 * separate deep userdata code in a dedicated file to allow external modules to implement Lanes-compatible deep userdata without requiring a binary dependency against the Lanes module
6 because of this linda_id function(eDO_metatable) must push 2 values on the stack: a metatable and a deep version string obtained from luaG_pushdeepversion()
7
3CHANGE 112 BGe 16-May-14 8CHANGE 112 BGe 16-May-14
4 * bumped version to 3.9.5 9 * bumped version to 3.9.5
5 * fix linda.__towatch to return non-nil when the linda is empty 10 * fix linda.__towatch to return non-nil when the linda is empty
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6ac9e5..80d9f22 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,7 +38,7 @@ ENDIF(CYGWIN)
38 38
39# Build 39# Build
40INCLUDE_DIRECTORIES(src) 40INCLUDE_DIRECTORIES(src)
41ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c src/keeper.c src/compat.c) 41ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/deep.c src/threading.c src/tools.c src/keeper.c src/compat.c)
42 42
43IF(UNIX AND NOT CYGWIN) 43IF(UNIX AND NOT CYGWIN)
44 SET(LIBS pthread) 44 SET(LIBS pthread)
diff --git a/docs/index.html b/docs/index.html
index da5da71..bfd26bb 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -70,7 +70,7 @@
70 </p> 70 </p>
71 71
72 <p> 72 <p>
73 This document was revised on 20-Mar-14, and applies to version <tt>3.9.4</tt>. 73 This document was revised on 17-Jun-14, and applies to version <tt>3.9.6</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -1511,12 +1511,12 @@ events to a common Linda, but... :).</font>
1511 <ul> 1511 <ul>
1512 <li><tt>eDO_new</tt>: requests the creation of a new object, whose pointer is returned.</li> 1512 <li><tt>eDO_new</tt>: requests the creation of a new object, whose pointer is returned.</li>
1513 <li><tt>eDO_delete</tt>: receives this same pointer on the stack as a light userdata, and should cleanup the object.</li> 1513 <li><tt>eDO_delete</tt>: receives this same pointer on the stack as a light userdata, and should cleanup the object.</li>
1514 <li><tt>eDO_metatable</tt>: should build a metatable for the object. Don't cache the metatable yourself, Lanes takes care of it (<tt>eDO_metatable</tt> should only be invoked once per state).</li> 1514 <li><tt>eDO_metatable</tt>: should build a metatable for the object. Don't cache the metatable yourself, Lanes takes care of it (<tt>eDO_metatable</tt> should only be invoked once per state). Push the metatable on the stack, then call <tt>luaG_pushdeepversion()</tt> before returning (new in version 3.9.5).</li>
1515 <li><tt>eDO_module</tt>: requests the name of the module that exports the idfunc, to be returned. It is necessary so that Lanes can require it in any lane state that receives a userdata. This is to prevent crashes in situations where the module could be unloaded while the idfunc pointer is still held.</li> 1515 <li><tt>eDO_module</tt>: requests the name of the module that exports the idfunc, to be returned. It is necessary so that Lanes can require it in any lane state that receives a userdata. This is to prevent crashes in situations where the module could be unloaded while the idfunc pointer is still held.</li>
1516 </ul> 1516 </ul>
1517 Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. 1517 Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>.
1518 </li> 1518 </li>
1519 <li>Include <tt>"deep.h"</tt> and link against Lanes. 1519 <li>Include <tt>"deep.h"</tt> and either link against Lanes or statically compile deep.c into your module if you want to avoid a runtime dependency for users that will use your module without Lanes.
1520 <li>Instanciate your userdata using <tt>luaG_newdeepuserdata()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given an <tt>idfunc</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li> 1520 <li>Instanciate your userdata using <tt>luaG_newdeepuserdata()</tt>, instead of the regular <tt>lua_newuserdata()</tt>. Given an <tt>idfunc</tt>, it sets up the support structures and returns a state-specific proxy userdata for accessing your data. This proxy can also be copied over to other lanes.</li>
1521 <li>Accessing the deep userdata from your C code, use <tt>luaG_todeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li> 1521 <li>Accessing the deep userdata from your C code, use <tt>luaG_todeep()</tt> instead of the regular <tt>lua_touserdata()</tt>.</li>
1522</ol> 1522</ol>
diff --git a/lanes-3.9.4-1.rockspec b/lanes-3.9.6-1.rockspec
index f5f7e83..3435f19 100644
--- a/lanes-3.9.4-1.rockspec
+++ b/lanes-3.9.6-1.rockspec
@@ -7,11 +7,11 @@
7 7
8package = "Lanes" 8package = "Lanes"
9 9
10version = "3.9.4-1" 10version = "3.9.6-1"
11 11
12source= { 12source= {
13 url= "git://github.com/LuaLanes/lanes.git", 13 url= "git://github.com/LuaLanes/lanes.git",
14 branch= "v3.9.4" 14 branch= "v3.9.6"
15} 15}
16 16
17description = { 17description = {
@@ -58,7 +58,7 @@ build = {
58 { 58 {
59 ["lanes.core"] = 59 ["lanes.core"] =
60 { 60 {
61 sources = { "src/compat.c", "src/lanes.c", "src/keeper.c", "src/tools.c", "src/threading.c"}, 61 sources = { "src/compat.c", "src/deep.c", "src/lanes.c", "src/keeper.c", "src/tools.c", "src/threading.c"},
62 incdirs = { "src"}, 62 incdirs = { "src"},
63 }, 63 },
64 lanes = "src/lanes.lua" 64 lanes = "src/lanes.lua"
diff --git a/src/Makefile b/src/Makefile
index 6a92ce2..7e45d2a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,7 +7,7 @@
7 7
8MODULE=lanes 8MODULE=lanes
9 9
10SRC=lanes.c compat.c threading.c tools.c keeper.c 10SRC=lanes.c compat.c threading.c tools.c deep.c keeper.c
11 11
12OBJ=$(SRC:.c=.o) 12OBJ=$(SRC:.c=.o)
13 13
diff --git a/src/deep.c b/src/deep.c
new file mode 100644
index 0000000..52a6485
--- /dev/null
+++ b/src/deep.c
@@ -0,0 +1,524 @@
1/*
2 * DEEP.C Copyright (c) 2014, Benoit Germain
3 *
4 * Depp userdata support, separate in its own source file to help integration
5 * without enforcing a Lanes dependency
6 */
7
8/*
9===============================================================================
10
11Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com>
12 2011-14 Benoit Germain <bnt.germain@gmail.com>
13
14Permission is hereby granted, free of charge, to any person obtaining a copy
15of this software and associated documentation files (the "Software"), to deal
16in the Software without restriction, including without limitation the rights
17to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18copies of the Software, and to permit persons to whom the Software is
19furnished to do so, subject to the following conditions:
20
21The above copyright notice and this permission notice shall be included in
22all copies or substantial portions of the Software.
23
24THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30THE SOFTWARE.
31
32===============================================================================
33*/
34
35#include "compat.h"
36#include "tools.h"
37#include "deep.h"
38
39#include <stdio.h>
40#include <string.h>
41#include <ctype.h>
42#include <stdlib.h>
43#if !defined(__APPLE__)
44#include <malloc.h>
45#endif
46
47/*-- Metatable copying --*/
48
49/*
50 * 'reg[ REG_MT_KNOWN ]'= {
51 * [ table ]= id_uint,
52 * ...
53 * [ id_uint ]= table,
54 * ...
55 * }
56 */
57
58/*
59* Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
60*/
61void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_)
62{
63 STACK_GROW( L, 3);
64 STACK_CHECK( L);
65
66 lua_pushlightuserdata( L, key_); // key
67 lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil
68
69 if( lua_isnil( L, -1))
70 {
71 lua_pop( L, 1); //
72 lua_newtable( L); // {}
73 lua_pushlightuserdata( L, key_); // {} key
74 lua_pushvalue( L, -2); // {} key {}
75
76 // _R[key_] = {}
77 lua_rawset( L, LUA_REGISTRYINDEX); // {}
78
79 // Set its metatable if requested
80 if( mode_)
81 {
82 lua_newtable( L); // {} mt
83 lua_pushliteral( L, "__mode"); // {} mt "__mode"
84 lua_pushstring( L, mode_); // {} mt "__mode" mode
85 lua_rawset( L, -3); // {} mt
86 lua_setmetatable( L, -2); // {}
87 }
88 }
89 STACK_END( L, 1);
90 ASSERT_L( lua_istable( L, -1));
91}
92
93
94/*
95* Push a registry subtable (keyed by unique 'key_') onto the stack.
96* If the subtable does not exist, it is created and chained.
97*/
98void push_registry_subtable( lua_State* L, void* key_)
99{
100 push_registry_subtable_mode( L, key_, NULL);
101}
102
103
104/*---=== Deep userdata ===---*/
105
106void luaG_pushdeepversion( lua_State* L) { (void) lua_pushliteral( L, "f248e77a-a84d-44b5-9ad0-96c05679b885");}
107
108
109
110/* The deep portion must be allocated separately of any Lua state's; it's
111* lifespan may be longer than that of the creating state.
112*/
113#define DEEP_MALLOC malloc
114#define DEEP_FREE free
115
116/*
117* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
118* metatables:
119*
120* metatable -> idfunc
121* idfunc -> metatable
122*/
123#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
124 // any unique light userdata
125
126
127/*
128* The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying
129*/
130#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy)
131
132/*
133* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
134* Pops the both values off the stack.
135*/
136static void set_deep_lookup( lua_State* L)
137{
138 STACK_GROW( L, 3);
139 STACK_CHECK( L); // a b
140 push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
141 STACK_MID( L, 1);
142 lua_insert( L, -3); // {} a b
143 lua_pushvalue( L, -1); // {} a b b
144 lua_pushvalue( L,-3); // {} a b b a
145 lua_rawset( L, -5); // {} a b
146 lua_rawset( L, -3); // {}
147 lua_pop( L, 1); //
148 STACK_END( L, -2);
149}
150
151/*
152* Pops the key (metatable or idfunc) off the stack, and replaces with the
153* deep lookup value (idfunc/metatable/nil).
154*/
155static void get_deep_lookup( lua_State* L)
156{
157 STACK_GROW( L, 1);
158 STACK_CHECK( L); // a
159 lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK
160 lua_rawget( L, LUA_REGISTRYINDEX); // a {}
161
162 if( !lua_isnil( L, -1))
163 {
164 lua_insert( L, -2); // {} a
165 lua_rawget( L, -2); // {} b
166 }
167 lua_remove( L, -2); // a|b
168 STACK_END( L, 0);
169}
170
171/*
172* Return the registered ID function for 'index' (deep userdata proxy),
173* or NULL if 'index' is not a deep userdata proxy.
174*/
175static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_)
176{
177 // when looking inside a keeper, we are 100% sure the object is a deep userdata
178 if( mode_ == eLM_FromKeeper)
179 {
180 DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
181 // we can (and must) cast and fetch the internally stored idfunc
182 return (*proxy)->idfunc;
183 }
184 else
185 {
186 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
187 // it is the only way to ensure that the userdata is indeed a deep userdata!
188 // of course, we could just trust the caller, but we won't
189 luaG_IdFunction ret;
190 STACK_GROW( L, 1);
191 STACK_CHECK( L);
192
193 if( !lua_getmetatable( L, index)) // deep ... metatable?
194 {
195 return NULL; // no metatable: can't be a deep userdata object!
196 }
197
198 // replace metatable with the idfunc pointer, if it is actually a deep userdata
199 get_deep_lookup( L); // deep ... idfunc|nil
200
201 ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
202 lua_pop( L, 1);
203 STACK_END( L, 0);
204 return ret;
205 }
206}
207
208
209void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_)
210{
211 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
212 lua_pushlightuserdata( L, prelude_->deep);
213 ASSERT_L( prelude_->idfunc);
214 prelude_->idfunc( L, eDO_delete);
215 DEEP_FREE( (void*) prelude_);
216}
217
218
219/*
220* void= mt.__gc( proxy_ud )
221*
222* End of life for a proxy object; reduce the deep reference count and clean
223* it up if reaches 0.
224*/
225static int deep_userdata_gc( lua_State* L)
226{
227 DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1);
228 DEEP_PRELUDE* p = *proxy;
229 struct s_Universe* U = get_universe( L);
230 int v;
231
232 *proxy = 0; // make sure we don't use it any more
233
234 MUTEX_LOCK( &U->deep_lock);
235 v = -- (p->refcount);
236 MUTEX_UNLOCK( &U->deep_lock);
237
238 if( v == 0)
239 {
240 // 'idfunc' expects a clean stack to work on
241 lua_settop( L, 0);
242 free_deep_prelude( L, p);
243
244 // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything!
245 if ( lua_gettop( L) > 1)
246 {
247 luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
248 }
249 }
250 return 0;
251}
252
253
254/*
255 * Push a proxy userdata on the stack.
256 * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem
257 * (error cannot happen with mode_ == eLM_ToKeeper)
258 *
259 * Initializes necessary structures if it's the first time 'idfunc' is being
260 * used in this Lua state (metatable, registring it). Otherwise, increments the
261 * reference count.
262 */
263char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_)
264{
265 DEEP_PRELUDE** proxy;
266
267 // Check if a proxy already exists
268 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
269 lua_pushlightuserdata( L, prelude->deep); // DPC deep
270 lua_rawget( L, -2); // DPC proxy
271 if ( !lua_isnil( L, -1))
272 {
273 lua_remove( L, -2); // proxy
274 return NULL;
275 }
276 else
277 {
278 lua_pop( L, 1); // DPC
279 }
280
281 MUTEX_LOCK( &U->deep_lock);
282 ++ (prelude->refcount); // one more proxy pointing to this deep data
283 MUTEX_UNLOCK( &U->deep_lock);
284
285 STACK_GROW( L, 7);
286 STACK_CHECK( L);
287
288 proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy
289 ASSERT_L( proxy);
290 *proxy = prelude;
291
292 // Get/create metatable for 'idfunc' (in this state)
293 lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc
294 get_deep_lookup( L); // DPC proxy metatable?
295
296 if( lua_isnil( L, -1)) // // No metatable yet.
297 {
298 char const* modname;
299 int oldtop = lua_gettop( L); // DPC proxy nil
300 lua_pop( L, 1); // DPC proxy
301 // 1 - make one and register it
302 if( mode_ != eLM_ToKeeper)
303 {
304 prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion
305 if( lua_gettop( L) - oldtop != 1 || !lua_istable( L, -2) || !lua_isstring( L, -1))
306 {
307 lua_settop( L, oldtop); // DPC proxy X
308 lua_pop( L, 3); //
309 return "Bad idfunc(eOP_metatable): unexpected pushed value";
310 }
311 luaG_pushdeepversion( L); // DPC proxy metatable deepversion deepversion
312 if( !lua_equal( L, -1, -2))
313 {
314 lua_pop( L, 5); //
315 return "Bad idfunc(eOP_metatable): mismatched deep version";
316 }
317 lua_pop( L, 2); // DPC proxy metatable
318 // make sure the idfunc didn't export __gc, as we will store our own
319 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
320 if( !lua_isnil( L, -1))
321 {
322 lua_pop( L, 4); //
323 return "idfunc-created metatable shouldn't contain __gc";
324 }
325 lua_pop( L, 1); // DPC proxy metatable
326 }
327 else
328 {
329 // keepers need a minimal metatable that only contains __gc
330 lua_newtable( L); // DPC proxy metatable
331 }
332 // Add our own '__gc' method
333 lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc
334 lua_setfield( L, -2, "__gc"); // DPC proxy metatable
335
336 // Memorize for later rounds
337 lua_pushvalue( L, -1); // DPC proxy metatable metatable
338 lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc
339 set_deep_lookup( L); // DPC proxy metatable
340
341 // 2 - cause the target state to require the module that exported the idfunc
342 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
343 {
344 int oldtop = lua_gettop( L);
345 modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
346 // make sure the function pushed nothing on the stack!
347 if( lua_gettop( L) - oldtop != 0)
348 {
349 lua_pop( L, 3); //
350 return "Bad idfunc(eOP_module): should not push anything";
351 }
352 }
353 if( modname) // we actually got a module name
354 {
355 // somehow, L.registry._LOADED can exist without having registered the 'package' library.
356 lua_getglobal( L, "require"); // DPC proxy metatable require()
357 // check that the module is already loaded (or being loaded, we are happy either way)
358 if( lua_isfunction( L, -1))
359 {
360 lua_pushstring( L, modname); // DPC proxy metatable require() "module"
361 lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED
362 if( lua_istable( L, -1))
363 {
364 bool_t alreadyloaded;
365 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
366 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
367 alreadyloaded = lua_toboolean( L, -1);
368 if( !alreadyloaded) // not loaded
369 {
370 int require_result;
371 lua_pop( L, 2); // DPC proxy metatable require() "module"
372 // require "modname"
373 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
374 if( require_result != LUA_OK)
375 {
376 // failed, return the error message
377 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
378 lua_insert( L, -2); // DPC proxy metatable prefix error
379 lua_concat( L, 2); // DPC proxy metatable error
380 return lua_tostring( L, -1);
381 }
382 }
383 else // already loaded, we are happy
384 {
385 lua_pop( L, 4); // DPC proxy metatable
386 }
387 }
388 else // no L.registry._LOADED; can this ever happen?
389 {
390 lua_pop( L, 6); //
391 return "unexpected error while requiring a module identified by idfunc(eOP_module)";
392 }
393 }
394 else // a module name, but no require() function :-(
395 {
396 lua_pop( L, 4); //
397 return "lanes receiving deep userdata should register the 'package' library";
398 }
399 }
400 }
401 STACK_MID( L, 2); // DPC proxy metatable
402 ASSERT_L( lua_isuserdata( L, -2));
403 ASSERT_L( lua_istable( L, -1));
404 lua_setmetatable( L, -2); // DPC proxy
405
406 // If we're here, we obviously had to create a new proxy, so cache it.
407 lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep
408 lua_pushvalue( L, -2); // DPC proxy deep proxy
409 lua_rawset( L, -4); // DPC proxy
410 lua_remove( L, -2); // proxy
411 ASSERT_L( lua_isuserdata( L, -1));
412 STACK_END( L, 0);
413 return NULL;
414}
415
416
417/*
418* Create a deep userdata
419*
420* proxy_ud= deep_userdata( idfunc [, ...] )
421*
422* Creates a deep userdata entry of the type defined by 'idfunc'.
423* Other parameters are passed on to the 'idfunc' "new" invocation.
424*
425* 'idfunc' must fulfill the following features:
426*
427* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance
428* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance
429* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies
430*
431* Reference counting and true userdata proxying are taken care of for the
432* actual data type.
433*
434* Types using the deep userdata system (and only those!) can be passed between
435* separate Lua states via 'luaG_inter_move()'.
436*
437* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
438*/
439int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc)
440{
441 char const* errmsg;
442 DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE));
443 if( prelude == NULL)
444 {
445 return luaL_error( L, "couldn't not allocate deep prelude: out of memory");
446 }
447
448 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
449 prelude->idfunc = idfunc;
450
451 STACK_GROW( L, 1);
452 STACK_CHECK( L);
453 {
454 int oldtop = lua_gettop( L);
455 prelude->deep = idfunc( L, eDO_new);
456 if( prelude->deep == NULL)
457 {
458 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
459 }
460
461 if( lua_gettop( L) - oldtop != 0)
462 {
463 luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
464 }
465 }
466 errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy
467 if( errmsg != NULL)
468 {
469 luaL_error( L, errmsg);
470 }
471 STACK_END( L, 1);
472 return 1;
473}
474
475
476/*
477* Access deep userdata through a proxy.
478*
479* Reference count is not changed, and access to the deep userdata is not
480* serialized. It is the module's responsibility to prevent conflicting usage.
481*/
482void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
483{
484 DEEP_PRELUDE** proxy;
485
486 STACK_CHECK( L);
487 // ensure it is actually a deep userdata
488 if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
489 {
490 return NULL; // no metatable, or wrong kind
491 }
492
493 proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
494 STACK_END( L, 0);
495
496 return (*proxy)->deep;
497}
498
499
500/*
501 * Copy deep userdata between two separate Lua states (from L to L2)
502 *
503 * Returns:
504 * the id function of the copied value, or NULL for non-deep userdata
505 * (not copied)
506 */
507luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_)
508{
509 char const* errmsg;
510 luaG_IdFunction idfunc = get_idfunc( L, index, mode_);
511 if( idfunc == NULL)
512 {
513 return NULL; // not a deep userdata
514 }
515
516 errmsg = push_deep_proxy( U, L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_);
517 if( errmsg != NULL)
518 {
519 // raise the error in the proper state (not the keeper)
520 lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
521 luaL_error( errL, errmsg);
522 }
523 return idfunc;
524}
diff --git a/src/deep.h b/src/deep.h
index e1f2c4f..8e999d6 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -27,5 +27,6 @@ typedef void* (*luaG_IdFunction)( lua_State* L, enum eDeepOp op_);
27 27
28extern LANES_API int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc); 28extern LANES_API int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc);
29extern LANES_API void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index); 29extern LANES_API void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index);
30extern LANES_API void luaG_pushdeepversion( lua_State* L);
30 31
31#endif // __LANES_DEEP_H__ 32#endif // __LANES_DEEP_H__
diff --git a/src/lanes.c b/src/lanes.c
index f45dac7..6acc711 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -52,7 +52,7 @@
52 * ... 52 * ...
53 */ 53 */
54 54
55char const* VERSION = "3.9.5"; 55char const* VERSION = "3.9.6";
56 56
57/* 57/*
58=============================================================================== 58===============================================================================
@@ -1248,7 +1248,8 @@ static void* linda_id( lua_State* L, enum eDeepOp op_)
1248 lua_pushlightuserdata( L, NIL_SENTINEL); 1248 lua_pushlightuserdata( L, NIL_SENTINEL);
1249 lua_setfield(L, -2, "null"); 1249 lua_setfield(L, -2, "null");
1250 1250
1251 STACK_END( L, 1); 1251 luaG_pushdeepversion( L);
1252 STACK_END( L, 2);
1252 return NULL; 1253 return NULL;
1253 } 1254 }
1254 1255
diff --git a/src/tools.c b/src/tools.c
index becf31e..628b277 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -44,6 +44,10 @@ THE SOFTWARE.
44#include <malloc.h> 44#include <malloc.h>
45#endif 45#endif
46 46
47// functions implemented in deep.c
48extern luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_);
49extern void push_registry_subtable( lua_State* L, void* key_);
50
47void* const UNIVERSE_REGKEY = (void*) luaopen_lanes_core; 51void* const UNIVERSE_REGKEY = (void*) luaopen_lanes_core;
48 52
49/* 53/*
@@ -708,477 +712,9 @@ lua_State* luaG_newstate( struct s_Universe* U, lua_State* from_, char const* li
708 712
709 713
710 714
711/*---=== Deep userdata ===---*/
712
713/* The deep portion must be allocated separately of any Lua state's; it's
714* lifespan may be longer than that of the creating state.
715*/
716#define DEEP_MALLOC malloc
717#define DEEP_FREE free
718
719/*
720* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
721* metatables:
722*
723* metatable -> idfunc
724* idfunc -> metatable
725*/
726#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
727 // any unique light userdata
728
729
730/*
731* The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying
732*/
733#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy)
734
735static void push_registry_subtable_mode( lua_State *L, void *token, const char* mode );
736static void push_registry_subtable( lua_State *L, void *token );
737
738/*
739* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
740* Pops the both values off the stack.
741*/
742static void set_deep_lookup( lua_State* L)
743{
744 STACK_GROW( L, 3);
745 STACK_CHECK( L); // a b
746 push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
747 STACK_MID( L, 1);
748 lua_insert( L, -3); // {} a b
749 lua_pushvalue( L, -1); // {} a b b
750 lua_pushvalue( L,-3); // {} a b b a
751 lua_rawset( L, -5); // {} a b
752 lua_rawset( L, -3); // {}
753 lua_pop( L, 1); //
754 STACK_END( L, -2);
755}
756
757/*
758* Pops the key (metatable or idfunc) off the stack, and replaces with the
759* deep lookup value (idfunc/metatable/nil).
760*/
761static void get_deep_lookup( lua_State* L)
762{
763 STACK_GROW( L, 1);
764 STACK_CHECK( L); // a
765 lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK
766 lua_rawget( L, LUA_REGISTRYINDEX); // a {}
767
768 if( !lua_isnil( L, -1))
769 {
770 lua_insert( L, -2); // {} a
771 lua_rawget( L, -2); // {} b
772 }
773 lua_remove( L, -2); // a|b
774 STACK_END( L, 0);
775}
776
777/*
778* Return the registered ID function for 'index' (deep userdata proxy),
779* or NULL if 'index' is not a deep userdata proxy.
780*/
781static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_)
782{
783 // when looking inside a keeper, we are 100% sure the object is a deep userdata
784 if( mode_ == eLM_FromKeeper)
785 {
786 DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
787 // we can (and must) cast and fetch the internally stored idfunc
788 return (*proxy)->idfunc;
789 }
790 else
791 {
792 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
793 // it is the only way to ensure that the userdata is indeed a deep userdata!
794 // of course, we could just trust the caller, but we won't
795 luaG_IdFunction ret;
796 STACK_GROW( L, 1);
797 STACK_CHECK( L);
798
799 if( !lua_getmetatable( L, index)) // deep ... metatable?
800 {
801 return NULL; // no metatable: can't be a deep userdata object!
802 }
803
804 // replace metatable with the idfunc pointer, if it is actually a deep userdata
805 get_deep_lookup( L); // deep ... idfunc|nil
806
807 ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
808 lua_pop( L, 1);
809 STACK_END( L, 0);
810 return ret;
811 }
812}
813
814
815void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_)
816{
817 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
818 lua_pushlightuserdata( L, prelude_->deep);
819 ASSERT_L( prelude_->idfunc);
820 prelude_->idfunc( L, eDO_delete);
821 DEEP_FREE( (void*) prelude_);
822}
823
824
825/*
826* void= mt.__gc( proxy_ud )
827*
828* End of life for a proxy object; reduce the deep reference count and clean
829* it up if reaches 0.
830*/
831static int deep_userdata_gc( lua_State* L)
832{
833 DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1);
834 DEEP_PRELUDE* p = *proxy;
835 struct s_Universe* U = get_universe( L);
836 int v;
837
838 *proxy = 0; // make sure we don't use it any more
839
840 MUTEX_LOCK( &U->deep_lock);
841 v = -- (p->refcount);
842 MUTEX_UNLOCK( &U->deep_lock);
843
844 if( v == 0)
845 {
846 // 'idfunc' expects a clean stack to work on
847 lua_settop( L, 0);
848 free_deep_prelude( L, p);
849
850 // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything!
851 if ( lua_gettop( L) > 1)
852 {
853 luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
854 }
855 }
856 return 0;
857}
858
859
860/*
861 * Push a proxy userdata on the stack.
862 * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem
863 * (error cannot happen with mode_ == eLM_ToKeeper)
864 *
865 * Initializes necessary structures if it's the first time 'idfunc' is being
866 * used in this Lua state (metatable, registring it). Otherwise, increments the
867 * reference count.
868 */
869char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_)
870{
871 DEEP_PRELUDE** proxy;
872
873 // Check if a proxy already exists
874 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
875 lua_pushlightuserdata( L, prelude->deep); // DPC deep
876 lua_rawget( L, -2); // DPC proxy
877 if ( !lua_isnil( L, -1))
878 {
879 lua_remove( L, -2); // proxy
880 return NULL;
881 }
882 else
883 {
884 lua_pop( L, 1); // DPC
885 }
886
887 MUTEX_LOCK( &U->deep_lock);
888 ++ (prelude->refcount); // one more proxy pointing to this deep data
889 MUTEX_UNLOCK( &U->deep_lock);
890
891 STACK_GROW( L, 7);
892 STACK_CHECK( L);
893
894 proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy
895 ASSERT_L( proxy);
896 *proxy = prelude;
897
898 // Get/create metatable for 'idfunc' (in this state)
899 lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc
900 get_deep_lookup( L); // DPC proxy metatable?
901
902 if( lua_isnil( L, -1)) // // No metatable yet.
903 {
904 char const* modname;
905 int oldtop = lua_gettop( L); // DPC proxy nil
906 lua_pop( L, 1); // DPC proxy
907 // 1 - make one and register it
908 if( mode_ != eLM_ToKeeper)
909 {
910 prelude->idfunc( L, eDO_metatable); // DPC proxy metatable
911 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1))
912 {
913 lua_pop( L, 3); //
914 return "Bad idfunc(eOP_metatable): unexpected pushed value";
915 }
916 // make sure the idfunc didn't export __gc, as we will store our own
917 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
918 if( !lua_isnil( L, -1))
919 {
920 lua_pop( L, 4); //
921 return "idfunc-created metatable shouldn't contain __gc";
922 }
923 lua_pop( L, 1); // DPC proxy metatable
924 }
925 else
926 {
927 // keepers need a minimal metatable that only contains __gc
928 lua_newtable( L); // DPC proxy metatable
929 }
930 // Add our own '__gc' method
931 lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc
932 lua_setfield( L, -2, "__gc"); // DPC proxy metatable
933
934 // Memorize for later rounds
935 lua_pushvalue( L, -1); // DPC proxy metatable metatable
936 lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc
937 set_deep_lookup( L); // DPC proxy metatable
938
939 // 2 - cause the target state to require the module that exported the idfunc
940 // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
941 {
942 int oldtop = lua_gettop( L);
943 modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
944 // make sure the function pushed nothing on the stack!
945 if( lua_gettop( L) - oldtop != 0)
946 {
947 lua_pop( L, 3); //
948 return "Bad idfunc(eOP_module): should not push anything";
949 }
950 }
951 if( modname) // we actually got a module name
952 {
953 // somehow, L.registry._LOADED can exist without having registered the 'package' library.
954 lua_getglobal( L, "require"); // DPC proxy metatable require()
955 // check that the module is already loaded (or being loaded, we are happy either way)
956 if( lua_isfunction( L, -1))
957 {
958 lua_pushstring( L, modname); // DPC proxy metatable require() "module"
959 lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED
960 if( lua_istable( L, -1))
961 {
962 bool_t alreadyloaded;
963 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
964 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
965 alreadyloaded = lua_toboolean( L, -1);
966 if( !alreadyloaded) // not loaded
967 {
968 int require_result;
969 lua_pop( L, 2); // DPC proxy metatable require() "module"
970 // require "modname"
971 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
972 if( require_result != LUA_OK)
973 {
974 // failed, return the error message
975 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
976 lua_insert( L, -2); // DPC proxy metatable prefix error
977 lua_concat( L, 2); // DPC proxy metatable error
978 return lua_tostring( L, -1);
979 }
980 }
981 else // already loaded, we are happy
982 {
983 lua_pop( L, 4); // DPC proxy metatable
984 }
985 }
986 else // no L.registry._LOADED; can this ever happen?
987 {
988 lua_pop( L, 6); //
989 return "unexpected error while requiring a module identified by idfunc(eOP_module)";
990 }
991 }
992 else // a module name, but no require() function :-(
993 {
994 lua_pop( L, 4); //
995 return "lanes receiving deep userdata should register the 'package' library";
996 }
997 }
998 }
999 STACK_MID( L, 2); // DPC proxy metatable
1000 ASSERT_L( lua_isuserdata( L, -2));
1001 ASSERT_L( lua_istable( L, -1));
1002 lua_setmetatable( L, -2); // DPC proxy
1003
1004 // If we're here, we obviously had to create a new proxy, so cache it.
1005 lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep
1006 lua_pushvalue( L, -2); // DPC proxy deep proxy
1007 lua_rawset( L, -4); // DPC proxy
1008 lua_remove( L, -2); // proxy
1009 ASSERT_L( lua_isuserdata( L, -1));
1010 STACK_END( L, 0);
1011 return NULL;
1012}
1013
1014
1015/*
1016* Create a deep userdata
1017*
1018* proxy_ud= deep_userdata( idfunc [, ...] )
1019*
1020* Creates a deep userdata entry of the type defined by 'idfunc'.
1021* Other parameters are passed on to the 'idfunc' "new" invocation.
1022*
1023* 'idfunc' must fulfill the following features:
1024*
1025* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance
1026* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance
1027* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies
1028*
1029* Reference counting and true userdata proxying are taken care of for the
1030* actual data type.
1031*
1032* Types using the deep userdata system (and only those!) can be passed between
1033* separate Lua states via 'luaG_inter_move()'.
1034*
1035* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
1036*/
1037int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc)
1038{
1039 char const* errmsg;
1040 DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE));
1041 if( prelude == NULL)
1042 {
1043 return luaL_error( L, "couldn't not allocate deep prelude: out of memory");
1044 }
1045
1046 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
1047 prelude->idfunc = idfunc;
1048
1049 STACK_GROW( L, 1);
1050 STACK_CHECK( L);
1051 {
1052 int oldtop = lua_gettop( L);
1053 prelude->deep = idfunc( L, eDO_new);
1054 if( prelude->deep == NULL)
1055 {
1056 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
1057 }
1058
1059 if( lua_gettop( L) - oldtop != 0)
1060 {
1061 luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
1062 }
1063 }
1064 errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy
1065 if( errmsg != NULL)
1066 {
1067 luaL_error( L, errmsg);
1068 }
1069 STACK_END( L, 1);
1070 return 1;
1071}
1072
1073
1074/*
1075* Access deep userdata through a proxy.
1076*
1077* Reference count is not changed, and access to the deep userdata is not
1078* serialized. It is the module's responsibility to prevent conflicting usage.
1079*/
1080void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
1081{
1082 DEEP_PRELUDE** proxy;
1083
1084 STACK_CHECK( L);
1085 // ensure it is actually a deep userdata
1086 if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
1087 {
1088 return NULL; // no metatable, or wrong kind
1089 }
1090
1091 proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
1092 STACK_END( L, 0);
1093
1094 return (*proxy)->deep;
1095}
1096
1097
1098/*
1099 * Copy deep userdata between two separate Lua states (from L to L2)
1100 *
1101 * Returns:
1102 * the id function of the copied value, or NULL for non-deep userdata
1103 * (not copied)
1104 */
1105static luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_)
1106{
1107 char const* errmsg;
1108 luaG_IdFunction idfunc = get_idfunc( L, index, mode_);
1109 if( idfunc == NULL)
1110 {
1111 return NULL; // not a deep userdata
1112 }
1113
1114 errmsg = push_deep_proxy( U, L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_);
1115 if( errmsg != NULL)
1116 {
1117 // raise the error in the proper state (not the keeper)
1118 lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
1119 luaL_error( errL, errmsg);
1120 }
1121 return idfunc;
1122}
1123
1124 715
1125/*---=== Inter-state copying ===---*/ 716/*---=== Inter-state copying ===---*/
1126 717
1127/*-- Metatable copying --*/
1128
1129/*
1130 * 'reg[ REG_MT_KNOWN ]'= {
1131 * [ table ]= id_uint,
1132 * ...
1133 * [ id_uint ]= table,
1134 * ...
1135 * }
1136 */
1137
1138/*
1139* Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
1140*/
1141static void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_)
1142{
1143 STACK_GROW( L, 3);
1144 STACK_CHECK( L);
1145
1146 lua_pushlightuserdata( L, key_); // key
1147 lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil
1148
1149 if( lua_isnil( L, -1))
1150 {
1151 lua_pop( L, 1); //
1152 lua_newtable( L); // {}
1153 lua_pushlightuserdata( L, key_); // {} key
1154 lua_pushvalue( L, -2); // {} key {}
1155
1156 // _R[key_] = {}
1157 lua_rawset( L, LUA_REGISTRYINDEX); // {}
1158
1159 // Set its metatable if requested
1160 if( mode_)
1161 {
1162 lua_newtable( L); // {} mt
1163 lua_pushliteral( L, "__mode"); // {} mt "__mode"
1164 lua_pushstring( L, mode_); // {} mt "__mode" mode
1165 lua_rawset( L, -3); // {} mt
1166 lua_setmetatable( L, -2); // {}
1167 }
1168 }
1169 STACK_END( L, 1);
1170 ASSERT_L( lua_istable( L, -1));
1171}
1172
1173/*
1174* Push a registry subtable (keyed by unique 'key_') onto the stack.
1175* If the subtable does not exist, it is created and chained.
1176*/
1177static inline void push_registry_subtable( lua_State* L, void* key_)
1178{
1179 push_registry_subtable_mode( L, key_, NULL);
1180}
1181
1182#define REG_MTID ( (void*) get_mt_id ) 718#define REG_MTID ( (void*) get_mt_id )
1183 719
1184/* 720/*