diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-11 15:14:52 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-04-11 15:14:52 +0200 |
commit | adaa36dbec1ce9aaafd61873b9d3d898a8c240cf (patch) | |
tree | 4c81e8f5983c3d696a636e2cc433ce7c0a9c3dd8 /src/linda.c | |
parent | 1d310e6ecb6e156598337612f16573d9cd284f5e (diff) | |
download | lanes-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.c | 115 |
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 | ||
61 | static void* linda_id( lua_State*, DeepOp); | 61 | static 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 |
274 | DECLARE_CONST_UNIQUE_KEY(BATCH_SENTINEL, 0x2DDFEE0968C62AA7); | ||
274 | LUAG_FUNC( linda_receive) | 275 | LUAG_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); |