aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m>2018-11-07 19:16:36 +0100
committerBenoit Germain <b n t DOT g e r m a i n AT g m a i l DOT c o m>2018-11-07 19:16:36 +0100
commita142eb1e1ee81919d10b55bb7fa2e33636098d85 (patch)
tree21ef5c830ce4b4e845454af4274beabd073cc720
parent91155c74fc10fa98ad6257d5309bfd13d4a61cf0 (diff)
downloadlanes-a142eb1e1ee81919d10b55bb7fa2e33636098d85.tar.gz
lanes-a142eb1e1ee81919d10b55bb7fa2e33636098d85.tar.bz2
lanes-a142eb1e1ee81919d10b55bb7fa2e33636098d85.zip
__lanesclone mechanism should actually work now
-rw-r--r--CHANGES3
-rw-r--r--deep_test/deep_test.c80
-rw-r--r--deep_test/deeptest.lua53
-rw-r--r--docs/index.html65
-rw-r--r--src/lanes.c1
-rw-r--r--src/tools.c89
6 files changed, 230 insertions, 61 deletions
diff --git a/CHANGES b/CHANGES
index be0e7d9..228038b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,8 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 132: BGe 7-Nov-18
4 * __lanesclone mechanism should actually work now
5
3CHANGE 131: BGe 7-Nov-18 6CHANGE 131: BGe 7-Nov-18
4 * Fix potential crash at application shutdown when deep userdata were created before Lanes is required 7 * Fix potential crash at application shutdown when deep userdata were created before Lanes is required
5 8
diff --git a/deep_test/deep_test.c b/deep_test/deep_test.c
index 8f34fe5..4aac586 100644
--- a/deep_test/deep_test.c
+++ b/deep_test/deep_test.c
@@ -80,10 +80,7 @@ static void* deep_test_id( lua_State* L, enum eDeepOp op_)
80 80
81 case eDO_metatable: 81 case eDO_metatable:
82 { 82 {
83 lua_newtable( L); // mt 83 luaL_getmetatable( L, "deep"); // mt
84 luaL_setfuncs( L, deep_mt, 0); // mt
85 lua_pushvalue( L, -1); // mt mt
86 lua_setfield( L, -2, "__index"); // mt
87 luaG_pushdeepversion( L); // mt version 84 luaG_pushdeepversion( L); // mt version
88 return NULL; 85 return NULL;
89 } 86 }
@@ -115,6 +112,16 @@ struct s_MyClonableUserdata
115 112
116// ################################################################################################ 113// ################################################################################################
117 114
115static int clonable_set( lua_State* L)
116{
117 struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1);
118 lua_Integer i = lua_tointeger( L, 2);
119 self->val = i;
120 return 0;
121}
122
123// ################################################################################################
124
118static int clonable_tostring(lua_State* L) 125static int clonable_tostring(lua_State* L)
119{ 126{
120 struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1); 127 struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1);
@@ -124,13 +131,34 @@ static int clonable_tostring(lua_State* L)
124 131
125// ################################################################################################ 132// ################################################################################################
126 133
134static int clonable_gc( lua_State* L)
135{
136 struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1);
137 return 0;
138}
139
140// ################################################################################################
141
127static int clonable_lanesclone( lua_State* L) 142static int clonable_lanesclone( lua_State* L)
128{ 143{
129 // no need to set the metatable, the Lane copying mechanism will take care of it 144 switch( lua_gettop( L))
130 struct s_MyClonableUserdata* self = lua_touserdata( L, 1); 145 {
131 struct s_MyClonableUserdata* to = lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); 146 case 0:
132 memcpy( to, self, sizeof(struct s_MyClonableUserdata)); 147 lua_pushinteger( L, sizeof( struct s_MyClonableUserdata));
133 return 1; 148 return 1;
149
150 case 2:
151 {
152 struct s_MyClonableUserdata* self = lua_touserdata( L, 1);
153 struct s_MyClonableUserdata* from = lua_touserdata( L, 2);
154 *self = *from;
155 return 0;
156 }
157
158 default:
159 (void) luaL_error( L, "Lanes called clonable_lanesclone with unexpected parameters");
160 }
161 return 0;
134} 162}
135 163
136// ################################################################################################ 164// ################################################################################################
@@ -138,9 +166,9 @@ static int clonable_lanesclone( lua_State* L)
138static luaL_Reg const clonable_mt[] = 166static luaL_Reg const clonable_mt[] =
139{ 167{
140 { "__tostring", clonable_tostring}, 168 { "__tostring", clonable_tostring},
141 //{ "__gc", deep_gc}, 169 { "__gc", clonable_gc},
142 { "__lanesclone", clonable_lanesclone}, 170 { "__lanesclone", clonable_lanesclone},
143 //{ "set", deep_set}, 171 { "set", clonable_set},
144 { NULL, NULL } 172 { NULL, NULL }
145}; 173};
146 174
@@ -149,15 +177,7 @@ static luaL_Reg const clonable_mt[] =
149int luaD_new_clonable( lua_State* L) 177int luaD_new_clonable( lua_State* L)
150{ 178{
151 lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); 179 lua_newuserdata( L, sizeof( struct s_MyClonableUserdata));
152 if( luaL_getmetatable( L, "clonable") == LUA_TNIL) // u mt? 180 luaL_setmetatable( L, "clonable");
153 {
154 lua_pop( L, 1); // u
155 lua_newtable( L); // u mt
156 luaL_setfuncs( L, clonable_mt, 0);
157 lua_pushvalue(L, -1); // u mt mt
158 lua_setfield(L, -2, "__index"); // u mt
159 }
160 lua_setmetatable( L, -2); // u
161 return 1; 181 return 1;
162} 182}
163 183
@@ -175,6 +195,24 @@ static luaL_Reg const deep_module[] =
175 195
176extern int __declspec(dllexport) luaopen_deep_test(lua_State* L) 196extern int __declspec(dllexport) luaopen_deep_test(lua_State* L)
177{ 197{
178 luaL_newlib( L, deep_module); 198 luaL_newlib( L, deep_module); // M
199
200 // preregister the metatables for the types we can instanciate so that Lanes can know about them
201 if( luaL_newmetatable( L, "clonable")) // M mt
202 {
203 luaL_setfuncs( L, clonable_mt, 0);
204 lua_pushvalue(L, -1); // M mt mt
205 lua_setfield(L, -2, "__index"); // M mt
206 }
207 lua_setfield(L, -2, "__clonableMT"); // M
208
209 if( luaL_newmetatable( L, "deep")) // mt
210 {
211 luaL_setfuncs( L, deep_mt, 0);
212 lua_pushvalue(L, -1); // mt mt
213 lua_setfield(L, -2, "__index"); // mt
214 }
215 lua_setfield(L, -2, "__deepMT"); // M
216
179 return 1; 217 return 1;
180} 218}
diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua
index 3b514dd..c0bbab4 100644
--- a/deep_test/deeptest.lua
+++ b/deep_test/deeptest.lua
@@ -1,22 +1,39 @@
1-- create a deep-aware full userdata while Lanes isn't loaded 1local lanes = require("lanes").configure{ with_timers = false}
2local dt = require "deep_test" 2local l = lanes.linda "my linda"
3local deep = dt.new_deep()
4deep:set(666)
5print( deep)
6 3
7local clonable = dt.new_clonable() 4-- we will transfer userdata created by this module, so we need to make Lanes aware of it
5local dt = lanes.require "deep_test"
8 6
9-- now load Lanes and see if that userdata is transferable 7local test_deep = true
10--[[ 8local test_clonable = false
11local lanes = require("lanes").configure() 9
12local l = lanes.linda "my linda" 10local performTest = function( obj_)
11 obj_:set(666)
12 print( "immediate:", obj_)
13
14 l:set( "key", obj_)
15 local out = l:get( "key")
16 print( "out of linda:", out)
17
18 local g = lanes.gen(
19 "package"
20 , {
21 required = { "deep_test"} -- we will transfer userdata created by this module, so we need to make this lane aware of it
22 }
23 , function( obj_)
24 print( "in lane:", obj_)
25 return obj_
26 end
27 )
28 h = g( obj_)
29 local from_lane = h[1]
30 print( "from lane:", from_lane)
31end
13 32
14l:set( "key", deep) 33if test_deep then
15local deep_out = l:get( "key") 34 performTest( dt.new_deep())
16print( deep_out) 35end
17 36
18lanes.register() 37if test_clonable then
19l:set( "key", clonable) 38 performTest( dt.new_clonable())
20local clonable_out = l:get( "key") 39end
21print( clonable_out)
22--]] \ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
index aa12a36..9c76bef 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 4-Nov-18, and applies to version <tt>3.13</tt>. 73 This document was revised on 7-Nov-18, and applies to version <tt>3.13</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -1516,15 +1516,68 @@ events to a common Linda, but... :).</font>
1516<table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> 1516<table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre>
1517static int clonable_lanesclone( lua_State* L) 1517static int clonable_lanesclone( lua_State* L)
1518{ 1518{
1519 // no need to set the metatable, the Lane copying mechanism will take care of it 1519 switch( lua_gettop( L))
1520 struct s_MyClonableUserdata* self = lua_touserdata( L, 1); 1520 {
1521 struct s_MyClonableUserdata* to = lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); 1521 case 0:
1522 memcpy( to, self, sizeof(struct s_MyClonableUserdata)); 1522 lua_pushinteger( L, sizeof( struct s_MyClonableUserdata));
1523 return 1;
1524
1525 case 2:
1526 {
1527 struct s_MyClonableUserdata* self = lua_touserdata( L, 1);
1528 struct s_MyClonableUserdata* from = lua_touserdata( L, 2);
1529 *self = *from;
1530 return 0;
1531 }
1532
1533 default:
1534 (void) luaL_error( L, "Lanes called clonable_lanesclone with unexpected parameters");
1535 }
1536 return 0;
1537}
1538</pre></td></tr></table>
1539</p>
1540
1541<p>
1542 Of course, more complex objects may require smarter cloning behavior than a simple <tt>memcpy</tt>. Also, the module initialisation code should make each metatable accessible from the module table itself as in:
1543<table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre>
1544int luaopen_deep_test(lua_State* L)
1545{
1546 luaL_newlib( L, deep_module);
1547
1548 // preregister the metatables for the types we can instanciate so that Lanes can know about them
1549 if( luaL_newmetatable( L, "clonable"))
1550 {
1551 luaL_setfuncs( L, clonable_mt, 0);
1552 lua_pushvalue(L, -1);
1553 lua_setfield(L, -2, "__index");
1554 }
1555 lua_setfield(L, -2, "__clonableMT"); // actual name is not important
1556
1557 if( luaL_newmetatable( L, "deep"))
1558 {
1559 luaL_setfuncs( L, deep_mt, 0);
1560 lua_pushvalue(L, -1);
1561 lua_setfield(L, -2, "__index");
1562 }
1563 lua_setfield(L, -2, "__deepMT"); // actual name is not important
1564
1565 return 1;
1566}
1567</pre></td></tr></table>
1568</p>
1569
1570<p>
1571 Then a new clonable userdata instance can just do like any non-Lanes aware userdata, as long as its metatable contains the aforementionned <tt>__lanesclone</tt> method. Note that the current implementation doesn't support uservalues on such userdata.
1572<table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre>
1573int luaD_new_clonable( lua_State* L)
1574{
1575 lua_newuserdata( L, sizeof( struct s_MyClonableUserdata));
1576 luaL_setmetatable( L, "clonable");
1523 return 1; 1577 return 1;
1524} 1578}
1525</pre></td></tr></table> 1579</pre></td></tr></table>
1526</p> 1580</p>
1527 Of course, more complex objects may require smarter cloning behavior than a simple <tt>memcpy</tt>.
1528 1581
1529<h3 id="deep_userdata">Deep userdata in your own apps</h3> 1582<h3 id="deep_userdata">Deep userdata in your own apps</h3>
1530 1583
diff --git a/src/lanes.c b/src/lanes.c
index d4b4c73..c3e64fb 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -2289,6 +2289,7 @@ LUAG_FUNC( lane_new)
2289 // require the module in the target state, and populate the lookup table there too 2289 // require the module in the target state, and populate the lookup table there too
2290 size_t len; 2290 size_t len;
2291 char const* name = lua_tolstring( L, -1, &len); 2291 char const* name = lua_tolstring( L, -1, &len);
2292 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "lane_new: require '%s'\n" INDENT_END, name));
2292 2293
2293 // require the module in the target lane 2294 // require the module in the target lane
2294 lua_getglobal( L2, "require"); // require()? 2295 lua_getglobal( L2, "require"); // require()?
diff --git a/src/tools.c b/src/tools.c
index 2f9de7b..8e886b5 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -825,6 +825,12 @@ static int table_lookup_sentinel( lua_State* L)
825 return luaL_error( L, "table lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1))); 825 return luaL_error( L, "table lookup sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1)));
826} 826}
827 827
828// function sentinel used to transfer cloned full userdata from/to keeper states
829static int userdata_clone_sentinel( lua_State* L)
830{
831 return luaL_error( L, "userdata clone sentinel for %s, should never be called", lua_tostring( L, lua_upvalueindex( 1)));
832}
833
828/* 834/*
829 * retrieve the name of a function/table in the lookup database 835 * retrieve the name of a function/table in the lookup database
830 */ 836 */
@@ -838,7 +844,7 @@ static char const* find_lookup_name( lua_State* L, uint_t i, LookupMode mode_, c
838 if( mode_ == eLM_FromKeeper) 844 if( mode_ == eLM_FromKeeper)
839 { 845 {
840 lua_CFunction f = lua_tocfunction( L, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel! 846 lua_CFunction f = lua_tocfunction( L, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel!
841 if( f == func_lookup_sentinel || f == table_lookup_sentinel) 847 if( f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel)
842 { 848 {
843 lua_getupvalue( L, i, 1); // ... v ... "f.q.n" 849 lua_getupvalue( L, i, 1); // ... v ... "f.q.n"
844 } 850 }
@@ -1594,6 +1600,7 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu
1594 bool_t ignore = FALSE; 1600 bool_t ignore = FALSE;
1595 int val_type = lua_type( L, i); 1601 int val_type = lua_type( L, i);
1596 STACK_GROW( L2, 1); 1602 STACK_GROW( L2, 1);
1603 STACK_CHECK( L); // L // L2
1597 STACK_CHECK( L2); // L // L2 1604 STACK_CHECK( L2); // L // L2
1598 1605
1599 /* Skip the object if it has metatable with { __lanesignore = true } */ 1606 /* Skip the object if it has metatable with { __lanesignore = true } */
@@ -1662,30 +1669,53 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu
1662 { 1669 {
1663 break; 1670 break;
1664 } 1671 }
1672 STACK_MID( L, 0);
1665 1673
1666 if( lua_getmetatable( L, i)) // ... mt? 1674 if( lua_getmetatable( L, i)) // ... mt?
1667 { 1675 {
1668 lua_getfield( L, -1, "__lanesclone"); // ... mt clone? 1676 lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone?
1669 if( !lua_isnil( L, -1)) 1677 if( lua_isnil( L, -1))
1678 {
1679 lua_pop( L, 2); // ...
1680 }
1681 else
1670 { 1682 {
1683 FuncSubType fst;
1684 lua_CFunction cloneFunc = luaG_tocfunction( L, -1, &fst);
1685 size_t userdata_size = 0;
1671 void* const source = lua_touserdata( L, i); 1686 void* const source = lua_touserdata( L, i);
1687 void* clone = NULL;
1688 lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone
1689 // call the cloning function with 0 arguments, should return the number of bytes to allocate for the clone
1690 lua_call( L, 0, 1); // ... mt __lanesclone size
1691 STACK_MID( L, 3);
1692 userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size
1693 lua_pop( L, 1); // ... mt __lanesclone
1694 clone = lua_newuserdata( L2, userdata_size); // ... u
1695 // call cloning function in source state to perform the actual memory cloning
1696 lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone
1697 lua_pushlightuserdata( L, source); // ... mt __lanesclone source
1698 lua_call( L, 2, 0); // ... mt
1699 STACK_MID( L, 1);
1672 // copy the metatable in the target state 1700 // copy the metatable in the target state
1673 if( push_cached_metatable( U, L2, L2_cache_i, L, i, mode_, upName_)) // ... mt? 1701 if( inter_copy_one_( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u mt?
1674 { 1702 {
1675 // retrieve cloning function 1703 lua_pop( L, 1); // ...
1676 lua_getfield( L2, -1, "__lanesclone"); // ... mt clone 1704 STACK_MID( L, 0);
1677 lua_pushlightuserdata( L2, source); // ... mt clone p 1705 // when writing to a keeper state, we have here a sentinel function with the metatable's fqn as upvalue
1678 // cloning function should create a new full userdata without a metatable 1706 if( eLM_ToKeeper == mode_) // ... u sentinel
1679 if( lua_pcall( L2, 1, 1, 0) == LUA_OK) // ... mt u
1680 { 1707 {
1681 lua_insert( L2, -2); // ... u mt 1708 ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel);
1682 lua_setmetatable( L2, -2); // ... u 1709 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
1710 lua_getupvalue( L2, -1, 1); // ... u sentinel fqn
1711 lua_remove( L2, -2); // ... u fqn
1712 lua_insert( L2, -2); // ... fqn u
1713 lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel
1683 } 1714 }
1684 else // ... mt err 1715 else // from keeper or direct, we have the userdata and the metatable
1685 { 1716 {
1686 // propagate any error to the source state 1717 ASSERT_L( lua_istable( L2, -1));
1687 char const* errmsg = lua_tostring( L2, -1); 1718 lua_setmetatable( L2, -2); // ... u
1688 (void) luaL_error( L, "can't copy non-deep full userdata across lanes: %s", errmsg);
1689 } 1719 }
1690 } 1720 }
1691 else 1721 else
@@ -1693,7 +1723,6 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu
1693 (void) luaL_error( L, "Error copying a metatable"); 1723 (void) luaL_error( L, "Error copying a metatable");
1694 } 1724 }
1695 } 1725 }
1696 lua_pop( L, 2); // ...
1697 break; 1726 break;
1698 } 1727 }
1699 1728
@@ -1724,6 +1753,33 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu
1724 ret = FALSE; 1753 ret = FALSE;
1725 break; 1754 break;
1726 } 1755 }
1756
1757 if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata
1758 {
1759 // clone the full userdata again
1760 size_t userdata_size = 0;
1761 void* source;
1762 void* clone;
1763 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself
1764 lua_getupvalue( L, i, 2); // ... u
1765 source = lua_touserdata( L, -1);
1766 lookup_table( L2, L, i, mode_, upName_); // ... u // ... mt
1767 // __lanesclone should always exist because we woudln't be restoring data from a userdata_clone_sentinel closure to begin with
1768 lua_getfield( L2, -1, "__lanesclone"); // // ... mt __lanesclone
1769 lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone
1770 // call the cloning function with 0 arguments, should return the number of bytes to allocate for the clone
1771 lua_call( L2, 0, 1); // ... mt __lanesclone size
1772 userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size
1773 lua_pop( L2, 1); // ... mt __lanesclone
1774 clone = lua_newuserdata( L2, userdata_size); // ... mt __lanesclone u
1775 lua_insert( L2, -3); // ... u mt __lanesclone
1776 lua_pushlightuserdata( L2, clone); // ... u mt __lanesclone clone
1777 lua_pushlightuserdata( L2, source); // ... u mt __lanesclone clone source
1778 lua_call( L2, 2, 0); // ... u mt
1779 lua_setmetatable( L2, -2); // ... u
1780 lua_pop( L, 1); // ...
1781 }
1782 else
1727 { 1783 {
1728 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "FUNCTION %s\n" INDENT_END, upName_)); 1784 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "FUNCTION %s\n" INDENT_END, upName_));
1729 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); 1785 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
@@ -1802,6 +1858,7 @@ static bool_t inter_copy_one_( Universe* U, lua_State* L2, uint_t L2_cache_i, lu
1802 } 1858 }
1803 1859
1804 STACK_END( L2, ret ? 1 : 0); 1860 STACK_END( L2, ret ? 1 : 0);
1861 STACK_END( L, 0);
1805 return ret; 1862 return ret;
1806} 1863}
1807 1864