aboutsummaryrefslogtreecommitdiff
path: root/src/linda.c
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-11 15:14:52 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-11 15:14:52 +0200
commitadaa36dbec1ce9aaafd61873b9d3d898a8c240cf (patch)
tree4c81e8f5983c3d696a636e2cc433ce7c0a9c3dd8 /src/linda.c
parent1d310e6ecb6e156598337612f16573d9cd284f5e (diff)
downloadlanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.tar.gz
lanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.tar.bz2
lanes-adaa36dbec1ce9aaafd61873b9d3d898a8c240cf.zip
Bring all interesting fixes from the C++ implementation back into the C implementation
Diffstat (limited to 'src/linda.c')
-rw-r--r--src/linda.c115
1 files changed, 59 insertions, 56 deletions
diff --git a/src/linda.c b/src/linda.c
index 8b59790..2128520 100644
--- a/src/linda.c
+++ b/src/linda.c
@@ -52,11 +52,11 @@ struct s_Linda
52 SIGNAL_T read_happened; 52 SIGNAL_T read_happened;
53 SIGNAL_T write_happened; 53 SIGNAL_T write_happened;
54 Universe* U; // the universe this linda belongs to 54 Universe* U; // the universe this linda belongs to
55 ptrdiff_t group; // a group to control keeper allocation between lindas 55 uintptr_t group; // a group to control keeper allocation between lindas
56 enum e_cancel_request simulate_cancel; 56 enum e_cancel_request simulate_cancel;
57 char name[1]; 57 char name[1];
58}; 58};
59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) 59#define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (uintptr_t)linda)
60 60
61static void* linda_id( lua_State*, DeepOp); 61static void* linda_id( lua_State*, DeepOp);
62 62
@@ -125,7 +125,7 @@ LUAG_FUNC( linda_send)
125 enum e_cancel_request cancel = CANCEL_NONE; 125 enum e_cancel_request cancel = CANCEL_NONE;
126 int pushed; 126 int pushed;
127 time_d timeout = -1.0; 127 time_d timeout = -1.0;
128 uint_t key_i = 2; // index of first key, if timeout not there 128 int key_i = 2; // index of first key, if timeout not there
129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided 129 bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided
130 130
131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 131 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
@@ -151,7 +151,7 @@ LUAG_FUNC( linda_send)
151 STACK_GROW( L, 1); 151 STACK_GROW( L, 1);
152 152
153 // make sure there is something to send 153 // make sure there is something to send
154 if( (uint_t)lua_gettop( L) == key_i) 154 if( lua_gettop( L) == key_i)
155 { 155 {
156 if( as_nil_sentinel) 156 if( as_nil_sentinel)
157 { 157 {
@@ -270,16 +270,17 @@ LUAG_FUNC( linda_send)
270 * returns the actual consumed values, or nil if there weren't enough values to consume 270 * returns the actual consumed values, or nil if there weren't enough values to consume
271 * 271 *
272 */ 272 */
273#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" 273 // xxh64 of string "BATCH_SENTINEL" generated at https://www.pelock.com/products/hash-calculator
274DECLARE_CONST_UNIQUE_KEY(BATCH_SENTINEL, 0x2DDFEE0968C62AA7);
274LUAG_FUNC( linda_receive) 275LUAG_FUNC( linda_receive)
275{ 276{
276 struct s_Linda* linda = lua_toLinda( L, 1); 277 struct s_Linda* linda = lua_toLinda( L, 1);
277 int pushed, expected_pushed_min, expected_pushed_max; 278 int pushed, expected_pushed_min, expected_pushed_max;
278 enum e_cancel_request cancel = CANCEL_NONE; 279 enum e_cancel_request cancel = CANCEL_NONE;
279 keeper_api_t keeper_receive; 280 keeper_api_t selected_keeper_receive;
280 281
281 time_d timeout = -1.0; 282 time_d timeout = -1.0;
282 uint_t key_i = 2; 283 int key_i = 2;
283 284
284 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 285 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
285 { 286 {
@@ -294,7 +295,7 @@ LUAG_FUNC( linda_receive)
294 // are we in batched mode? 295 // are we in batched mode?
295 { 296 {
296 int is_batched; 297 int is_batched;
297 lua_pushliteral( L, BATCH_SENTINEL); 298 push_unique_key( L, BATCH_SENTINEL);
298 is_batched = lua501_equal( L, key_i, -1); 299 is_batched = lua501_equal( L, key_i, -1);
299 lua_pop( L, 1); 300 lua_pop( L, 1);
300 if( is_batched) 301 if( is_batched)
@@ -304,7 +305,7 @@ LUAG_FUNC( linda_receive)
304 // make sure the keys are of a valid type 305 // make sure the keys are of a valid type
305 check_key_types( L, key_i, key_i); 306 check_key_types( L, key_i, key_i);
306 // receive multiple values from a single slot 307 // receive multiple values from a single slot
307 keeper_receive = KEEPER_API( receive_batched); 308 selected_keeper_receive = KEEPER_API( receive_batched);
308 // we expect a user-defined amount of return value 309 // we expect a user-defined amount of return value
309 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); 310 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1);
310 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); 311 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min);
@@ -321,7 +322,7 @@ LUAG_FUNC( linda_receive)
321 // make sure the keys are of a valid type 322 // make sure the keys are of a valid type
322 check_key_types( L, key_i, lua_gettop( L)); 323 check_key_types( L, key_i, lua_gettop( L));
323 // receive a single value, checking multiple slots 324 // receive a single value, checking multiple slots
324 keeper_receive = KEEPER_API( receive); 325 selected_keeper_receive = KEEPER_API( receive);
325 // we expect a single (value, key) pair of returned values 326 // we expect a single (value, key) pair of returned values
326 expected_pushed_min = expected_pushed_max = 2; 327 expected_pushed_min = expected_pushed_max = 2;
327 } 328 }
@@ -347,7 +348,7 @@ LUAG_FUNC( linda_receive)
347 } 348 }
348 349
349 // all arguments of receive() but the first are passed to the keeper's receive function 350 // all arguments of receive() but the first are passed to the keeper's receive function
350 pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); 351 pushed = keeper_call( linda->U, K->L, selected_keeper_receive, L, linda, key_i);
351 if( pushed < 0) 352 if( pushed < 0)
352 { 353 {
353 break; 354 break;
@@ -511,29 +512,27 @@ LUAG_FUNC( linda_get)
511 512
512 // make sure the key is of a valid type (throws an error if not the case) 513 // make sure the key is of a valid type (throws an error if not the case)
513 check_key_types( L, 2, 2); 514 check_key_types( L, 2, 2);
514 {
515 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
516 515
517 if( linda->simulate_cancel == CANCEL_NONE) 516 if( linda->simulate_cancel == CANCEL_NONE)
518 { 517 {
519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); 518 Keeper* const K = which_keeper(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
520 if( pushed > 0) 519 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2);
521 { 520 if( pushed > 0)
522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
523 }
524 }
525 else // linda is cancelled
526 {
527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1;
530 }
531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0)
533 { 521 {
534 return luaL_error( L, "tried to copy unsupported types"); 522 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
535 } 523 }
536 } 524 }
525 else // linda is cancelled
526 {
527 // do nothing and return lanes.cancel_error
528 push_unique_key( L, CANCEL_ERROR);
529 pushed = 1;
530 }
531 // an error can be raised if we attempt to read an unregistered function
532 if( pushed < 0)
533 {
534 return luaL_error( L, "tried to copy unsupported types");
535 }
537 536
538 return pushed; 537 return pushed;
539} 538}
@@ -557,26 +556,23 @@ LUAG_FUNC( linda_limit)
557 // make sure the key is of a valid type 556 // make sure the key is of a valid type
558 check_key_types( L, 2, 2); 557 check_key_types( L, 2, 2);
559 558
559 if( linda->simulate_cancel == CANCEL_NONE)
560 { 560 {
561 Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 561 Keeper* const K = which_keeper(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda));
562 562 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
563 if( linda->simulate_cancel == CANCEL_NONE) 563 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
564 { 564 if( pushed == 1)
565 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
566 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
567 if( pushed == 1)
568 {
569 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
570 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
571 }
572 }
573 else // linda is cancelled
574 { 565 {
575 // do nothing and return lanes.cancel_error 566 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
576 push_unique_key( L, CANCEL_ERROR); 567 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
577 pushed = 1;
578 } 568 }
579 } 569 }
570 else // linda is cancelled
571 {
572 // do nothing and return lanes.cancel_error
573 push_unique_key( L, CANCEL_ERROR);
574 pushed = 1;
575 }
580 // propagate pushed boolean if any 576 // propagate pushed boolean if any
581 return pushed; 577 return pushed;
582} 578}
@@ -762,6 +758,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
762 { 758 {
763 case eDO_new: 759 case eDO_new:
764 { 760 {
761 Universe* const U = universe_get(L);
765 struct s_Linda* s; 762 struct s_Linda* s;
766 size_t name_len = 0; 763 size_t name_len = 0;
767 char const* linda_name = NULL; 764 char const* linda_name = NULL;
@@ -795,7 +792,6 @@ static void* linda_id( lua_State* L, DeepOp op_)
795 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda 792 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
796 */ 793 */
797 { 794 {
798 Universe* const U = universe_get(L);
799 AllocatorDefinition* const allocD = &U->internal_allocator; 795 AllocatorDefinition* const allocD = &U->internal_allocator;
800 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included 796 s = (struct s_Linda*) allocD->allocF(allocD->allocUD, NULL, 0, sizeof(struct s_Linda) + name_len); // terminating 0 is already included
801 } 797 }
@@ -804,7 +800,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
804 s->prelude.magic.value = DEEP_VERSION.value; 800 s->prelude.magic.value = DEEP_VERSION.value;
805 SIGNAL_INIT( &s->read_happened); 801 SIGNAL_INIT( &s->read_happened);
806 SIGNAL_INIT( &s->write_happened); 802 SIGNAL_INIT( &s->write_happened);
807 s->U = universe_get( L); 803 s->U = U;
808 s->simulate_cancel = CANCEL_NONE; 804 s->simulate_cancel = CANCEL_NONE;
809 s->group = linda_group << KEEPER_MAGIC_SHIFT; 805 s->group = linda_group << KEEPER_MAGIC_SHIFT;
810 s->name[0] = 0; 806 s->name[0] = 0;
@@ -815,25 +811,32 @@ static void* linda_id( lua_State* L, DeepOp op_)
815 811
816 case eDO_delete: 812 case eDO_delete:
817 { 813 {
818 Keeper* K; 814 Keeper* myK;
819 struct s_Linda* linda = lua_touserdata( L, 1); 815 struct s_Linda* linda = lua_touserdata( L, 1);
820 ASSERT_L( linda); 816 ASSERT_L( linda);
821 817
822 // Clean associated structures in the keeper state. 818 // Clean associated structures in the keeper state.
823 K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); 819 myK = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda));
824 if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) 820 if (myK)
825 { 821 {
822 // if collected from my own keeper, we can't acquire/release it
823 // because we are already inside a protected area, and trying to do so would deadlock!
824 bool_t const need_acquire_release = (myK->L != L);
825 // Clean associated structures in the keeper state.
826 Keeper* const K = need_acquire_release ? keeper_acquire(linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)) : myK;
826 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 827 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
827 keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); 828 keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0);
829 if(need_acquire_release)
830 {
831 keeper_release(K);
832 }
828 } 833 }
829 keeper_release( K);
830 834
831 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? 835 // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right?
832 SIGNAL_FREE( &linda->read_happened); 836 SIGNAL_FREE( &linda->read_happened);
833 SIGNAL_FREE( &linda->write_happened); 837 SIGNAL_FREE( &linda->write_happened);
834 { 838 {
835 Universe* const U = universe_get(L); 839 AllocatorDefinition* const allocD = &linda->U->internal_allocator;
836 AllocatorDefinition* const allocD = &U->internal_allocator;
837 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0); 840 (void) allocD->allocF(allocD->allocUD, linda, sizeof(struct s_Linda) + strlen(linda->name), 0);
838 } 841 }
839 return NULL; 842 return NULL;
@@ -901,7 +904,7 @@ static void* linda_id( lua_State* L, DeepOp op_)
901 lua_setfield( L, -2, "dump"); 904 lua_setfield( L, -2, "dump");
902 905
903 // some constants 906 // some constants
904 lua_pushliteral( L, BATCH_SENTINEL); 907 push_unique_key( L, BATCH_SENTINEL);
905 lua_setfield( L, -2, "batched"); 908 lua_setfield( L, -2, "batched");
906 909
907 push_unique_key( L, NIL_SENTINEL); 910 push_unique_key( L, NIL_SENTINEL);