From 089f68cf6d18799028eb12cc16bbe2f8cf5e57c3 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Thu, 28 Mar 2024 15:29:59 +0100 Subject: linda.batched is now a lightuserdata instead of a string. plus some reformatting. --- src/linda.cpp | 679 +++++++++++++++++++++++++++----------------------------- src/uniquekey.h | 8 +- 2 files changed, 335 insertions(+), 352 deletions(-) diff --git a/src/linda.cpp b/src/linda.cpp index dc5864b..77dc4cb 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -1,5 +1,5 @@ /* - * LINDA.C Copyright (c) 2018, Benoit Germain + * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain * * Linda deep userdata. */ @@ -30,20 +30,15 @@ THE SOFTWARE. =============================================================================== */ -#include -#include -#include - -#include "threading.h" #include "compat.h" -#include "tools.h" -#include "universe.h" -#include "keeper.h" #include "deep.h" +#include "keeper.h" #include "lanes_private.h" +#include "threading.h" +#include "tools.h" +#include "universe.h" #include -#include #include /* @@ -166,11 +161,10 @@ static inline Linda* lua_toLinda(lua_State* L, int idx_) static void check_key_types(lua_State* L, int start_, int end_) { - int i; - for( i = start_; i <= end_; ++ i) + for (int i{ start_ }; i <= end_; ++i) { - int t = lua_type( L, i); - if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) + int const t{ lua_type(L, i) }; + if (t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) { continue; } @@ -185,26 +179,27 @@ LUAG_FUNC(linda_protected_call) Linda* const linda{ lua_toLinda(L, 1) }; // acquire the keeper - Keeper* K = keeper_acquire( linda->U->keepers, linda->hashSeed()); - lua_State* KL = K ? K->L : nullptr; - if( KL == nullptr) return 0; + Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; + lua_State* const KL{ K ? K->L : nullptr }; + if (KL == nullptr) + return 0; // retrieve the actual function to be called and move it before the arguments - lua_pushvalue( L, lua_upvalueindex( 1)); - lua_insert( L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_insert(L, 1); // do a protected call int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) }; // release the keeper - keeper_release( K); + keeper_release(K); // if there was an error, forward it - if( rc != LUA_OK) + if (rc != LUA_OK) { raise_lua_error(L); } // return whatever the actual operation provided - return lua_gettop( L); + return lua_gettop(L); } // ################################################################################################# @@ -218,7 +213,7 @@ LUAG_FUNC(linda_protected_call) * 'false' for timeout (only happens when the queue size is limited) * nil, CANCEL_ERROR if cancelled */ -LUAG_FUNC( linda_send) +LUAG_FUNC(linda_send) { Linda* const linda{ lua_toLinda(L, 1) }; bool ret{ false }; @@ -227,56 +222,57 @@ LUAG_FUNC( linda_send) time_d timeout = -1.0; int key_i = 2; // index of first key, if timeout not there - if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion + if (lua_type(L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion { - timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); - ++ key_i; + timeout = SIGNAL_TIMEOUT_PREPARE(lua_tonumber(L, 2)); + ++key_i; } - else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key + else if (lua_isnil(L, 2)) // alternate explicit "no timeout" by passing nil before the key { - ++ key_i; + ++key_i; } bool const as_nil_sentinel{ NIL_SENTINEL.equals(L, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided - if( as_nil_sentinel) + if (as_nil_sentinel) { // the real key to send data to is after the NIL_SENTINEL marker - ++ key_i; + ++key_i; } // make sure the key is of a valid type - check_key_types( L, key_i, key_i); + check_key_types(L, key_i, key_i); - STACK_GROW( L, 1); + STACK_GROW(L, 1); // make sure there is something to send - if( lua_gettop( L) == key_i) + if (lua_gettop(L) == key_i) { - if( as_nil_sentinel) + if (as_nil_sentinel) { // send a single nil if nothing is provided NIL_SENTINEL.push(L); } else { - return luaL_error( L, "no data to send"); + return luaL_error(L, "no data to send"); } } // convert nils to some special non-nil sentinel in sent values - keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); + keeper_toggle_nil_sentinels(L, key_i + 1, eLM_ToKeeper); { - Lane* const s{ get_lane_from_registry(L) }; + Lane* const lane{ get_lane_from_registry(L) }; Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; - lua_State* KL = K ? K->L : nullptr; - if( KL == nullptr) return 0; + lua_State* const KL{ K ? K->L : nullptr }; + if (KL == nullptr) + return 0; STACK_CHECK_START_REL(KL, 0); - for(bool try_again{ true };;) + for (bool try_again{ true };;) { - if( s != nullptr) + if (lane != nullptr) { - cancel = s->cancel_request; + cancel = lane->cancel_request; } cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; // if user wants to cancel, or looped because of a timeout, the call returns without sending anything @@ -286,60 +282,60 @@ LUAG_FUNC( linda_send) break; } - STACK_CHECK( KL, 0); - pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); - if( pushed < 0) + STACK_CHECK(KL, 0); + pushed = keeper_call(linda->U, KL, KEEPER_API(send), L, linda, key_i); + if (pushed < 0) { break; } - ASSERT_L( pushed == 1); + ASSERT_L(pushed == 1); - ret = lua_toboolean( L, -1) ? true : false; - lua_pop( L, 1); + ret = lua_toboolean(L, -1) ? true : false; + lua_pop(L, 1); - if( ret) + if (ret) { // Wake up ALL waiting threads - SIGNAL_ALL( &linda->write_happened); + SIGNAL_ALL(&linda->write_happened); break; } // instant timout to bypass the wait syscall - if( timeout == 0.0) + if (timeout == 0.0) { - break; /* no wait; instant timeout */ + break; /* no wait; instant timeout */ } // storage limit hit, wait until timeout or signalled that we should try again { enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings - if( s != nullptr) + if (lane != nullptr) { // change status of lane to "waiting" - prev_status = s->status; // RUNNING, most likely - ASSERT_L( prev_status == RUNNING); // but check, just in case - s->status = WAITING; - ASSERT_L( s->waiting_on == nullptr); - s->waiting_on = &linda->read_happened; + prev_status = lane->status; // RUNNING, most likely + ASSERT_L(prev_status == RUNNING); // but check, just in case + lane->status = WAITING; + ASSERT_L(lane->waiting_on == nullptr); + lane->waiting_on = &linda->read_happened; } // could not send because no room: wait until some data was read before trying again, or until timeout is reached - try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); - if( s != nullptr) + try_again = SIGNAL_WAIT(&linda->read_happened, &K->keeper_cs, timeout); + if (lane != nullptr) { - s->waiting_on = nullptr; - s->status = prev_status; + lane->waiting_on = nullptr; + lane->status = prev_status; } } } - STACK_CHECK( KL, 0); + STACK_CHECK(KL, 0); } - if( pushed < 0) + if (pushed < 0) { - return luaL_error( L, "tried to copy unsupported types"); + return luaL_error(L, "tried to copy unsupported types"); } - switch( cancel) + switch (cancel) { case CancelRequest::Soft: // if user wants to soft-cancel, the call returns lanes.cancel_error @@ -351,7 +347,7 @@ LUAG_FUNC( linda_send) raise_cancel_error(L); // raises an error and doesn't return default: - lua_pushboolean( L, ret); // true (success) or false (timeout) + lua_pushboolean(L, ret); // true (success) or false (timeout) return 1; } } @@ -369,132 +365,130 @@ LUAG_FUNC( linda_send) * returns the actual consumed values, or nil if there weren't enough values to consume * */ -#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" -LUAG_FUNC( linda_receive) +// xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator +static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull }; +LUAG_FUNC(linda_receive) { Linda* const linda{ lua_toLinda(L, 1) }; - int pushed, expected_pushed_min, expected_pushed_max; - CancelRequest cancel{ CancelRequest::None }; - keeper_api_t keeper_receive; - - time_d timeout = -1.0; - int key_i = 2; - if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion + time_d timeout{ -1.0 }; + int key_i{ 2 }; + + if (lua_type(L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion { - timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); - ++ key_i; + timeout = SIGNAL_TIMEOUT_PREPARE(lua_tonumber(L, 2)); + ++key_i; } - else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key + else if (lua_isnil(L, 2)) // alternate explicit "no timeout" by passing nil before the key { - ++ key_i; + ++key_i; } + keeper_api_t keeper_receive; + int expected_pushed_min{ 0 }, expected_pushed_max{ 0 }; // are we in batched mode? + BATCH_SENTINEL.push(L); + int const is_batched{ lua501_equal(L, key_i, -1) }; + lua_pop(L, 1); + if (is_batched) + { + // no need to pass linda.batched in the keeper state + ++key_i; + // make sure the keys are of a valid type + check_key_types(L, key_i, key_i); + // receive multiple values from a single slot + keeper_receive = KEEPER_API(receive_batched); + // we expect a user-defined amount of return value + expected_pushed_min = (int) luaL_checkinteger(L, key_i + 1); + expected_pushed_max = (int) luaL_optinteger(L, key_i + 2, expected_pushed_min); + // don't forget to count the key in addition to the values + ++expected_pushed_min; + ++expected_pushed_max; + if (expected_pushed_min > expected_pushed_max) + { + return luaL_error(L, "batched min/max error"); + } + } + else + { + // make sure the keys are of a valid type + check_key_types(L, key_i, lua_gettop(L)); + // receive a single value, checking multiple slots + keeper_receive = KEEPER_API(receive); + // we expect a single (value, key) pair of returned values + expected_pushed_min = expected_pushed_max = 2; + } + + Lane* const lane{ get_lane_from_registry(L) }; + Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; + if (K == nullptr) + return 0; + CancelRequest cancel{ CancelRequest::None }; + int pushed{ 0 }; + for (bool try_again{ true };;) { - int is_batched; - lua_pushliteral( L, BATCH_SENTINEL); - is_batched = lua501_equal( L, key_i, -1); - lua_pop( L, 1); - if( is_batched) + if (lane != nullptr) { - // no need to pass linda.batched in the keeper state - ++ key_i; - // make sure the keys are of a valid type - check_key_types( L, key_i, key_i); - // receive multiple values from a single slot - keeper_receive = KEEPER_API( receive_batched); - // we expect a user-defined amount of return value - expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); - expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); - // don't forget to count the key in addition to the values - ++ expected_pushed_min; - ++ expected_pushed_max; - if( expected_pushed_min > expected_pushed_max) - { - return luaL_error( L, "batched min/max error"); - } + cancel = lane->cancel_request; } - else + cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; + // if user wants to cancel, or looped because of a timeout, the call returns without sending anything + if (!try_again || cancel != CancelRequest::None) { - // make sure the keys are of a valid type - check_key_types( L, key_i, lua_gettop( L)); - // receive a single value, checking multiple slots - keeper_receive = KEEPER_API( receive); - // we expect a single (value, key) pair of returned values - expected_pushed_min = expected_pushed_max = 2; + pushed = 0; + break; } - } - { - Lane* const s{ get_lane_from_registry(L) }; - Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; - if( K == nullptr) return 0; - for (bool try_again{ true };;) + // all arguments of receive() but the first are passed to the keeper's receive function + pushed = keeper_call(linda->U, K->L, keeper_receive, L, linda, key_i); + if (pushed < 0) { - if( s != nullptr) - { - cancel = s->cancel_request; - } - cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; - // if user wants to cancel, or looped because of a timeout, the call returns without sending anything - if (!try_again || cancel != CancelRequest::None) - { - pushed = 0; - break; - } + break; + } + if (pushed > 0) + { + ASSERT_L(pushed >= expected_pushed_min && pushed <= expected_pushed_max); + // replace sentinels with real nils + keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, eLM_FromKeeper); + // To be done from within the 'K' locking area + // + SIGNAL_ALL(&linda->read_happened); + break; + } - // all arguments of receive() but the first are passed to the keeper's receive function - pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); - if( pushed < 0) - { - break; - } - if( pushed > 0) - { - ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); - // replace sentinels with real nils - keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); - // To be done from within the 'K' locking area - // - SIGNAL_ALL( &linda->read_happened); - break; - } + if (timeout == 0.0) + { + break; /* instant timeout */ + } - if( timeout == 0.0) + // nothing received, wait until timeout or signalled that we should try again + { + enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings + if (lane != nullptr) { - break; /* instant timeout */ + // change status of lane to "waiting" + prev_status = lane->status; // RUNNING, most likely + ASSERT_L(prev_status == RUNNING); // but check, just in case + lane->status = WAITING; + ASSERT_L(lane->waiting_on == nullptr); + lane->waiting_on = &linda->write_happened; } - - // nothing received, wait until timeout or signalled that we should try again + // not enough data to read: wakeup when data was sent, or when timeout is reached + try_again = SIGNAL_WAIT(&linda->write_happened, &K->keeper_cs, timeout); + if (lane != nullptr) { - enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings - if( s != nullptr) - { - // change status of lane to "waiting" - prev_status = s->status; // RUNNING, most likely - ASSERT_L( prev_status == RUNNING); // but check, just in case - s->status = WAITING; - ASSERT_L( s->waiting_on == nullptr); - s->waiting_on = &linda->write_happened; - } - // not enough data to read: wakeup when data was sent, or when timeout is reached - try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); - if( s != nullptr) - { - s->waiting_on = nullptr; - s->status = prev_status; - } + lane->waiting_on = nullptr; + lane->status = prev_status; } } } - if( pushed < 0) + if (pushed < 0) { - return luaL_error( L, "tried to copy unsupported types"); + return luaL_error(L, "tried to copy unsupported types"); } - switch( cancel) + switch (cancel) { case CancelRequest::Soft: // if user wants to soft-cancel, the call returns CANCEL_ERROR @@ -520,53 +514,49 @@ LUAG_FUNC( linda_receive) * * Existing slot value is replaced, and possible queued entries removed. */ -LUAG_FUNC( linda_set) +LUAG_FUNC(linda_set) { Linda* const linda{ lua_toLinda(L, 1) }; - int pushed; bool const has_value{ lua_gettop(L) > 2 }; - // make sure the key is of a valid type (throws an error if not the case) - check_key_types( L, 2, 2); + check_key_types(L, 2, 2); + Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; + int pushed{ 0 }; + if (linda->simulate_cancel == CancelRequest::None) { - Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; - - if (linda->simulate_cancel == CancelRequest::None) + if (has_value) + { + // convert nils to some special non-nil sentinel in sent values + keeper_toggle_nil_sentinels(L, 3, eLM_ToKeeper); + } + pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L, linda, 2); + if (pushed >= 0) // no error? { - if( has_value) + ASSERT_L(pushed == 0 || pushed == 1); + + if (has_value) { - // convert nils to some special non-nil sentinel in sent values - keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); + // we put some data in the slot, tell readers that they should wake + SIGNAL_ALL(&linda->write_happened); // To be done from within the 'K' locking area } - pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); - if( pushed >= 0) // no error? + if (pushed == 1) { - ASSERT_L( pushed == 0 || pushed == 1); - - if( has_value) - { - // we put some data in the slot, tell readers that they should wake - SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area - } - if( pushed == 1) - { - // the key was full, but it is no longer the case, tell writers they should wake - ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); - SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area - } + // the key was full, but it is no longer the case, tell writers they should wake + ASSERT_L(lua_type(L, -1) == LUA_TBOOLEAN && lua_toboolean(L, -1) == 1); + SIGNAL_ALL(&linda->read_happened); // To be done from within the 'K' locking area } } - else // linda is cancelled - { - // do nothing and return lanes.cancel_error - CANCEL_ERROR.push(L); - pushed = 1; - } + } + else // linda is cancelled + { + // do nothing and return lanes.cancel_error + CANCEL_ERROR.push(L); + pushed = 1; } // must trigger any error after keeper state has been released - return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; + return (pushed < 0) ? luaL_error(L, "tried to copy unsupported types") : pushed; } // ################################################################################################# @@ -576,21 +566,17 @@ LUAG_FUNC( linda_set) * * Get a count of the pending elements in the specified keys */ -LUAG_FUNC( linda_count) +LUAG_FUNC(linda_count) { Linda* const linda{ lua_toLinda(L, 1) }; - int pushed; - // make sure the keys are of a valid type - check_key_types( L, 2, lua_gettop( L)); + check_key_types(L, 2, lua_gettop(L)); + Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; + int const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) }; + if (pushed < 0) { - Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; - pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); - if( pushed < 0) - { - return luaL_error( L, "tried to count an invalid key"); - } + return luaL_error(L, "tried to count an invalid key"); } return pushed; } @@ -602,39 +588,36 @@ LUAG_FUNC( linda_count) * * Get one or more values from Linda. */ -LUAG_FUNC( linda_get) +LUAG_FUNC(linda_get) { Linda* const linda{ lua_toLinda(L, 1) }; - int pushed; - lua_Integer count = luaL_optinteger( L, 3, 1); - luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); - luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); - + lua_Integer const count{ luaL_optinteger(L, 3, 1) }; + luaL_argcheck(L, count >= 1, 3, "count should be >= 1"); + luaL_argcheck(L, lua_gettop(L) <= 3, 4, "too many arguments"); // make sure the key is of a valid type (throws an error if not the case) - check_key_types( L, 2, 2); + check_key_types(L, 2, 2); + + int pushed{ 0 }; + if (linda->simulate_cancel == CancelRequest::None) { Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; - - if (linda->simulate_cancel == CancelRequest::None) + pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2); + if (pushed > 0) { - pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); - if( pushed > 0) - { - keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); - } - } - else // linda is cancelled - { - // do nothing and return lanes.cancel_error - CANCEL_ERROR.push(L); - pushed = 1; - } - // an error can be raised if we attempt to read an unregistered function - if( pushed < 0) - { - return luaL_error( L, "tried to copy unsupported types"); + keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, eLM_FromKeeper); } } + else // linda is cancelled + { + // do nothing and return lanes.cancel_error + CANCEL_ERROR.push(L); + pushed = 1; + } + // an error can be raised if we attempt to read an unregistered function + if (pushed < 0) + { + return luaL_error(L, "tried to copy unsupported types"); + } return pushed; } @@ -650,8 +633,6 @@ LUAG_FUNC( linda_get) LUAG_FUNC( linda_limit) { Linda* const linda{ lua_toLinda(L, 1) }; - int pushed; - // make sure we got 3 arguments: the linda, a key and a limit luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); // make sure we got a numeric limit @@ -659,26 +640,24 @@ LUAG_FUNC( linda_limit) // make sure the key is of a valid type check_key_types( L, 2, 2); + int pushed{ 0 }; + if (linda->simulate_cancel == CancelRequest::None) { Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; - - if (linda->simulate_cancel == CancelRequest::None) - { - pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); - ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads - if( pushed == 1) - { - ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); - SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area - } - } - else // linda is cancelled + pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2); + ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads + if( pushed == 1) { - // do nothing and return lanes.cancel_error - CANCEL_ERROR.push(L); - pushed = 1; + ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); + SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area } } + else // linda is cancelled + { + // do nothing and return lanes.cancel_error + CANCEL_ERROR.push(L); + pushed = 1; + } // propagate pushed boolean if any return pushed; } @@ -690,35 +669,34 @@ LUAG_FUNC( linda_limit) * * Signal linda so that waiting threads wake up as if their own lane was cancelled */ -LUAG_FUNC( linda_cancel) +LUAG_FUNC(linda_cancel) { Linda* const linda{ lua_toLinda(L, 1) }; - char const* who = luaL_optstring( L, 2, "both"); - + char const* who = luaL_optstring(L, 2, "both"); // make sure we got 3 arguments: the linda, a key and a limit - luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); + luaL_argcheck(L, lua_gettop(L) <= 2, 2, "wrong number of arguments"); linda->simulate_cancel = CancelRequest::Soft; - if( strcmp( who, "both") == 0) // tell everyone writers to wake up + if (strcmp(who, "both") == 0) // tell everyone writers to wake up { - SIGNAL_ALL( &linda->write_happened); - SIGNAL_ALL( &linda->read_happened); + SIGNAL_ALL(&linda->write_happened); + SIGNAL_ALL(&linda->read_happened); } - else if( strcmp( who, "none") == 0) // reset flag + else if (strcmp(who, "none") == 0) // reset flag { linda->simulate_cancel = CancelRequest::None; } - else if( strcmp( who, "read") == 0) // tell blocked readers to wake up + else if (strcmp(who, "read") == 0) // tell blocked readers to wake up { - SIGNAL_ALL( &linda->write_happened); + SIGNAL_ALL(&linda->write_happened); } - else if( strcmp( who, "write") == 0) // tell blocked writers to wake up + else if (strcmp(who, "write") == 0) // tell blocked writers to wake up { - SIGNAL_ALL( &linda->read_happened); + SIGNAL_ALL(&linda->read_happened); } else { - return luaL_error( L, "unknown wake hint '%s'", who); + return luaL_error(L, "unknown wake hint '%s'", who); } return 0; } @@ -735,10 +713,10 @@ LUAG_FUNC( linda_cancel) * different userdata and won't be known to be essentially the same deep one * without this. */ -LUAG_FUNC( linda_deep) +LUAG_FUNC(linda_deep) { Linda* const linda{ lua_toLinda(L, 1) }; - lua_pushlightuserdata( L, linda); // just the address + lua_pushlightuserdata(L, linda); // just the address return 1; } @@ -756,21 +734,21 @@ template static int linda_tostring(lua_State* L, int idx_) { Linda* const linda{ lua_toLinda(L, idx_) }; - if( linda != nullptr) + if (linda != nullptr) { char text[128]; int len; - if( linda->getName()) - len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->getName()); + if (linda->getName()) + len = sprintf(text, "Linda: %.*s", (int) sizeof(text) - 8, linda->getName()); else - len = sprintf( text, "Linda: %p", linda); - lua_pushlstring( L, text, len); + len = sprintf(text, "Linda: %p", linda); + lua_pushlstring(L, text, len); return 1; } return 0; } -LUAG_FUNC( linda_tostring) +LUAG_FUNC(linda_tostring) { return linda_tostring(L, 1); } @@ -784,25 +762,25 @@ LUAG_FUNC( linda_tostring) * * Useful for concatenation or debugging purposes */ -LUAG_FUNC( linda_concat) -{ // linda1? linda2? +LUAG_FUNC(linda_concat) +{ // linda1? linda2? bool atLeastOneLinda{ false }; // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. - if( linda_tostring( L, 1)) + if (linda_tostring(L, 1)) { atLeastOneLinda = true; - lua_replace( L, 1); + lua_replace(L, 1); } - if( linda_tostring( L, 2)) + if (linda_tostring(L, 2)) { atLeastOneLinda = true; - lua_replace( L, 2); + lua_replace(L, 2); } - if( !atLeastOneLinda) // should not be possible + if (!atLeastOneLinda) // should not be possible { - return luaL_error( L, "internal error: linda_concat called on non-Linda"); + return luaL_error(L, "internal error: linda_concat called on non-Linda"); } - lua_concat( L, 2); + lua_concat(L, 2); return 1; } @@ -812,17 +790,19 @@ LUAG_FUNC( linda_concat) * table = linda:dump() * return a table listing all pending data inside the linda */ -LUAG_FUNC( linda_dump) +LUAG_FUNC(linda_dump) { Linda* const linda{ lua_toLinda(L, 1) }; return keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()); } +// ################################################################################################# + /* * table = linda:dump() * return a table listing all pending data inside the linda */ -LUAG_FUNC( linda_towatch) +LUAG_FUNC(linda_towatch) { Linda* const linda{ lua_toLinda(L, 1) }; int pushed{ keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()) }; @@ -834,6 +814,8 @@ LUAG_FUNC( linda_towatch) return pushed; } +// ################################################################################################# + /* * Identity function of a shared userdata object. * @@ -868,51 +850,51 @@ static void* linda_id( lua_State* L, DeepOp op_) char const* linda_name = nullptr; unsigned long linda_group = 0; // should have a string and/or a number of the stack as parameters (name and group) - switch( lua_gettop( L)) + switch (lua_gettop(L)) { default: // 0 break; case 1: // 1 parameter, either a name or a group - if( lua_type( L, -1) == LUA_TSTRING) + if (lua_type(L, -1) == LUA_TSTRING) { - linda_name = lua_tolstring( L, -1, &name_len); + linda_name = lua_tolstring(L, -1, &name_len); } else { - linda_group = (unsigned long) lua_tointeger( L, -1); + linda_group = (unsigned long) lua_tointeger(L, -1); } break; case 2: // 2 parameters, a name and group, in that order - linda_name = lua_tolstring( L, -2, &name_len); - linda_group = (unsigned long) lua_tointeger( L, -1); + linda_name = lua_tolstring(L, -2, &name_len); + linda_group = (unsigned long) lua_tointeger(L, -1); break; } /* The deep data is allocated separately of Lua stack; we might no - * longer be around when last reference to it is being released. - * One can use any memory allocation scheme. - * just don't use L's allocF because we don't know which state will get the honor of GCing the linda - */ + * longer be around when last reference to it is being released. + * One can use any memory allocation scheme. + * just don't use L's allocF because we don't know which state will get the honor of GCing the linda + */ Universe* const U{ universe_get(L) }; - Linda* s{ new (U) Linda{ U, linda_group, linda_name, name_len } }; - return s; + Linda* linda{ new (U) Linda{ U, linda_group, linda_name, name_len } }; + return linda; } case eDO_delete: { Linda* const linda{ lua_tolightuserdata(L, 1) }; - ASSERT_L( linda); + ASSERT_L(linda); // Clean associated structures in the keeper state. Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; - if( K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) + if (K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) { // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... - keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); + keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0); } - keeper_release( K); + keeper_release(K); delete linda; // operator delete overload ensures things go as expected return nullptr; @@ -920,73 +902,72 @@ static void* linda_id( lua_State* L, DeepOp op_) case eDO_metatable: { - STACK_CHECK_START_REL(L, 0); - lua_newtable( L); + lua_newtable(L); // metatable is its own index - lua_pushvalue( L, -1); - lua_setfield( L, -2, "__index"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); // protect metatable from external access - lua_pushliteral( L, "Linda"); - lua_setfield( L, -2, "__metatable"); + lua_pushliteral(L, "Linda"); + lua_setfield(L, -2, "__metatable"); - lua_pushcfunction( L, LG_linda_tostring); - lua_setfield( L, -2, "__tostring"); + lua_pushcfunction(L, LG_linda_tostring); + lua_setfield(L, -2, "__tostring"); // Decoda __towatch support - lua_pushcfunction( L, LG_linda_towatch); - lua_setfield( L, -2, "__towatch"); + lua_pushcfunction(L, LG_linda_towatch); + lua_setfield(L, -2, "__towatch"); - lua_pushcfunction( L, LG_linda_concat); - lua_setfield( L, -2, "__concat"); + lua_pushcfunction(L, LG_linda_concat); + lua_setfield(L, -2, "__concat"); // protected calls, to ensure associated keeper is always released even in case of error // all function are the protected call wrapper, where the actual operation is provided as upvalue // note that this kind of thing can break function lookup as we use the function pointer here and there - lua_pushcfunction( L, LG_linda_send); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "send"); + lua_pushcfunction(L, LG_linda_send); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "send"); - lua_pushcfunction( L, LG_linda_receive); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "receive"); + lua_pushcfunction(L, LG_linda_receive); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "receive"); - lua_pushcfunction( L, LG_linda_limit); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "limit"); + lua_pushcfunction(L, LG_linda_limit); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "limit"); - lua_pushcfunction( L, LG_linda_set); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "set"); + lua_pushcfunction(L, LG_linda_set); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "set"); - lua_pushcfunction( L, LG_linda_count); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "count"); + lua_pushcfunction(L, LG_linda_count); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "count"); - lua_pushcfunction( L, LG_linda_get); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "get"); + lua_pushcfunction(L, LG_linda_get); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "get"); - lua_pushcfunction( L, LG_linda_cancel); - lua_setfield( L, -2, "cancel"); + lua_pushcfunction(L, LG_linda_cancel); + lua_setfield(L, -2, "cancel"); - lua_pushcfunction( L, LG_linda_deep); - lua_setfield( L, -2, "deep"); + lua_pushcfunction(L, LG_linda_deep); + lua_setfield(L, -2, "deep"); - lua_pushcfunction( L, LG_linda_dump); - lua_pushcclosure( L, LG_linda_protected_call, 1); - lua_setfield( L, -2, "dump"); + lua_pushcfunction(L, LG_linda_dump); + lua_pushcclosure(L, LG_linda_protected_call, 1); + lua_setfield(L, -2, "dump"); // some constants - lua_pushliteral( L, BATCH_SENTINEL); - lua_setfield( L, -2, "batched"); + BATCH_SENTINEL.push(L); + lua_setfield(L, -2, "batched"); NIL_SENTINEL.push(L); - lua_setfield( L, -2, "null"); + lua_setfield(L, -2, "null"); - STACK_CHECK( L, 1); + STACK_CHECK(L, 1); return nullptr; } @@ -1001,24 +982,26 @@ static void* linda_id( lua_State* L, DeepOp op_) } } +// ################################################################################################# + /* * ud = lanes.linda( [name[,group]]) * * returns a linda object, or raises an error if creation failed */ -LUAG_FUNC( linda) +LUAG_FUNC(linda) { - int const top = lua_gettop( L); - luaL_argcheck( L, top <= 2, top, "too many arguments"); - if( top == 1) + int const top = lua_gettop(L); + luaL_argcheck(L, top <= 2, top, "too many arguments"); + if (top == 1) { - int const t = lua_type( L, 1); - luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); + int const t = lua_type(L, 1); + luaL_argcheck(L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); } - else if( top == 2) + else if (top == 2) { - luaL_checktype( L, 1, LUA_TSTRING); - luaL_checktype( L, 2, LUA_TNUMBER); + luaL_checktype(L, 1, LUA_TSTRING); + luaL_checktype(L, 2, LUA_TNUMBER); } - return luaG_newdeepuserdata( L, linda_id, 0); + return luaG_newdeepuserdata(L, linda_id, 0); } diff --git a/src/uniquekey.h b/src/uniquekey.h index 777d640..93aaf37 100644 --- a/src/uniquekey.h +++ b/src/uniquekey.h @@ -3,6 +3,8 @@ #include "compat.h" #include "macros_and_utils.h" +#include + class UniqueKey { private: @@ -31,13 +33,11 @@ class UniqueKey void push(lua_State* const L) const { - // unfortunately, converting a scalar to a pointer must go through a C cast - lua_pushlightuserdata(L, (void*) m_storage); + lua_pushlightuserdata(L, std::bit_cast(m_storage)); } bool equals(lua_State* const L, int i) const { - // unfortunately, converting a scalar to a pointer must go through a C cast - return lua_touserdata(L, i) == (void*) m_storage; + return lua_touserdata(L, i) == std::bit_cast(m_storage); } void query_registry(lua_State* const L) const { -- cgit v1.2.3-55-g6feb