From 77630de350fc89038378c798cd482ed751280fc2 Mon Sep 17 00:00:00 2001
From: Benoit Germain
Date: Tue, 17 Jun 2014 16:34:31 +0200
Subject: 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()
---
CHANGES | 5 +
CMakeLists.txt | 2 +-
docs/index.html | 6 +-
lanes-3.9.4-1.rockspec | 66 -------
lanes-3.9.6-1.rockspec | 66 +++++++
src/Makefile | 2 +-
src/deep.c | 524 +++++++++++++++++++++++++++++++++++++++++++++++++
src/deep.h | 1 +
src/lanes.c | 5 +-
src/tools.c | 472 +-------------------------------------------
10 files changed, 608 insertions(+), 541 deletions(-)
delete mode 100644 lanes-3.9.4-1.rockspec
create mode 100644 lanes-3.9.6-1.rockspec
create mode 100644 src/deep.c
diff --git a/CHANGES b/CHANGES
index 31599c4..3d8015d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,10 @@
CHANGES:
+CHANGE 113: BGe 17-Jun-14
+ * 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()
+
CHANGE 112 BGe 16-May-14
* bumped version to 3.9.5
* 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)
# Build
INCLUDE_DIRECTORIES(src)
-ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c src/keeper.c src/compat.c)
+ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/deep.c src/threading.c src/tools.c src/keeper.c src/compat.c)
IF(UNIX AND NOT CYGWIN)
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 @@
- This document was revised on 20-Mar-14, and applies to version 3.9.4.
+ This document was revised on 17-Jun-14, and applies to version 3.9.6.
@@ -1511,12 +1511,12 @@ events to a common Linda, but... :).
- eDO_new: requests the creation of a new object, whose pointer is returned.
- eDO_delete: receives this same pointer on the stack as a light userdata, and should cleanup the object.
- - eDO_metatable: should build a metatable for the object. Don't cache the metatable yourself, Lanes takes care of it (eDO_metatable should only be invoked once per state).
+ - eDO_metatable: should build a metatable for the object. Don't cache the metatable yourself, Lanes takes care of it (eDO_metatable should only be invoked once per state). Push the metatable on the stack, then call luaG_pushdeepversion() before returning (new in version 3.9.5).
- eDO_module: 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.
Take a look at linda_id in lanes.c.
- Include "deep.h" and link against Lanes.
+ Include "deep.h" 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.
Instanciate your userdata using luaG_newdeepuserdata(), instead of the regular lua_newuserdata(). Given an idfunc, 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.
Accessing the deep userdata from your C code, use luaG_todeep() instead of the regular lua_touserdata().
diff --git a/lanes-3.9.4-1.rockspec b/lanes-3.9.4-1.rockspec
deleted file mode 100644
index f5f7e83..0000000
--- a/lanes-3.9.4-1.rockspec
+++ /dev/null
@@ -1,66 +0,0 @@
---
--- Lanes rockspec
---
--- Ref:
---
---
-
-package = "Lanes"
-
-version = "3.9.4-1"
-
-source= {
- url= "git://github.com/LuaLanes/lanes.git",
- branch= "v3.9.4"
-}
-
-description = {
- summary= "Multithreading support for Lua",
- detailed= [[
- Lua Lanes is a portable, message passing multithreading library
- providing the possibility to run multiple Lua states in parallel.
- ]],
- license= "MIT/X11",
- homepage="https://github.com/LuaLanes/lanes",
- maintainer="Benoit Germain "
-}
-
--- Q: What is the difference of "windows" and "win32"? Seems there is none;
--- so should we list either one or both?
---
-supported_platforms= { "win32",
- "macosx",
- "linux",
- "freebsd", -- TBD: not tested
- "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?)
-}
-
-dependencies= {
- "lua >= 5.1", -- builds with either 5.1 and 5.2
-}
-
-build = {
- type = "builtin",
- platforms =
- {
- linux =
- {
- modules =
- {
- ["lanes.core"] =
- {
- libraries = "pthread"
- },
- }
- }
- },
- modules =
- {
- ["lanes.core"] =
- {
- sources = { "src/compat.c", "src/lanes.c", "src/keeper.c", "src/tools.c", "src/threading.c"},
- incdirs = { "src"},
- },
- lanes = "src/lanes.lua"
- }
-}
diff --git a/lanes-3.9.6-1.rockspec b/lanes-3.9.6-1.rockspec
new file mode 100644
index 0000000..3435f19
--- /dev/null
+++ b/lanes-3.9.6-1.rockspec
@@ -0,0 +1,66 @@
+--
+-- Lanes rockspec
+--
+-- Ref:
+--
+--
+
+package = "Lanes"
+
+version = "3.9.6-1"
+
+source= {
+ url= "git://github.com/LuaLanes/lanes.git",
+ branch= "v3.9.6"
+}
+
+description = {
+ summary= "Multithreading support for Lua",
+ detailed= [[
+ Lua Lanes is a portable, message passing multithreading library
+ providing the possibility to run multiple Lua states in parallel.
+ ]],
+ license= "MIT/X11",
+ homepage="https://github.com/LuaLanes/lanes",
+ maintainer="Benoit Germain "
+}
+
+-- Q: What is the difference of "windows" and "win32"? Seems there is none;
+-- so should we list either one or both?
+--
+supported_platforms= { "win32",
+ "macosx",
+ "linux",
+ "freebsd", -- TBD: not tested
+ "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?)
+}
+
+dependencies= {
+ "lua >= 5.1", -- builds with either 5.1 and 5.2
+}
+
+build = {
+ type = "builtin",
+ platforms =
+ {
+ linux =
+ {
+ modules =
+ {
+ ["lanes.core"] =
+ {
+ libraries = "pthread"
+ },
+ }
+ }
+ },
+ modules =
+ {
+ ["lanes.core"] =
+ {
+ sources = { "src/compat.c", "src/deep.c", "src/lanes.c", "src/keeper.c", "src/tools.c", "src/threading.c"},
+ incdirs = { "src"},
+ },
+ 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 @@
MODULE=lanes
-SRC=lanes.c compat.c threading.c tools.c keeper.c
+SRC=lanes.c compat.c threading.c tools.c deep.c keeper.c
OBJ=$(SRC:.c=.o)
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 @@
+/*
+ * DEEP.C Copyright (c) 2014, Benoit Germain
+ *
+ * Depp userdata support, separate in its own source file to help integration
+ * without enforcing a Lanes dependency
+ */
+
+/*
+===============================================================================
+
+Copyright (C) 2002-10 Asko Kauppi
+ 2011-14 Benoit Germain
+
+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.
+
+===============================================================================
+*/
+
+#include "compat.h"
+#include "tools.h"
+#include "deep.h"
+
+#include
+#include
+#include
+#include
+#if !defined(__APPLE__)
+#include
+#endif
+
+/*-- Metatable copying --*/
+
+/*
+ * 'reg[ REG_MT_KNOWN ]'= {
+ * [ table ]= id_uint,
+ * ...
+ * [ id_uint ]= table,
+ * ...
+ * }
+ */
+
+/*
+* Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
+*/
+void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_)
+{
+ STACK_GROW( L, 3);
+ STACK_CHECK( L);
+
+ lua_pushlightuserdata( L, key_); // key
+ lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil
+
+ if( lua_isnil( L, -1))
+ {
+ lua_pop( L, 1); //
+ lua_newtable( L); // {}
+ lua_pushlightuserdata( L, key_); // {} key
+ lua_pushvalue( L, -2); // {} key {}
+
+ // _R[key_] = {}
+ lua_rawset( L, LUA_REGISTRYINDEX); // {}
+
+ // Set its metatable if requested
+ if( mode_)
+ {
+ lua_newtable( L); // {} mt
+ lua_pushliteral( L, "__mode"); // {} mt "__mode"
+ lua_pushstring( L, mode_); // {} mt "__mode" mode
+ lua_rawset( L, -3); // {} mt
+ lua_setmetatable( L, -2); // {}
+ }
+ }
+ STACK_END( L, 1);
+ ASSERT_L( lua_istable( L, -1));
+}
+
+
+/*
+* Push a registry subtable (keyed by unique 'key_') onto the stack.
+* If the subtable does not exist, it is created and chained.
+*/
+void push_registry_subtable( lua_State* L, void* key_)
+{
+ push_registry_subtable_mode( L, key_, NULL);
+}
+
+
+/*---=== Deep userdata ===---*/
+
+void luaG_pushdeepversion( lua_State* L) { (void) lua_pushliteral( L, "f248e77a-a84d-44b5-9ad0-96c05679b885");}
+
+
+
+/* The deep portion must be allocated separately of any Lua state's; it's
+* lifespan may be longer than that of the creating state.
+*/
+#define DEEP_MALLOC malloc
+#define DEEP_FREE free
+
+/*
+* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
+* metatables:
+*
+* metatable -> idfunc
+* idfunc -> metatable
+*/
+#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
+ // any unique light userdata
+
+
+/*
+* The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying
+*/
+#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy)
+
+/*
+* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
+* Pops the both values off the stack.
+*/
+static void set_deep_lookup( lua_State* L)
+{
+ STACK_GROW( L, 3);
+ STACK_CHECK( L); // a b
+ push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
+ STACK_MID( L, 1);
+ lua_insert( L, -3); // {} a b
+ lua_pushvalue( L, -1); // {} a b b
+ lua_pushvalue( L,-3); // {} a b b a
+ lua_rawset( L, -5); // {} a b
+ lua_rawset( L, -3); // {}
+ lua_pop( L, 1); //
+ STACK_END( L, -2);
+}
+
+/*
+* Pops the key (metatable or idfunc) off the stack, and replaces with the
+* deep lookup value (idfunc/metatable/nil).
+*/
+static void get_deep_lookup( lua_State* L)
+{
+ STACK_GROW( L, 1);
+ STACK_CHECK( L); // a
+ lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK
+ lua_rawget( L, LUA_REGISTRYINDEX); // a {}
+
+ if( !lua_isnil( L, -1))
+ {
+ lua_insert( L, -2); // {} a
+ lua_rawget( L, -2); // {} b
+ }
+ lua_remove( L, -2); // a|b
+ STACK_END( L, 0);
+}
+
+/*
+* Return the registered ID function for 'index' (deep userdata proxy),
+* or NULL if 'index' is not a deep userdata proxy.
+*/
+static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_)
+{
+ // when looking inside a keeper, we are 100% sure the object is a deep userdata
+ if( mode_ == eLM_FromKeeper)
+ {
+ DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
+ // we can (and must) cast and fetch the internally stored idfunc
+ return (*proxy)->idfunc;
+ }
+ else
+ {
+ // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
+ // it is the only way to ensure that the userdata is indeed a deep userdata!
+ // of course, we could just trust the caller, but we won't
+ luaG_IdFunction ret;
+ STACK_GROW( L, 1);
+ STACK_CHECK( L);
+
+ if( !lua_getmetatable( L, index)) // deep ... metatable?
+ {
+ return NULL; // no metatable: can't be a deep userdata object!
+ }
+
+ // replace metatable with the idfunc pointer, if it is actually a deep userdata
+ get_deep_lookup( L); // deep ... idfunc|nil
+
+ ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
+ lua_pop( L, 1);
+ STACK_END( L, 0);
+ return ret;
+ }
+}
+
+
+void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_)
+{
+ // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
+ lua_pushlightuserdata( L, prelude_->deep);
+ ASSERT_L( prelude_->idfunc);
+ prelude_->idfunc( L, eDO_delete);
+ DEEP_FREE( (void*) prelude_);
+}
+
+
+/*
+* void= mt.__gc( proxy_ud )
+*
+* End of life for a proxy object; reduce the deep reference count and clean
+* it up if reaches 0.
+*/
+static int deep_userdata_gc( lua_State* L)
+{
+ DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1);
+ DEEP_PRELUDE* p = *proxy;
+ struct s_Universe* U = get_universe( L);
+ int v;
+
+ *proxy = 0; // make sure we don't use it any more
+
+ MUTEX_LOCK( &U->deep_lock);
+ v = -- (p->refcount);
+ MUTEX_UNLOCK( &U->deep_lock);
+
+ if( v == 0)
+ {
+ // 'idfunc' expects a clean stack to work on
+ lua_settop( L, 0);
+ free_deep_prelude( L, p);
+
+ // 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!
+ if ( lua_gettop( L) > 1)
+ {
+ luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Push a proxy userdata on the stack.
+ * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem
+ * (error cannot happen with mode_ == eLM_ToKeeper)
+ *
+ * Initializes necessary structures if it's the first time 'idfunc' is being
+ * used in this Lua state (metatable, registring it). Otherwise, increments the
+ * reference count.
+ */
+char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_)
+{
+ DEEP_PRELUDE** proxy;
+
+ // Check if a proxy already exists
+ push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
+ lua_pushlightuserdata( L, prelude->deep); // DPC deep
+ lua_rawget( L, -2); // DPC proxy
+ if ( !lua_isnil( L, -1))
+ {
+ lua_remove( L, -2); // proxy
+ return NULL;
+ }
+ else
+ {
+ lua_pop( L, 1); // DPC
+ }
+
+ MUTEX_LOCK( &U->deep_lock);
+ ++ (prelude->refcount); // one more proxy pointing to this deep data
+ MUTEX_UNLOCK( &U->deep_lock);
+
+ STACK_GROW( L, 7);
+ STACK_CHECK( L);
+
+ proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy
+ ASSERT_L( proxy);
+ *proxy = prelude;
+
+ // Get/create metatable for 'idfunc' (in this state)
+ lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc
+ get_deep_lookup( L); // DPC proxy metatable?
+
+ if( lua_isnil( L, -1)) // // No metatable yet.
+ {
+ char const* modname;
+ int oldtop = lua_gettop( L); // DPC proxy nil
+ lua_pop( L, 1); // DPC proxy
+ // 1 - make one and register it
+ if( mode_ != eLM_ToKeeper)
+ {
+ prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion
+ if( lua_gettop( L) - oldtop != 1 || !lua_istable( L, -2) || !lua_isstring( L, -1))
+ {
+ lua_settop( L, oldtop); // DPC proxy X
+ lua_pop( L, 3); //
+ return "Bad idfunc(eOP_metatable): unexpected pushed value";
+ }
+ luaG_pushdeepversion( L); // DPC proxy metatable deepversion deepversion
+ if( !lua_equal( L, -1, -2))
+ {
+ lua_pop( L, 5); //
+ return "Bad idfunc(eOP_metatable): mismatched deep version";
+ }
+ lua_pop( L, 2); // DPC proxy metatable
+ // make sure the idfunc didn't export __gc, as we will store our own
+ lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
+ if( !lua_isnil( L, -1))
+ {
+ lua_pop( L, 4); //
+ return "idfunc-created metatable shouldn't contain __gc";
+ }
+ lua_pop( L, 1); // DPC proxy metatable
+ }
+ else
+ {
+ // keepers need a minimal metatable that only contains __gc
+ lua_newtable( L); // DPC proxy metatable
+ }
+ // Add our own '__gc' method
+ lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc
+ lua_setfield( L, -2, "__gc"); // DPC proxy metatable
+
+ // Memorize for later rounds
+ lua_pushvalue( L, -1); // DPC proxy metatable metatable
+ lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc
+ set_deep_lookup( L); // DPC proxy metatable
+
+ // 2 - cause the target state to require the module that exported the idfunc
+ // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
+ {
+ int oldtop = lua_gettop( L);
+ modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
+ // make sure the function pushed nothing on the stack!
+ if( lua_gettop( L) - oldtop != 0)
+ {
+ lua_pop( L, 3); //
+ return "Bad idfunc(eOP_module): should not push anything";
+ }
+ }
+ if( modname) // we actually got a module name
+ {
+ // somehow, L.registry._LOADED can exist without having registered the 'package' library.
+ lua_getglobal( L, "require"); // DPC proxy metatable require()
+ // check that the module is already loaded (or being loaded, we are happy either way)
+ if( lua_isfunction( L, -1))
+ {
+ lua_pushstring( L, modname); // DPC proxy metatable require() "module"
+ lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED
+ if( lua_istable( L, -1))
+ {
+ bool_t alreadyloaded;
+ lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
+ lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
+ alreadyloaded = lua_toboolean( L, -1);
+ if( !alreadyloaded) // not loaded
+ {
+ int require_result;
+ lua_pop( L, 2); // DPC proxy metatable require() "module"
+ // require "modname"
+ require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
+ if( require_result != LUA_OK)
+ {
+ // failed, return the error message
+ lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
+ lua_insert( L, -2); // DPC proxy metatable prefix error
+ lua_concat( L, 2); // DPC proxy metatable error
+ return lua_tostring( L, -1);
+ }
+ }
+ else // already loaded, we are happy
+ {
+ lua_pop( L, 4); // DPC proxy metatable
+ }
+ }
+ else // no L.registry._LOADED; can this ever happen?
+ {
+ lua_pop( L, 6); //
+ return "unexpected error while requiring a module identified by idfunc(eOP_module)";
+ }
+ }
+ else // a module name, but no require() function :-(
+ {
+ lua_pop( L, 4); //
+ return "lanes receiving deep userdata should register the 'package' library";
+ }
+ }
+ }
+ STACK_MID( L, 2); // DPC proxy metatable
+ ASSERT_L( lua_isuserdata( L, -2));
+ ASSERT_L( lua_istable( L, -1));
+ lua_setmetatable( L, -2); // DPC proxy
+
+ // If we're here, we obviously had to create a new proxy, so cache it.
+ lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep
+ lua_pushvalue( L, -2); // DPC proxy deep proxy
+ lua_rawset( L, -4); // DPC proxy
+ lua_remove( L, -2); // proxy
+ ASSERT_L( lua_isuserdata( L, -1));
+ STACK_END( L, 0);
+ return NULL;
+}
+
+
+/*
+* Create a deep userdata
+*
+* proxy_ud= deep_userdata( idfunc [, ...] )
+*
+* Creates a deep userdata entry of the type defined by 'idfunc'.
+* Other parameters are passed on to the 'idfunc' "new" invocation.
+*
+* 'idfunc' must fulfill the following features:
+*
+* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance
+* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance
+* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies
+*
+* Reference counting and true userdata proxying are taken care of for the
+* actual data type.
+*
+* Types using the deep userdata system (and only those!) can be passed between
+* separate Lua states via 'luaG_inter_move()'.
+*
+* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
+*/
+int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc)
+{
+ char const* errmsg;
+ DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE));
+ if( prelude == NULL)
+ {
+ return luaL_error( L, "couldn't not allocate deep prelude: out of memory");
+ }
+
+ prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
+ prelude->idfunc = idfunc;
+
+ STACK_GROW( L, 1);
+ STACK_CHECK( L);
+ {
+ int oldtop = lua_gettop( L);
+ prelude->deep = idfunc( L, eDO_new);
+ if( prelude->deep == NULL)
+ {
+ luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
+ }
+
+ if( lua_gettop( L) - oldtop != 0)
+ {
+ luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
+ }
+ }
+ errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy
+ if( errmsg != NULL)
+ {
+ luaL_error( L, errmsg);
+ }
+ STACK_END( L, 1);
+ return 1;
+}
+
+
+/*
+* Access deep userdata through a proxy.
+*
+* Reference count is not changed, and access to the deep userdata is not
+* serialized. It is the module's responsibility to prevent conflicting usage.
+*/
+void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
+{
+ DEEP_PRELUDE** proxy;
+
+ STACK_CHECK( L);
+ // ensure it is actually a deep userdata
+ if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
+ {
+ return NULL; // no metatable, or wrong kind
+ }
+
+ proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
+ STACK_END( L, 0);
+
+ return (*proxy)->deep;
+}
+
+
+/*
+ * Copy deep userdata between two separate Lua states (from L to L2)
+ *
+ * Returns:
+ * the id function of the copied value, or NULL for non-deep userdata
+ * (not copied)
+ */
+luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_)
+{
+ char const* errmsg;
+ luaG_IdFunction idfunc = get_idfunc( L, index, mode_);
+ if( idfunc == NULL)
+ {
+ return NULL; // not a deep userdata
+ }
+
+ errmsg = push_deep_proxy( U, L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_);
+ if( errmsg != NULL)
+ {
+ // raise the error in the proper state (not the keeper)
+ lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
+ luaL_error( errL, errmsg);
+ }
+ return idfunc;
+}
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_);
extern LANES_API int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc);
extern LANES_API void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index);
+extern LANES_API void luaG_pushdeepversion( lua_State* L);
#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 @@
* ...
*/
-char const* VERSION = "3.9.5";
+char const* VERSION = "3.9.6";
/*
===============================================================================
@@ -1248,7 +1248,8 @@ static void* linda_id( lua_State* L, enum eDeepOp op_)
lua_pushlightuserdata( L, NIL_SENTINEL);
lua_setfield(L, -2, "null");
- STACK_END( L, 1);
+ luaG_pushdeepversion( L);
+ STACK_END( L, 2);
return NULL;
}
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.
#include
#endif
+// functions implemented in deep.c
+extern luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_);
+extern void push_registry_subtable( lua_State* L, void* key_);
+
void* const UNIVERSE_REGKEY = (void*) luaopen_lanes_core;
/*
@@ -708,477 +712,9 @@ lua_State* luaG_newstate( struct s_Universe* U, lua_State* from_, char const* li
-/*---=== Deep userdata ===---*/
-
-/* The deep portion must be allocated separately of any Lua state's; it's
-* lifespan may be longer than that of the creating state.
-*/
-#define DEEP_MALLOC malloc
-#define DEEP_FREE free
-
-/*
-* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
-* metatables:
-*
-* metatable -> idfunc
-* idfunc -> metatable
-*/
-#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
- // any unique light userdata
-
-
-/*
-* The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying
-*/
-#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy)
-
-static void push_registry_subtable_mode( lua_State *L, void *token, const char* mode );
-static void push_registry_subtable( lua_State *L, void *token );
-
-/*
-* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
-* Pops the both values off the stack.
-*/
-static void set_deep_lookup( lua_State* L)
-{
- STACK_GROW( L, 3);
- STACK_CHECK( L); // a b
- push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {}
- STACK_MID( L, 1);
- lua_insert( L, -3); // {} a b
- lua_pushvalue( L, -1); // {} a b b
- lua_pushvalue( L,-3); // {} a b b a
- lua_rawset( L, -5); // {} a b
- lua_rawset( L, -3); // {}
- lua_pop( L, 1); //
- STACK_END( L, -2);
-}
-
-/*
-* Pops the key (metatable or idfunc) off the stack, and replaces with the
-* deep lookup value (idfunc/metatable/nil).
-*/
-static void get_deep_lookup( lua_State* L)
-{
- STACK_GROW( L, 1);
- STACK_CHECK( L); // a
- lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK
- lua_rawget( L, LUA_REGISTRYINDEX); // a {}
-
- if( !lua_isnil( L, -1))
- {
- lua_insert( L, -2); // {} a
- lua_rawget( L, -2); // {} b
- }
- lua_remove( L, -2); // a|b
- STACK_END( L, 0);
-}
-
-/*
-* Return the registered ID function for 'index' (deep userdata proxy),
-* or NULL if 'index' is not a deep userdata proxy.
-*/
-static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_)
-{
- // when looking inside a keeper, we are 100% sure the object is a deep userdata
- if( mode_ == eLM_FromKeeper)
- {
- DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
- // we can (and must) cast and fetch the internally stored idfunc
- return (*proxy)->idfunc;
- }
- else
- {
- // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database
- // it is the only way to ensure that the userdata is indeed a deep userdata!
- // of course, we could just trust the caller, but we won't
- luaG_IdFunction ret;
- STACK_GROW( L, 1);
- STACK_CHECK( L);
-
- if( !lua_getmetatable( L, index)) // deep ... metatable?
- {
- return NULL; // no metatable: can't be a deep userdata object!
- }
-
- // replace metatable with the idfunc pointer, if it is actually a deep userdata
- get_deep_lookup( L); // deep ... idfunc|nil
-
- ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata
- lua_pop( L, 1);
- STACK_END( L, 0);
- return ret;
- }
-}
-
-
-void free_deep_prelude( lua_State* L, DEEP_PRELUDE* prelude_)
-{
- // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
- lua_pushlightuserdata( L, prelude_->deep);
- ASSERT_L( prelude_->idfunc);
- prelude_->idfunc( L, eDO_delete);
- DEEP_FREE( (void*) prelude_);
-}
-
-
-/*
-* void= mt.__gc( proxy_ud )
-*
-* End of life for a proxy object; reduce the deep reference count and clean
-* it up if reaches 0.
-*/
-static int deep_userdata_gc( lua_State* L)
-{
- DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1);
- DEEP_PRELUDE* p = *proxy;
- struct s_Universe* U = get_universe( L);
- int v;
-
- *proxy = 0; // make sure we don't use it any more
-
- MUTEX_LOCK( &U->deep_lock);
- v = -- (p->refcount);
- MUTEX_UNLOCK( &U->deep_lock);
-
- if( v == 0)
- {
- // 'idfunc' expects a clean stack to work on
- lua_settop( L, 0);
- free_deep_prelude( L, p);
-
- // 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!
- if ( lua_gettop( L) > 1)
- {
- luaL_error( L, "Bad idfunc(eDO_delete): should not push anything");
- }
- }
- return 0;
-}
-
-
-/*
- * Push a proxy userdata on the stack.
- * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem
- * (error cannot happen with mode_ == eLM_ToKeeper)
- *
- * Initializes necessary structures if it's the first time 'idfunc' is being
- * used in this Lua state (metatable, registring it). Otherwise, increments the
- * reference count.
- */
-char const* push_deep_proxy( struct s_Universe* U, lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_)
-{
- DEEP_PRELUDE** proxy;
-
- // Check if a proxy already exists
- push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
- lua_pushlightuserdata( L, prelude->deep); // DPC deep
- lua_rawget( L, -2); // DPC proxy
- if ( !lua_isnil( L, -1))
- {
- lua_remove( L, -2); // proxy
- return NULL;
- }
- else
- {
- lua_pop( L, 1); // DPC
- }
-
- MUTEX_LOCK( &U->deep_lock);
- ++ (prelude->refcount); // one more proxy pointing to this deep data
- MUTEX_UNLOCK( &U->deep_lock);
-
- STACK_GROW( L, 7);
- STACK_CHECK( L);
-
- proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy
- ASSERT_L( proxy);
- *proxy = prelude;
-
- // Get/create metatable for 'idfunc' (in this state)
- lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc
- get_deep_lookup( L); // DPC proxy metatable?
-
- if( lua_isnil( L, -1)) // // No metatable yet.
- {
- char const* modname;
- int oldtop = lua_gettop( L); // DPC proxy nil
- lua_pop( L, 1); // DPC proxy
- // 1 - make one and register it
- if( mode_ != eLM_ToKeeper)
- {
- prelude->idfunc( L, eDO_metatable); // DPC proxy metatable
- if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1))
- {
- lua_pop( L, 3); //
- return "Bad idfunc(eOP_metatable): unexpected pushed value";
- }
- // make sure the idfunc didn't export __gc, as we will store our own
- lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
- if( !lua_isnil( L, -1))
- {
- lua_pop( L, 4); //
- return "idfunc-created metatable shouldn't contain __gc";
- }
- lua_pop( L, 1); // DPC proxy metatable
- }
- else
- {
- // keepers need a minimal metatable that only contains __gc
- lua_newtable( L); // DPC proxy metatable
- }
- // Add our own '__gc' method
- lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc
- lua_setfield( L, -2, "__gc"); // DPC proxy metatable
-
- // Memorize for later rounds
- lua_pushvalue( L, -1); // DPC proxy metatable metatable
- lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc
- set_deep_lookup( L); // DPC proxy metatable
-
- // 2 - cause the target state to require the module that exported the idfunc
- // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc
- {
- int oldtop = lua_gettop( L);
- modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable
- // make sure the function pushed nothing on the stack!
- if( lua_gettop( L) - oldtop != 0)
- {
- lua_pop( L, 3); //
- return "Bad idfunc(eOP_module): should not push anything";
- }
- }
- if( modname) // we actually got a module name
- {
- // somehow, L.registry._LOADED can exist without having registered the 'package' library.
- lua_getglobal( L, "require"); // DPC proxy metatable require()
- // check that the module is already loaded (or being loaded, we are happy either way)
- if( lua_isfunction( L, -1))
- {
- lua_pushstring( L, modname); // DPC proxy metatable require() "module"
- lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED
- if( lua_istable( L, -1))
- {
- bool_t alreadyloaded;
- lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
- lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
- alreadyloaded = lua_toboolean( L, -1);
- if( !alreadyloaded) // not loaded
- {
- int require_result;
- lua_pop( L, 2); // DPC proxy metatable require() "module"
- // require "modname"
- require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
- if( require_result != LUA_OK)
- {
- // failed, return the error message
- lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname);
- lua_insert( L, -2); // DPC proxy metatable prefix error
- lua_concat( L, 2); // DPC proxy metatable error
- return lua_tostring( L, -1);
- }
- }
- else // already loaded, we are happy
- {
- lua_pop( L, 4); // DPC proxy metatable
- }
- }
- else // no L.registry._LOADED; can this ever happen?
- {
- lua_pop( L, 6); //
- return "unexpected error while requiring a module identified by idfunc(eOP_module)";
- }
- }
- else // a module name, but no require() function :-(
- {
- lua_pop( L, 4); //
- return "lanes receiving deep userdata should register the 'package' library";
- }
- }
- }
- STACK_MID( L, 2); // DPC proxy metatable
- ASSERT_L( lua_isuserdata( L, -2));
- ASSERT_L( lua_istable( L, -1));
- lua_setmetatable( L, -2); // DPC proxy
-
- // If we're here, we obviously had to create a new proxy, so cache it.
- lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep
- lua_pushvalue( L, -2); // DPC proxy deep proxy
- lua_rawset( L, -4); // DPC proxy
- lua_remove( L, -2); // proxy
- ASSERT_L( lua_isuserdata( L, -1));
- STACK_END( L, 0);
- return NULL;
-}
-
-
-/*
-* Create a deep userdata
-*
-* proxy_ud= deep_userdata( idfunc [, ...] )
-*
-* Creates a deep userdata entry of the type defined by 'idfunc'.
-* Other parameters are passed on to the 'idfunc' "new" invocation.
-*
-* 'idfunc' must fulfill the following features:
-*
-* lightuserdata = idfunc( eDO_new [, ...] ) -- creates a new deep data instance
-* void = idfunc( eDO_delete, lightuserdata ) -- releases a deep data instance
-* tbl = idfunc( eDO_metatable ) -- gives metatable for userdata proxies
-*
-* Reference counting and true userdata proxying are taken care of for the
-* actual data type.
-*
-* Types using the deep userdata system (and only those!) can be passed between
-* separate Lua states via 'luaG_inter_move()'.
-*
-* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()'
-*/
-int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc)
-{
- char const* errmsg;
- DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE));
- if( prelude == NULL)
- {
- return luaL_error( L, "couldn't not allocate deep prelude: out of memory");
- }
-
- prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
- prelude->idfunc = idfunc;
-
- STACK_GROW( L, 1);
- STACK_CHECK( L);
- {
- int oldtop = lua_gettop( L);
- prelude->deep = idfunc( L, eDO_new);
- if( prelude->deep == NULL)
- {
- luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
- }
-
- if( lua_gettop( L) - oldtop != 0)
- {
- luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
- }
- }
- errmsg = push_deep_proxy( get_universe( L), L, prelude, eLM_LaneBody); // proxy
- if( errmsg != NULL)
- {
- luaL_error( L, errmsg);
- }
- STACK_END( L, 1);
- return 1;
-}
-
-
-/*
-* Access deep userdata through a proxy.
-*
-* Reference count is not changed, and access to the deep userdata is not
-* serialized. It is the module's responsibility to prevent conflicting usage.
-*/
-void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
-{
- DEEP_PRELUDE** proxy;
-
- STACK_CHECK( L);
- // ensure it is actually a deep userdata
- if( get_idfunc( L, index, eLM_LaneBody) != idfunc)
- {
- return NULL; // no metatable, or wrong kind
- }
-
- proxy = (DEEP_PRELUDE**) lua_touserdata( L, index);
- STACK_END( L, 0);
-
- return (*proxy)->deep;
-}
-
-
-/*
- * Copy deep userdata between two separate Lua states (from L to L2)
- *
- * Returns:
- * the id function of the copied value, or NULL for non-deep userdata
- * (not copied)
- */
-static luaG_IdFunction copydeep( struct s_Universe* U, lua_State* L, lua_State* L2, int index, enum eLookupMode mode_)
-{
- char const* errmsg;
- luaG_IdFunction idfunc = get_idfunc( L, index, mode_);
- if( idfunc == NULL)
- {
- return NULL; // not a deep userdata
- }
-
- errmsg = push_deep_proxy( U, L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_);
- if( errmsg != NULL)
- {
- // raise the error in the proper state (not the keeper)
- lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L;
- luaL_error( errL, errmsg);
- }
- return idfunc;
-}
-
/*---=== Inter-state copying ===---*/
-/*-- Metatable copying --*/
-
-/*
- * 'reg[ REG_MT_KNOWN ]'= {
- * [ table ]= id_uint,
- * ...
- * [ id_uint ]= table,
- * ...
- * }
- */
-
-/*
-* Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
-*/
-static void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_)
-{
- STACK_GROW( L, 3);
- STACK_CHECK( L);
-
- lua_pushlightuserdata( L, key_); // key
- lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil
-
- if( lua_isnil( L, -1))
- {
- lua_pop( L, 1); //
- lua_newtable( L); // {}
- lua_pushlightuserdata( L, key_); // {} key
- lua_pushvalue( L, -2); // {} key {}
-
- // _R[key_] = {}
- lua_rawset( L, LUA_REGISTRYINDEX); // {}
-
- // Set its metatable if requested
- if( mode_)
- {
- lua_newtable( L); // {} mt
- lua_pushliteral( L, "__mode"); // {} mt "__mode"
- lua_pushstring( L, mode_); // {} mt "__mode" mode
- lua_rawset( L, -3); // {} mt
- lua_setmetatable( L, -2); // {}
- }
- }
- STACK_END( L, 1);
- ASSERT_L( lua_istable( L, -1));
-}
-
-/*
-* Push a registry subtable (keyed by unique 'key_') onto the stack.
-* If the subtable does not exist, it is created and chained.
-*/
-static inline void push_registry_subtable( lua_State* L, void* key_)
-{
- push_registry_subtable_mode( L, key_, NULL);
-}
-
#define REG_MTID ( (void*) get_mt_id )
/*
--
cgit v1.2.3-55-g6feb