diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2018-11-08 17:32:05 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2018-11-08 17:32:05 +0100 |
commit | 0cc1c9c9dcea5955f7dab921d9a2fff78c4e1729 (patch) | |
tree | 5c35acf11087f9b60b24599695f1d3c348ebaa25 | |
parent | a142eb1e1ee81919d10b55bb7fa2e33636098d85 (diff) | |
download | lanes-0cc1c9c9dcea5955f7dab921d9a2fff78c4e1729.tar.gz lanes-0cc1c9c9dcea5955f7dab921d9a2fff78c4e1729.tar.bz2 lanes-0cc1c9c9dcea5955f7dab921d9a2fff78c4e1729.zip |
Make sure any linda operation that can raise an error won't ever leave a mutex unreleased
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | docs/index.html | 2 | ||||
-rw-r--r-- | src/keeper.c | 11 | ||||
-rw-r--r-- | src/keeper.h | 1 | ||||
-rw-r--r-- | src/lanes.c | 99 | ||||
-rw-r--r-- | tests/deadlock.lua | 28 |
6 files changed, 101 insertions, 44 deletions
@@ -1,5 +1,9 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 133: BGe 8-Nov-18 | ||
4 | * Make sure any linda operation that can raise an error won't ever leave a mutex unreleased | ||
5 | * lane:join() now returns nil, "timeout" in case of timeout | ||
6 | |||
3 | CHANGE 132: BGe 7-Nov-18 | 7 | CHANGE 132: BGe 7-Nov-18 |
4 | * __lanesclone mechanism should actually work now | 8 | * __lanesclone mechanism should actually work now |
5 | 9 | ||
diff --git a/docs/index.html b/docs/index.html index 9c76bef..ef227c8 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -855,7 +855,7 @@ | |||
855 | </pre></td></tr></table> | 855 | </pre></td></tr></table> |
856 | 856 | ||
857 | <p> | 857 | <p> |
858 | Waits until the lane finishes, or <tt>timeout</tt> seconds have passed. Returns <tt>nil</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error, <tt>nil, "killed"</tt> if forcefully killed (starting with v3.3.0), or the return values of the lane. | 858 | Waits until the lane finishes, or <tt>timeout</tt> seconds have passed. Returns <tt>nil, "timeout"</tt> on timeout (since v3.13), <tt>nil,err,stack_tbl</tt> if the lane hit an error, <tt>nil, "killed"</tt> if forcefully killed (starting with v3.3.0), or the return values of the lane. |
859 | Unlike in reading the results in table fashion, errors are not propagated. | 859 | Unlike in reading the results in table fashion, errors are not propagated. |
860 | </p> | 860 | </p> |
861 | 861 | ||
diff --git a/src/keeper.c b/src/keeper.c index 715583b..b67bee2 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -188,7 +188,7 @@ static void push_table( lua_State* L, int idx_) | |||
188 | 188 | ||
189 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) | 189 | int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t magic_) |
190 | { | 190 | { |
191 | Keeper* const K = keeper_acquire( U->keepers, magic_); | 191 | Keeper* const K = which_keeper( U->keepers, magic_); |
192 | lua_State* const KL = K ? K->L : NULL; | 192 | lua_State* const KL = K ? K->L : NULL; |
193 | if( KL == NULL) return 0; | 193 | if( KL == NULL) return 0; |
194 | STACK_GROW( KL, 4); | 194 | STACK_GROW( KL, 4); |
@@ -233,7 +233,6 @@ int keeper_push_linda_storage( Universe* U, lua_State* L, void* ptr_, ptrdiff_t | |||
233 | STACK_END( L, 1); | 233 | STACK_END( L, 1); |
234 | lua_pop( KL, 1); // | 234 | lua_pop( KL, 1); // |
235 | STACK_END( KL, 0); | 235 | STACK_END( KL, 0); |
236 | keeper_release( K); | ||
237 | return 1; | 236 | return 1; |
238 | } | 237 | } |
239 | 238 | ||
@@ -715,6 +714,14 @@ void init_keepers( Universe* U, lua_State* L) | |||
715 | STACK_END( L, 0); | 714 | STACK_END( L, 0); |
716 | } | 715 | } |
717 | 716 | ||
717 | // should be called only when inside a keeper_acquire/keeper_release pair (see linda_protected_call) | ||
718 | Keeper* which_keeper(Keepers* keepers_, ptrdiff_t magic_) | ||
719 | { | ||
720 | int const nbKeepers = keepers_->nb_keepers; | ||
721 | unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); | ||
722 | return &keepers_->keeper_array[i]; | ||
723 | } | ||
724 | |||
718 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) | 725 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_) |
719 | { | 726 | { |
720 | int const nbKeepers = keepers_->nb_keepers; | 727 | int const nbKeepers = keepers_->nb_keepers; |
diff --git a/src/keeper.h b/src/keeper.h index 37922fb..60410da 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -29,6 +29,7 @@ typedef struct s_Keepers Keepers; | |||
29 | void init_keepers( Universe* U, lua_State* L); | 29 | void init_keepers( Universe* U, lua_State* L); |
30 | void close_keepers( Universe* U, lua_State* L); | 30 | void close_keepers( Universe* U, lua_State* L); |
31 | 31 | ||
32 | Keeper* which_keeper( Keepers* keepers_, ptrdiff_t magic_); | ||
32 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_); | 33 | Keeper* keeper_acquire( Keepers* keepers_, ptrdiff_t magic_); |
33 | #define KEEPER_MAGIC_SHIFT 3 | 34 | #define KEEPER_MAGIC_SHIFT 3 |
34 | void keeper_release( Keeper* K); | 35 | void keeper_release( Keeper* K); |
diff --git a/src/lanes.c b/src/lanes.c index c3e64fb..72f9dd6 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -453,6 +453,34 @@ static void check_key_types( lua_State* L, int start_, int end_) | |||
453 | } | 453 | } |
454 | } | 454 | } |
455 | 455 | ||
456 | LUAG_FUNC( linda_protected_call) | ||
457 | { | ||
458 | int rc = LUA_OK; | ||
459 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
460 | |||
461 | // acquire the keeper | ||
462 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)); | ||
463 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
464 | if( KL == NULL) return 0; | ||
465 | |||
466 | // retrieve the actual function to be called and move it before the arguments | ||
467 | lua_pushvalue( L, lua_upvalueindex( 1)); | ||
468 | lua_insert( L, 1); | ||
469 | // do a protected call | ||
470 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); | ||
471 | |||
472 | // release the keeper | ||
473 | keeper_release( K); | ||
474 | |||
475 | // if there was an error, forward it | ||
476 | if( rc != LUA_OK) | ||
477 | { | ||
478 | return lua_error( L); | ||
479 | } | ||
480 | // return whatever the actual operation provided | ||
481 | return lua_gettop( L); | ||
482 | } | ||
483 | |||
456 | /* | 484 | /* |
457 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | 485 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) |
458 | * | 486 | * |
@@ -514,7 +542,7 @@ LUAG_FUNC( linda_send) | |||
514 | { | 542 | { |
515 | bool_t try_again = TRUE; | 543 | bool_t try_again = TRUE; |
516 | Lane* const s = get_lane_from_registry( L); | 544 | Lane* const s = get_lane_from_registry( L); |
517 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 545 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
518 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | 546 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' |
519 | if( KL == NULL) return 0; | 547 | if( KL == NULL) return 0; |
520 | STACK_CHECK( KL); | 548 | STACK_CHECK( KL); |
@@ -578,10 +606,8 @@ LUAG_FUNC( linda_send) | |||
578 | } | 606 | } |
579 | } | 607 | } |
580 | STACK_END( KL, 0); | 608 | STACK_END( KL, 0); |
581 | keeper_release( K); | ||
582 | } | 609 | } |
583 | 610 | ||
584 | // must trigger error after keeper state has been released | ||
585 | if( pushed < 0) | 611 | if( pushed < 0) |
586 | { | 612 | { |
587 | return luaL_error( L, "tried to copy unsupported types"); | 613 | return luaL_error( L, "tried to copy unsupported types"); |
@@ -676,7 +702,7 @@ LUAG_FUNC( linda_receive) | |||
676 | { | 702 | { |
677 | bool_t try_again = TRUE; | 703 | bool_t try_again = TRUE; |
678 | Lane* const s = get_lane_from_registry( L); | 704 | Lane* const s = get_lane_from_registry( L); |
679 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 705 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
680 | if( K == NULL) return 0; | 706 | if( K == NULL) return 0; |
681 | for( ;;) | 707 | for( ;;) |
682 | { | 708 | { |
@@ -735,10 +761,8 @@ LUAG_FUNC( linda_receive) | |||
735 | } | 761 | } |
736 | } | 762 | } |
737 | } | 763 | } |
738 | keeper_release( K); | ||
739 | } | 764 | } |
740 | 765 | ||
741 | // must trigger error after keeper state has been released | ||
742 | if( pushed < 0) | 766 | if( pushed < 0) |
743 | { | 767 | { |
744 | return luaL_error( L, "tried to copy unsupported types"); | 768 | return luaL_error( L, "tried to copy unsupported types"); |
@@ -779,8 +803,7 @@ LUAG_FUNC( linda_set) | |||
779 | check_key_types( L, 2, 2); | 803 | check_key_types( L, 2, 2); |
780 | 804 | ||
781 | { | 805 | { |
782 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 806 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
783 | if( K == NULL) return 0; | ||
784 | 807 | ||
785 | if( linda->simulate_cancel == CANCEL_NONE) | 808 | if( linda->simulate_cancel == CANCEL_NONE) |
786 | { | 809 | { |
@@ -813,7 +836,6 @@ LUAG_FUNC( linda_set) | |||
813 | push_unique_key( L, CANCEL_ERROR); | 836 | push_unique_key( L, CANCEL_ERROR); |
814 | pushed = 1; | 837 | pushed = 1; |
815 | } | 838 | } |
816 | keeper_release( K); | ||
817 | } | 839 | } |
818 | 840 | ||
819 | // must trigger any error after keeper state has been released | 841 | // must trigger any error after keeper state has been released |
@@ -835,10 +857,8 @@ LUAG_FUNC( linda_count) | |||
835 | check_key_types( L, 2, lua_gettop( L)); | 857 | check_key_types( L, 2, lua_gettop( L)); |
836 | 858 | ||
837 | { | 859 | { |
838 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 860 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
839 | if( K == NULL) return 0; | ||
840 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); | 861 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); |
841 | keeper_release( K); | ||
842 | if( pushed < 0) | 862 | if( pushed < 0) |
843 | { | 863 | { |
844 | return luaL_error( L, "tried to count an invalid key"); | 864 | return luaL_error( L, "tried to count an invalid key"); |
@@ -864,8 +884,7 @@ LUAG_FUNC( linda_get) | |||
864 | // make sure the key is of a valid type (throws an error if not the case) | 884 | // make sure the key is of a valid type (throws an error if not the case) |
865 | check_key_types( L, 2, 2); | 885 | check_key_types( L, 2, 2); |
866 | { | 886 | { |
867 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 887 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
868 | if( K == NULL) return 0; | ||
869 | 888 | ||
870 | if( linda->simulate_cancel == CANCEL_NONE) | 889 | if( linda->simulate_cancel == CANCEL_NONE) |
871 | { | 890 | { |
@@ -881,9 +900,7 @@ LUAG_FUNC( linda_get) | |||
881 | push_unique_key( L, CANCEL_ERROR); | 900 | push_unique_key( L, CANCEL_ERROR); |
882 | pushed = 1; | 901 | pushed = 1; |
883 | } | 902 | } |
884 | keeper_release( K); | 903 | // an error can be raised if we attempt to read an unregistered function |
885 | // must trigger error after keeper state has been released | ||
886 | // (an error can be raised if we attempt to read an unregistered function) | ||
887 | if( pushed < 0) | 904 | if( pushed < 0) |
888 | { | 905 | { |
889 | return luaL_error( L, "tried to copy unsupported types"); | 906 | return luaL_error( L, "tried to copy unsupported types"); |
@@ -913,8 +930,7 @@ LUAG_FUNC( linda_limit) | |||
913 | check_key_types( L, 2, 2); | 930 | check_key_types( L, 2, 2); |
914 | 931 | ||
915 | { | 932 | { |
916 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 933 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
917 | if( K == NULL) return 0; | ||
918 | 934 | ||
919 | if( linda->simulate_cancel == CANCEL_NONE) | 935 | if( linda->simulate_cancel == CANCEL_NONE) |
920 | { | 936 | { |
@@ -932,7 +948,6 @@ LUAG_FUNC( linda_limit) | |||
932 | push_unique_key( L, CANCEL_ERROR); | 948 | push_unique_key( L, CANCEL_ERROR); |
933 | pushed = 1; | 949 | pushed = 1; |
934 | } | 950 | } |
935 | keeper_release( K); | ||
936 | } | 951 | } |
937 | // propagate pushed boolean if any | 952 | // propagate pushed boolean if any |
938 | return pushed; | 953 | return pushed; |
@@ -948,15 +963,11 @@ LUAG_FUNC( linda_cancel) | |||
948 | { | 963 | { |
949 | struct s_Linda* linda = lua_toLinda( L, 1); | 964 | struct s_Linda* linda = lua_toLinda( L, 1); |
950 | char const* who = luaL_optstring( L, 2, "both"); | 965 | char const* who = luaL_optstring( L, 2, "both"); |
951 | Keeper* K; | 966 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
952 | 967 | ||
953 | // make sure we got 3 arguments: the linda, a key and a limit | 968 | // make sure we got 3 arguments: the linda, a key and a limit |
954 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); | 969 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); |
955 | 970 | ||
956 | // signalling must be done from inside the K locking area | ||
957 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
958 | if( K == NULL) return 0; | ||
959 | |||
960 | linda->simulate_cancel = CANCEL_SOFT; | 971 | linda->simulate_cancel = CANCEL_SOFT; |
961 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | 972 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up |
962 | { | 973 | { |
@@ -977,14 +988,6 @@ LUAG_FUNC( linda_cancel) | |||
977 | } | 988 | } |
978 | else | 989 | else |
979 | { | 990 | { |
980 | // error ... | ||
981 | linda = NULL; | ||
982 | } | ||
983 | keeper_release( K); | ||
984 | |||
985 | // ... but we must raise it outside the lock | ||
986 | if( !linda) | ||
987 | { | ||
988 | return luaL_error( L, "unknown wake hint '%s'", who); | 991 | return luaL_error( L, "unknown wake hint '%s'", who); |
989 | } | 992 | } |
990 | return 0; | 993 | return 0; |
@@ -1184,18 +1187,16 @@ static void* linda_id( lua_State* L, DeepOp op_) | |||
1184 | struct s_Linda* linda = lua_touserdata( L, 1); | 1187 | struct s_Linda* linda = lua_touserdata( L, 1); |
1185 | ASSERT_L( linda); | 1188 | ASSERT_L( linda); |
1186 | 1189 | ||
1187 | /* Clean associated structures in the keeper state. | 1190 | // Clean associated structures in the keeper state. |
1188 | */ | ||
1189 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | 1191 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); |
1190 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | 1192 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) |
1191 | { | 1193 | { |
1194 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
1192 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); | 1195 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); |
1193 | } | 1196 | } |
1194 | keeper_release( K); | 1197 | keeper_release( K); |
1195 | 1198 | ||
1196 | /* There aren't any lanes waiting on these lindas, since all proxies | 1199 | // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? |
1197 | * have been gc'ed. Right? | ||
1198 | */ | ||
1199 | SIGNAL_FREE( &linda->read_happened); | 1200 | SIGNAL_FREE( &linda->read_happened); |
1200 | SIGNAL_FREE( &linda->write_happened); | 1201 | SIGNAL_FREE( &linda->write_happened); |
1201 | free( linda); | 1202 | free( linda); |
@@ -1225,34 +1226,46 @@ static void* linda_id( lua_State* L, DeepOp op_) | |||
1225 | lua_pushcfunction( L, LG_linda_concat); | 1226 | lua_pushcfunction( L, LG_linda_concat); |
1226 | lua_setfield( L, -2, "__concat"); | 1227 | lua_setfield( L, -2, "__concat"); |
1227 | 1228 | ||
1228 | // [-1]: linda metatable | 1229 | // protected calls, to ensure associated keeper is always released even in case of error |
1230 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | ||
1231 | // note that this kind of thing can break function lookup as we use the function pointer here and there | ||
1232 | |||
1229 | lua_pushcfunction( L, LG_linda_send); | 1233 | lua_pushcfunction( L, LG_linda_send); |
1234 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1230 | lua_setfield( L, -2, "send"); | 1235 | lua_setfield( L, -2, "send"); |
1231 | 1236 | ||
1232 | lua_pushcfunction( L, LG_linda_receive); | 1237 | lua_pushcfunction( L, LG_linda_receive); |
1238 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1233 | lua_setfield( L, -2, "receive"); | 1239 | lua_setfield( L, -2, "receive"); |
1234 | 1240 | ||
1235 | lua_pushcfunction( L, LG_linda_limit); | 1241 | lua_pushcfunction( L, LG_linda_limit); |
1242 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1236 | lua_setfield( L, -2, "limit"); | 1243 | lua_setfield( L, -2, "limit"); |
1237 | 1244 | ||
1238 | lua_pushcfunction( L, LG_linda_set); | 1245 | lua_pushcfunction( L, LG_linda_set); |
1246 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1239 | lua_setfield( L, -2, "set"); | 1247 | lua_setfield( L, -2, "set"); |
1240 | 1248 | ||
1241 | lua_pushcfunction( L, LG_linda_count); | 1249 | lua_pushcfunction( L, LG_linda_count); |
1250 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1242 | lua_setfield( L, -2, "count"); | 1251 | lua_setfield( L, -2, "count"); |
1243 | 1252 | ||
1244 | lua_pushcfunction( L, LG_linda_get); | 1253 | lua_pushcfunction( L, LG_linda_get); |
1254 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1245 | lua_setfield( L, -2, "get"); | 1255 | lua_setfield( L, -2, "get"); |
1246 | 1256 | ||
1247 | lua_pushcfunction( L, LG_linda_cancel); | 1257 | lua_pushcfunction( L, LG_linda_cancel); |
1258 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1248 | lua_setfield( L, -2, "cancel"); | 1259 | lua_setfield( L, -2, "cancel"); |
1249 | 1260 | ||
1250 | lua_pushcfunction( L, LG_linda_deep); | 1261 | lua_pushcfunction( L, LG_linda_deep); |
1251 | lua_setfield( L, -2, "deep"); | 1262 | lua_setfield( L, -2, "deep"); |
1252 | 1263 | ||
1253 | lua_pushcfunction( L, LG_linda_dump); | 1264 | lua_pushcfunction( L, LG_linda_dump); |
1265 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
1254 | lua_setfield( L, -2, "dump"); | 1266 | lua_setfield( L, -2, "dump"); |
1255 | 1267 | ||
1268 | // some constants | ||
1256 | lua_pushliteral( L, BATCH_SENTINEL); | 1269 | lua_pushliteral( L, BATCH_SENTINEL); |
1257 | lua_setfield(L, -2, "batched"); | 1270 | lua_setfield(L, -2, "batched"); |
1258 | 1271 | ||
@@ -2074,13 +2087,14 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void* vs) | |||
2074 | // But don't register it in the lookup database because of the s_lane pointer upvalue | 2087 | // But don't register it in the lookup database because of the s_lane pointer upvalue |
2075 | lua_pushlightuserdata( L, s); | 2088 | lua_pushlightuserdata( L, s); |
2076 | lua_pushcclosure( L, LG_set_debug_threadname, 1); | 2089 | lua_pushcclosure( L, LG_set_debug_threadname, 1); |
2077 | lua_setglobal( L, "set_debug_threadname" ); | 2090 | lua_setglobal( L, "set_debug_threadname"); |
2078 | 2091 | ||
2079 | // Tie "cancel_test()" to the state | 2092 | // Tie "cancel_test()" to the state |
2080 | lua_pushcfunction( L, LG_cancel_test); | 2093 | lua_pushcfunction( L, LG_cancel_test); |
2081 | populate_func_lookup_table( L, -1, "cancel_test"); | 2094 | populate_func_lookup_table( L, -1, "cancel_test"); |
2082 | lua_setglobal( L, "cancel_test"); | 2095 | lua_setglobal( L, "cancel_test"); |
2083 | 2096 | ||
2097 | // this could be done in lane_new before the lane body function is pushed on the stack to avoid unnecessary stack slot shifting around | ||
2084 | #if ERROR_FULL_STACK | 2098 | #if ERROR_FULL_STACK |
2085 | // Tie "set_error_reporting()" to the state | 2099 | // Tie "set_error_reporting()" to the state |
2086 | lua_pushcfunction( L, LG_set_error_reporting); | 2100 | lua_pushcfunction( L, LG_set_error_reporting); |
@@ -2654,7 +2668,10 @@ LUAG_FUNC( thread_join) | |||
2654 | bool_t done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); | 2668 | bool_t done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); |
2655 | if( !done || !L2) | 2669 | if( !done || !L2) |
2656 | { | 2670 | { |
2657 | return 0; // timeout: pushes none, leaves 'L2' alive | 2671 | STACK_GROW( L, 2); |
2672 | lua_pushnil( L); | ||
2673 | lua_pushliteral( L, "timeout"); | ||
2674 | return 2; | ||
2658 | } | 2675 | } |
2659 | 2676 | ||
2660 | STACK_CHECK( L); | 2677 | STACK_CHECK( L); |
diff --git a/tests/deadlock.lua b/tests/deadlock.lua new file mode 100644 index 0000000..8ccae89 --- /dev/null +++ b/tests/deadlock.lua | |||
@@ -0,0 +1,28 @@ | |||
1 | local lanes = require('lanes').configure() | ||
2 | local linda = lanes.linda "deadlock_linda" | ||
3 | |||
4 | local do_extra_stuff = true | ||
5 | |||
6 | if do_extra_stuff then | ||
7 | -- just something to make send() succeed and receive() fail: | ||
8 | local payload = { lanes.require('socket').connect } | ||
9 | |||
10 | -- lane generator | ||
11 | local g = lanes.gen('*', function() | ||
12 | set_debug_threadname "deadlock_lane" | ||
13 | print("In lane 1:", table.unpack{pcall(linda.receive, linda, 'tmp')}) | ||
14 | print("In lane 2:", table.unpack{pcall(linda.receive, linda, 'tmp')}) | ||
15 | return 33, 55 | ||
16 | end) | ||
17 | |||
18 | -- send payload twice | ||
19 | linda:send('tmp', payload, payload) | ||
20 | local h = g() | ||
21 | local err, stack = h:join() | ||
22 | print('we reach here and then deadlock', err, stack) | ||
23 | end | ||
24 | -- start lane | ||
25 | |||
26 | -- wait some | ||
27 | lanes.sleep(2) | ||
28 | print('we never reach here') \ No newline at end of file | ||