aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2018-11-15 11:20:14 +0100
committerBenoit Germain <bnt.germain@gmail.com>2018-11-15 11:20:14 +0100
commit01f83215a2ad235fbf306f591c6c0547b1bb7047 (patch)
tree51d1edff1c3f684bac388c64d91ee30d8f6fbbf2
parent55acf8e19728ac39581c35f1324debf9449bd185 (diff)
downloadlanes-01f83215a2ad235fbf306f591c6c0547b1bb7047.tar.gz
lanes-01f83215a2ad235fbf306f591c6c0547b1bb7047.tar.bz2
lanes-01f83215a2ad235fbf306f591c6c0547b1bb7047.zip
Deep userdata must embed DeepPrelude to save an allocation (also changes Deep protocol)
-rw-r--r--CHANGES3
-rw-r--r--deep_test/deep_test.c3
-rw-r--r--docs/index.html18
-rw-r--r--src/deep.c66
-rw-r--r--src/deep.h12
-rw-r--r--src/keeper.c2
-rw-r--r--src/lanes.c6
-rw-r--r--src/lanes_private.h2
-rw-r--r--src/linda.c5
9 files changed, 56 insertions, 61 deletions
diff --git a/CHANGES b/CHANGES
index 1025484..713e516 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,8 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 137: BGe 15-Nov-18
4 * Deep userdata must embed DeepPrelude to save an allocation (also changes Deep protocol)
5
3CHANGE 136: BGe 15-Nov-18 6CHANGE 136: BGe 15-Nov-18
4 * split linda code in a separate file 7 * split linda code in a separate file
5 * rockspec for version v3.13.0 8 * rockspec for version v3.13.0
diff --git a/deep_test/deep_test.c b/deep_test/deep_test.c
index 4aac586..7edd33f 100644
--- a/deep_test/deep_test.c
+++ b/deep_test/deep_test.c
@@ -17,6 +17,7 @@
17 17
18struct s_MyDeepUserdata 18struct s_MyDeepUserdata
19{ 19{
20 DeepPrelude prelude; // Deep userdata MUST start with this header
20 lua_Integer val; 21 lua_Integer val;
21}; 22};
22static void* deep_test_id( lua_State* L, enum eDeepOp op_); 23static void* deep_test_id( lua_State* L, enum eDeepOp op_);
@@ -67,6 +68,7 @@ static void* deep_test_id( lua_State* L, enum eDeepOp op_)
67 case eDO_new: 68 case eDO_new:
68 { 69 {
69 struct s_MyDeepUserdata* deep_test = (struct s_MyDeepUserdata*) malloc( sizeof(struct s_MyDeepUserdata)); 70 struct s_MyDeepUserdata* deep_test = (struct s_MyDeepUserdata*) malloc( sizeof(struct s_MyDeepUserdata));
71 deep_test->prelude.magic.value = DEEP_VERSION.value;
70 deep_test->val = 0; 72 deep_test->val = 0;
71 return deep_test; 73 return deep_test;
72 } 74 }
@@ -81,7 +83,6 @@ static void* deep_test_id( lua_State* L, enum eDeepOp op_)
81 case eDO_metatable: 83 case eDO_metatable:
82 { 84 {
83 luaL_getmetatable( L, "deep"); // mt 85 luaL_getmetatable( L, "deep"); // mt
84 luaG_pushdeepversion( L); // mt version
85 return NULL; 86 return NULL;
86 } 87 }
87 88
diff --git a/docs/index.html b/docs/index.html
index ddb9ed3..723766d 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1607,9 +1607,9 @@ int luaD_new_clonable( lua_State* L)
1607 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> void* idfunc( lua_State* L, DeepOp op_);</pre></td></tr></table> 1607 <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> void* idfunc( lua_State* L, DeepOp op_);</pre></td></tr></table>
1608 <tt>op_</tt> can be one of: 1608 <tt>op_</tt> can be one of:
1609 <ul> 1609 <ul>
1610 <li><tt>eDO_new</tt>: requests the creation of a new object, whose pointer is returned.</li> 1610 <li><tt>eDO_new</tt>: requests the creation of a new object, whose pointer is returned. Starting with version 3.13.0, object should embed <tt>DeepPrelude</tt> structure as header and initialize its <tt>magic</tt> member with the current <tt>DEEP_VERSION</tt>.</li>
1611 <li><tt>eDO_delete</tt>: receives this same pointer on the stack as a light userdata, and should cleanup the object.</li> 1611 <li><tt>eDO_delete</tt>: receives this same pointer on the stack as a light userdata, and should cleanup the object.</li>
1612 <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> 1612 <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). Just push the metatable on the stack.</li>
1613 <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> 1613 <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>
1614 </ul> 1614 </ul>
1615 Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt> or <tt>deep_test_id</tt> in <tt>deep_test.c</tt>. 1615 Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt> or <tt>deep_test_id</tt> in <tt>deep_test.c</tt>.
@@ -1624,13 +1624,13 @@ int luaD_new_clonable( lua_State* L)
1624</p> 1624</p>
1625 1625
1626<p> 1626<p>
1627 Deep userdata in transit inside keeper states (sent in a linda but not yet consumed) don't call <tt>idfunc(eDO_delete)</tt> and aren't considered by reference counting. The rationale is the following: 1627 Deep userdata in transit inside keeper states (sent in a linda but not yet consumed) don't call <tt>idfunc(eDO_delete)</tt> and aren't considered by reference counting. The rationale is the following:
1628 <br/> 1628 <br />
1629 If some non-keeper state holds a deep userdata for some deep object, then even if the keeper collects its own deep userdata, it shouldn't be cleaned up since the refcount is not 0. 1629 If some non-keeper state holds a deep userdata for some deep object, then even if the keeper collects its own deep userdata, it shouldn't be cleaned up since the refcount is not 0.
1630 <br/> 1630 <br />
1631 OTOH, if a keeper state holds the last deep userdata for some deep object, then no lane can do actual work with it. But as it happens, deep userdata are only copied to and from keeper states. Most notably, the object's <tt>idfunc()</tt> is never called from a keeper state. 1631 OTOH, if a keeper state holds the last deep userdata for some deep object, then no lane can do actual work with it. Deep userdata's <tt>idfunc()</tt> is never called from a keeper state.
1632 <br/> 1632 <br />
1633 Therefore, Lanes can just call <tt>idfunc(eDO_delete)</tt> when the last non-keeper-held deep userdata is collected, as long as it doens't do the same in a keeper state after that, since any remaining deep userdata in keeper states now hold stale pointers. 1633 Therefore, Lanes can just call <tt>idfunc(eDO_delete)</tt> when the last non-keeper-held deep userdata is collected, as long as it doesn't do the same in a keeper state after that, since any remaining deep userdata in keeper states now hold stale pointers.
1634</p> 1634</p>
1635 1635
1636<p> 1636<p>
diff --git a/src/deep.c b/src/deep.c
index af7f580..c351bf7 100644
--- a/src/deep.c
+++ b/src/deep.c
@@ -105,16 +105,6 @@ void push_registry_subtable( lua_State* L, UniqueKey key_)
105 105
106/*---=== Deep userdata ===---*/ 106/*---=== Deep userdata ===---*/
107 107
108void luaG_pushdeepversion( lua_State* L) { (void) lua_pushliteral( L, "ab8743e5-84f8-485d-9c39-008e84656188");}
109
110
111
112/* The deep portion must be allocated separately of any Lua state's; it's
113* lifespan may be longer than that of the creating state.
114*/
115#define DEEP_MALLOC malloc
116#define DEEP_FREE free
117
118/* 108/*
119* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's 109* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
120* metatables: 110* metatables:
@@ -211,10 +201,9 @@ static inline luaG_IdFunction get_idfunc( lua_State* L, int index, LookupMode mo
211void free_deep_prelude( lua_State* L, DeepPrelude* prelude_) 201void free_deep_prelude( lua_State* L, DeepPrelude* prelude_)
212{ 202{
213 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup 203 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
214 lua_pushlightuserdata( L, prelude_->deep); 204 lua_pushlightuserdata( L, prelude_);
215 ASSERT_L( prelude_->idfunc); 205 ASSERT_L( prelude_->idfunc);
216 prelude_->idfunc( L, eDO_delete); 206 prelude_->idfunc( L, eDO_delete);
217 DEEP_FREE( (void*) prelude_);
218} 207}
219 208
220 209
@@ -276,7 +265,7 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, Lo
276 265
277 // Check if a proxy already exists 266 // Check if a proxy already exists
278 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC 267 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
279 lua_pushlightuserdata( L, prelude->deep); // DPC deep 268 lua_pushlightuserdata( L, prelude); // DPC deep
280 lua_rawget( L, -2); // DPC proxy 269 lua_rawget( L, -2); // DPC proxy
281 if ( !lua_isnil( L, -1)) 270 if ( !lua_isnil( L, -1))
282 { 271 {
@@ -313,20 +302,13 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, Lo
313 // 1 - make one and register it 302 // 1 - make one and register it
314 if( mode_ != eLM_ToKeeper) 303 if( mode_ != eLM_ToKeeper)
315 { 304 {
316 (void) prelude->idfunc( L, eDO_metatable); // DPC proxy metatable deepversion 305 (void) prelude->idfunc( L, eDO_metatable); // DPC proxy metatable
317 if( lua_gettop( L) - oldtop != 1 || !lua_istable( L, -2) || !lua_isstring( L, -1)) 306 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1))
318 { 307 {
319 lua_settop( L, oldtop); // DPC proxy X 308 lua_settop( L, oldtop); // DPC proxy X
320 lua_pop( L, 3); // 309 lua_pop( L, 3); //
321 return "Bad idfunc(eOP_metatable): unexpected pushed value"; 310 return "Bad idfunc(eOP_metatable): unexpected pushed value";
322 } 311 }
323 luaG_pushdeepversion( L); // DPC proxy metatable deepversion deepversion
324 if( !lua501_equal( L, -1, -2))
325 {
326 lua_pop( L, 5); //
327 return "Bad idfunc(eOP_metatable): mismatched deep version";
328 }
329 lua_pop( L, 2); // DPC proxy metatable
330 // if the metatable contains a __gc, we will call it from our own 312 // if the metatable contains a __gc, we will call it from our own
331 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc 313 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
332 } 314 }
@@ -420,7 +402,7 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, Lo
420 lua_setmetatable( L, -2); // DPC proxy 402 lua_setmetatable( L, -2); // DPC proxy
421 403
422 // If we're here, we obviously had to create a new proxy, so cache it. 404 // If we're here, we obviously had to create a new proxy, so cache it.
423 lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep 405 lua_pushlightuserdata( L, prelude); // DPC proxy deep
424 lua_pushvalue( L, -2); // DPC proxy deep proxy 406 lua_pushvalue( L, -2); // DPC proxy deep proxy
425 lua_rawset( L, -4); // DPC proxy 407 lua_rawset( L, -4); // DPC proxy
426 lua_remove( L, -2); // proxy 408 lua_remove( L, -2); // proxy
@@ -454,34 +436,38 @@ char const* push_deep_proxy( Universe* U, lua_State* L, DeepPrelude* prelude, Lo
454int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) 436int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc)
455{ 437{
456 char const* errmsg; 438 char const* errmsg;
457 DeepPrelude* prelude = DEEP_MALLOC( sizeof( DeepPrelude));
458 if( prelude == NULL)
459 {
460 return luaL_error( L, "couldn't not allocate deep prelude: out of memory");
461 }
462
463 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
464 prelude->idfunc = idfunc;
465 439
466 STACK_GROW( L, 1); 440 STACK_GROW( L, 1);
467 STACK_CHECK( L); 441 STACK_CHECK( L);
468 { 442 {
469 int oldtop = lua_gettop( L); 443 int oldtop = lua_gettop( L);
470 prelude->deep = idfunc( L, eDO_new); 444 DeepPrelude* prelude = idfunc( L, eDO_new);
471 if( prelude->deep == NULL) 445 if( prelude == NULL)
472 { 446 {
473 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)"); 447 luaL_error( L, "idfunc(eDO_new) failed to create deep userdata (out of memory)");
474 } 448 }
449 if( prelude->magic.value != DEEP_VERSION.value)
450 {
451 // just in case, don't leak the newly allocated deep userdata object
452 lua_pushlightuserdata( L, prelude);
453 idfunc( L, eDO_delete);
454 return luaL_error( L, "Bad idfunc(eDO_new): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
455 }
456 prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1
457 prelude->idfunc = idfunc;
475 458
476 if( lua_gettop( L) - oldtop != 0) 459 if( lua_gettop( L) - oldtop != 0)
477 { 460 {
478 luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack"); 461 // just in case, don't leak the newly allocated deep userdata object
462 lua_pushlightuserdata( L, prelude);
463 idfunc( L, eDO_delete);
464 return luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack");
465 }
466 errmsg = push_deep_proxy( universe_get( L), L, prelude, eLM_LaneBody); // proxy
467 if( errmsg != NULL)
468 {
469 return luaL_error( L, errmsg);
479 } 470 }
480 }
481 errmsg = push_deep_proxy( universe_get( L), L, prelude, eLM_LaneBody); // proxy
482 if( errmsg != NULL)
483 {
484 luaL_error( L, errmsg);
485 } 471 }
486 STACK_END( L, 1); 472 STACK_END( L, 1);
487 return 1; 473 return 1;
@@ -508,7 +494,7 @@ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
508 proxy = (DeepPrelude**) lua_touserdata( L, index); 494 proxy = (DeepPrelude**) lua_touserdata( L, index);
509 STACK_END( L, 0); 495 STACK_END( L, 0);
510 496
511 return (*proxy)->deep; 497 return *proxy;
512} 498}
513 499
514 500
diff --git a/src/deep.h b/src/deep.h
index 918de6a..8d06395 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -8,6 +8,7 @@
8 8
9#include "lua.h" 9#include "lua.h"
10#include "platform.h" 10#include "platform.h"
11#include "uniquekey.h"
11 12
12// forwards 13// forwards
13struct s_Universe; 14struct s_Universe;
@@ -42,13 +43,17 @@ typedef void* (*luaG_IdFunction)( lua_State* L, DeepOp op_);
42 43
43// ################################################################################################ 44// ################################################################################################
44 45
45// this is pointed to by full userdata proxies, and allocated with malloc() to survive any lua_State lifetime 46// crc64/we of string "DEEP_VERSION_1" generated at http://www.nitrxgen.net/hashgen/
47static DECLARE_CONST_UNIQUE_KEY( DEEP_VERSION, 0x4f4eadf0accf6c73);
48
49// should be used as header for full userdata
46struct s_DeepPrelude 50struct s_DeepPrelude
47{ 51{
48 volatile int refcount; 52 DECLARE_UNIQUE_KEY( magic); // must be filled by the Deep userdata idfunc that allocates it on eDO_new operation
49 void* deep;
50 // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc 53 // when stored in a keeper state, the full userdata doesn't have a metatable, so we need direct access to the idfunc
51 luaG_IdFunction idfunc; 54 luaG_IdFunction idfunc;
55 // data is destroyed when refcount is 0
56 volatile int refcount;
52}; 57};
53typedef struct s_DeepPrelude DeepPrelude; 58typedef struct s_DeepPrelude DeepPrelude;
54 59
@@ -57,6 +62,5 @@ void free_deep_prelude( lua_State* L, DeepPrelude* prelude_);
57 62
58extern LANES_API int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc); 63extern LANES_API int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc);
59extern LANES_API void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index); 64extern LANES_API void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index);
60extern LANES_API void luaG_pushdeepversion( lua_State* L);
61 65
62#endif // __LANES_DEEP_H__ 66#endif // __LANES_DEEP_H__
diff --git a/src/keeper.c b/src/keeper.c
index 091463e..0471cb7 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -754,7 +754,7 @@ void keeper_release( Keeper* K)
754 if( K) MUTEX_UNLOCK( &K->keeper_cs); 754 if( K) MUTEX_UNLOCK( &K->keeper_cs);
755} 755}
756 756
757void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode mode_) 757void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, LookupMode const mode_)
758{ 758{
759 int i, n = lua_gettop( L); 759 int i, n = lua_gettop( L);
760 for( i = val_i_; i <= n; ++ i) 760 for( i = val_i_; i <= n; ++ i)
diff --git a/src/lanes.c b/src/lanes.c
index f2e3065..037e44f 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -733,7 +733,10 @@ static int selfdestruct_gc( lua_State* L)
733 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1 733 // necessary so that calling free_deep_prelude doesn't crash because linda_id expects a linda lightuserdata at absolute slot 1
734 lua_settop( L, 0); 734 lua_settop( L, 0);
735 // no need to mutex-protect this as all threads in the universe are gone at that point 735 // no need to mutex-protect this as all threads in the universe are gone at that point
736 -- U->timer_deep->refcount; // should be 0 now 736 if( U->timer_deep != NULL) // test ins case some early internal error prevented Lanes from creating the deep timer
737 {
738 -- U->timer_deep->refcount; // should be 0 now
739 }
737 free_deep_prelude( L, (DeepPrelude*) U->timer_deep); 740 free_deep_prelude( L, (DeepPrelude*) U->timer_deep);
738 U->timer_deep = NULL; 741 U->timer_deep = NULL;
739 742
@@ -2162,7 +2165,6 @@ LUAG_FUNC( configure)
2162 2165
2163 // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer 2166 // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer
2164 U->timer_deep = *(DeepPrelude**) lua_touserdata( L, -1); 2167 U->timer_deep = *(DeepPrelude**) lua_touserdata( L, -1);
2165 ASSERT_L( U->timer_deep && (U->timer_deep->refcount == 1) && U->timer_deep->deep && U->timer_deep->idfunc == linda_id);
2166 // increment refcount that this linda remains alive as long as the universe is. 2168 // increment refcount that this linda remains alive as long as the universe is.
2167 ++ U->timer_deep->refcount; 2169 ++ U->timer_deep->refcount;
2168 lua_pop( L, 1); // settings 2170 lua_pop( L, 1); // settings
diff --git a/src/lanes_private.h b/src/lanes_private.h
index a7e21d7..1adfa31 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -112,6 +112,4 @@ static inline int cancel_error( lua_State* L)
112 return lua_error( L); // doesn't return 112 return lua_error( L); // doesn't return
113} 113}
114 114
115
116
117#endif // __lanes_private_h__ \ No newline at end of file 115#endif // __lanes_private_h__ \ No newline at end of file
diff --git a/src/linda.c b/src/linda.c
index 98d2a8e..ee60ebc 100644
--- a/src/linda.c
+++ b/src/linda.c
@@ -46,6 +46,7 @@ THE SOFTWARE.
46*/ 46*/
47struct s_Linda 47struct s_Linda
48{ 48{
49 DeepPrelude prelude; // Deep userdata MUST start with this header
49 SIGNAL_T read_happened; 50 SIGNAL_T read_happened;
50 SIGNAL_T write_happened; 51 SIGNAL_T write_happened;
51 Universe* U; // the universe this linda belongs to 52 Universe* U; // the universe this linda belongs to
@@ -794,6 +795,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
794 s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included 795 s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included
795 if( s) 796 if( s)
796 { 797 {
798 s->prelude.magic.value = DEEP_VERSION.value;
797 SIGNAL_INIT( &s->read_happened); 799 SIGNAL_INIT( &s->read_happened);
798 SIGNAL_INIT( &s->write_happened); 800 SIGNAL_INIT( &s->write_happened);
799 s->U = universe_get( L); 801 s->U = universe_get( L);
@@ -895,8 +897,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
895 push_unique_key( L, NIL_SENTINEL); 897 push_unique_key( L, NIL_SENTINEL);
896 lua_setfield(L, -2, "null"); 898 lua_setfield(L, -2, "null");
897 899
898 luaG_pushdeepversion( L); 900 STACK_END( L, 1);
899 STACK_END( L, 2);
900 return NULL; 901 return NULL;
901 } 902 }
902 903