diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-03-28 15:29:59 +0100 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-03-28 15:29:59 +0100 |
| commit | 089f68cf6d18799028eb12cc16bbe2f8cf5e57c3 (patch) | |
| tree | 196e5765d35b42376b65da2d01d4f40bc6c710ee /src | |
| parent | 23a8f1ed5b24c05ac203fc7f30a03f7fc52c0cbd (diff) | |
| download | lanes-089f68cf6d18799028eb12cc16bbe2f8cf5e57c3.tar.gz lanes-089f68cf6d18799028eb12cc16bbe2f8cf5e57c3.tar.bz2 lanes-089f68cf6d18799028eb12cc16bbe2f8cf5e57c3.zip | |
linda.batched is now a lightuserdata instead of a string. plus some reformatting.
Diffstat (limited to 'src')
| -rw-r--r-- | src/linda.cpp | 679 | ||||
| -rw-r--r-- | 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 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * LINDA.C Copyright (c) 2018, Benoit Germain | 2 | * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain |
| 3 | * | 3 | * |
| 4 | * Linda deep userdata. | 4 | * Linda deep userdata. |
| 5 | */ | 5 | */ |
| @@ -30,20 +30,15 @@ THE SOFTWARE. | |||
| 30 | =============================================================================== | 30 | =============================================================================== |
| 31 | */ | 31 | */ |
| 32 | 32 | ||
| 33 | #include <stdlib.h> | ||
| 34 | #include <string.h> | ||
| 35 | #include <assert.h> | ||
| 36 | |||
| 37 | #include "threading.h" | ||
| 38 | #include "compat.h" | 33 | #include "compat.h" |
| 39 | #include "tools.h" | ||
| 40 | #include "universe.h" | ||
| 41 | #include "keeper.h" | ||
| 42 | #include "deep.h" | 34 | #include "deep.h" |
| 35 | #include "keeper.h" | ||
| 43 | #include "lanes_private.h" | 36 | #include "lanes_private.h" |
| 37 | #include "threading.h" | ||
| 38 | #include "tools.h" | ||
| 39 | #include "universe.h" | ||
| 44 | 40 | ||
| 45 | #include <array> | 41 | #include <array> |
| 46 | #include <bit> | ||
| 47 | #include <variant> | 42 | #include <variant> |
| 48 | 43 | ||
| 49 | /* | 44 | /* |
| @@ -166,11 +161,10 @@ static inline Linda* lua_toLinda(lua_State* L, int idx_) | |||
| 166 | 161 | ||
| 167 | static void check_key_types(lua_State* L, int start_, int end_) | 162 | static void check_key_types(lua_State* L, int start_, int end_) |
| 168 | { | 163 | { |
| 169 | int i; | 164 | for (int i{ start_ }; i <= end_; ++i) |
| 170 | for( i = start_; i <= end_; ++ i) | ||
| 171 | { | 165 | { |
| 172 | int t = lua_type( L, i); | 166 | int const t{ lua_type(L, i) }; |
| 173 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | 167 | if (t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) |
| 174 | { | 168 | { |
| 175 | continue; | 169 | continue; |
| 176 | } | 170 | } |
| @@ -185,26 +179,27 @@ LUAG_FUNC(linda_protected_call) | |||
| 185 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 179 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 186 | 180 | ||
| 187 | // acquire the keeper | 181 | // acquire the keeper |
| 188 | Keeper* K = keeper_acquire( linda->U->keepers, linda->hashSeed()); | 182 | Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; |
| 189 | lua_State* KL = K ? K->L : nullptr; | 183 | lua_State* const KL{ K ? K->L : nullptr }; |
| 190 | if( KL == nullptr) return 0; | 184 | if (KL == nullptr) |
| 185 | return 0; | ||
| 191 | 186 | ||
| 192 | // retrieve the actual function to be called and move it before the arguments | 187 | // retrieve the actual function to be called and move it before the arguments |
| 193 | lua_pushvalue( L, lua_upvalueindex( 1)); | 188 | lua_pushvalue(L, lua_upvalueindex(1)); |
| 194 | lua_insert( L, 1); | 189 | lua_insert(L, 1); |
| 195 | // do a protected call | 190 | // do a protected call |
| 196 | int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) }; | 191 | int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) }; |
| 197 | 192 | ||
| 198 | // release the keeper | 193 | // release the keeper |
| 199 | keeper_release( K); | 194 | keeper_release(K); |
| 200 | 195 | ||
| 201 | // if there was an error, forward it | 196 | // if there was an error, forward it |
| 202 | if( rc != LUA_OK) | 197 | if (rc != LUA_OK) |
| 203 | { | 198 | { |
| 204 | raise_lua_error(L); | 199 | raise_lua_error(L); |
| 205 | } | 200 | } |
| 206 | // return whatever the actual operation provided | 201 | // return whatever the actual operation provided |
| 207 | return lua_gettop( L); | 202 | return lua_gettop(L); |
| 208 | } | 203 | } |
| 209 | 204 | ||
| 210 | // ################################################################################################# | 205 | // ################################################################################################# |
| @@ -218,7 +213,7 @@ LUAG_FUNC(linda_protected_call) | |||
| 218 | * 'false' for timeout (only happens when the queue size is limited) | 213 | * 'false' for timeout (only happens when the queue size is limited) |
| 219 | * nil, CANCEL_ERROR if cancelled | 214 | * nil, CANCEL_ERROR if cancelled |
| 220 | */ | 215 | */ |
| 221 | LUAG_FUNC( linda_send) | 216 | LUAG_FUNC(linda_send) |
| 222 | { | 217 | { |
| 223 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 218 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 224 | bool ret{ false }; | 219 | bool ret{ false }; |
| @@ -227,56 +222,57 @@ LUAG_FUNC( linda_send) | |||
| 227 | time_d timeout = -1.0; | 222 | time_d timeout = -1.0; |
| 228 | int key_i = 2; // index of first key, if timeout not there | 223 | int key_i = 2; // index of first key, if timeout not there |
| 229 | 224 | ||
| 230 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 225 | if (lua_type(L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion |
| 231 | { | 226 | { |
| 232 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | 227 | timeout = SIGNAL_TIMEOUT_PREPARE(lua_tonumber(L, 2)); |
| 233 | ++ key_i; | 228 | ++key_i; |
| 234 | } | 229 | } |
| 235 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | 230 | else if (lua_isnil(L, 2)) // alternate explicit "no timeout" by passing nil before the key |
| 236 | { | 231 | { |
| 237 | ++ key_i; | 232 | ++key_i; |
| 238 | } | 233 | } |
| 239 | 234 | ||
| 240 | 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 | 235 | 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 |
| 241 | if( as_nil_sentinel) | 236 | if (as_nil_sentinel) |
| 242 | { | 237 | { |
| 243 | // the real key to send data to is after the NIL_SENTINEL marker | 238 | // the real key to send data to is after the NIL_SENTINEL marker |
| 244 | ++ key_i; | 239 | ++key_i; |
| 245 | } | 240 | } |
| 246 | 241 | ||
| 247 | // make sure the key is of a valid type | 242 | // make sure the key is of a valid type |
| 248 | check_key_types( L, key_i, key_i); | 243 | check_key_types(L, key_i, key_i); |
| 249 | 244 | ||
| 250 | STACK_GROW( L, 1); | 245 | STACK_GROW(L, 1); |
| 251 | 246 | ||
| 252 | // make sure there is something to send | 247 | // make sure there is something to send |
| 253 | if( lua_gettop( L) == key_i) | 248 | if (lua_gettop(L) == key_i) |
| 254 | { | 249 | { |
| 255 | if( as_nil_sentinel) | 250 | if (as_nil_sentinel) |
| 256 | { | 251 | { |
| 257 | // send a single nil if nothing is provided | 252 | // send a single nil if nothing is provided |
| 258 | NIL_SENTINEL.push(L); | 253 | NIL_SENTINEL.push(L); |
| 259 | } | 254 | } |
| 260 | else | 255 | else |
| 261 | { | 256 | { |
| 262 | return luaL_error( L, "no data to send"); | 257 | return luaL_error(L, "no data to send"); |
| 263 | } | 258 | } |
| 264 | } | 259 | } |
| 265 | 260 | ||
| 266 | // convert nils to some special non-nil sentinel in sent values | 261 | // convert nils to some special non-nil sentinel in sent values |
| 267 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); | 262 | keeper_toggle_nil_sentinels(L, key_i + 1, eLM_ToKeeper); |
| 268 | 263 | ||
| 269 | { | 264 | { |
| 270 | Lane* const s{ get_lane_from_registry(L) }; | 265 | Lane* const lane{ get_lane_from_registry(L) }; |
| 271 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 266 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
| 272 | lua_State* KL = K ? K->L : nullptr; | 267 | lua_State* const KL{ K ? K->L : nullptr }; |
| 273 | if( KL == nullptr) return 0; | 268 | if (KL == nullptr) |
| 269 | return 0; | ||
| 274 | STACK_CHECK_START_REL(KL, 0); | 270 | STACK_CHECK_START_REL(KL, 0); |
| 275 | for(bool try_again{ true };;) | 271 | for (bool try_again{ true };;) |
| 276 | { | 272 | { |
| 277 | if( s != nullptr) | 273 | if (lane != nullptr) |
| 278 | { | 274 | { |
| 279 | cancel = s->cancel_request; | 275 | cancel = lane->cancel_request; |
| 280 | } | 276 | } |
| 281 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; | 277 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; |
| 282 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 278 | // 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) | |||
| 286 | break; | 282 | break; |
| 287 | } | 283 | } |
| 288 | 284 | ||
| 289 | STACK_CHECK( KL, 0); | 285 | STACK_CHECK(KL, 0); |
| 290 | pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); | 286 | pushed = keeper_call(linda->U, KL, KEEPER_API(send), L, linda, key_i); |
| 291 | if( pushed < 0) | 287 | if (pushed < 0) |
| 292 | { | 288 | { |
| 293 | break; | 289 | break; |
| 294 | } | 290 | } |
| 295 | ASSERT_L( pushed == 1); | 291 | ASSERT_L(pushed == 1); |
| 296 | 292 | ||
| 297 | ret = lua_toboolean( L, -1) ? true : false; | 293 | ret = lua_toboolean(L, -1) ? true : false; |
| 298 | lua_pop( L, 1); | 294 | lua_pop(L, 1); |
| 299 | 295 | ||
| 300 | if( ret) | 296 | if (ret) |
| 301 | { | 297 | { |
| 302 | // Wake up ALL waiting threads | 298 | // Wake up ALL waiting threads |
| 303 | SIGNAL_ALL( &linda->write_happened); | 299 | SIGNAL_ALL(&linda->write_happened); |
| 304 | break; | 300 | break; |
| 305 | } | 301 | } |
| 306 | 302 | ||
| 307 | // instant timout to bypass the wait syscall | 303 | // instant timout to bypass the wait syscall |
| 308 | if( timeout == 0.0) | 304 | if (timeout == 0.0) |
| 309 | { | 305 | { |
| 310 | break; /* no wait; instant timeout */ | 306 | break; /* no wait; instant timeout */ |
| 311 | } | 307 | } |
| 312 | 308 | ||
| 313 | // storage limit hit, wait until timeout or signalled that we should try again | 309 | // storage limit hit, wait until timeout or signalled that we should try again |
| 314 | { | 310 | { |
| 315 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 311 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
| 316 | if( s != nullptr) | 312 | if (lane != nullptr) |
| 317 | { | 313 | { |
| 318 | // change status of lane to "waiting" | 314 | // change status of lane to "waiting" |
| 319 | prev_status = s->status; // RUNNING, most likely | 315 | prev_status = lane->status; // RUNNING, most likely |
| 320 | ASSERT_L( prev_status == RUNNING); // but check, just in case | 316 | ASSERT_L(prev_status == RUNNING); // but check, just in case |
| 321 | s->status = WAITING; | 317 | lane->status = WAITING; |
| 322 | ASSERT_L( s->waiting_on == nullptr); | 318 | ASSERT_L(lane->waiting_on == nullptr); |
| 323 | s->waiting_on = &linda->read_happened; | 319 | lane->waiting_on = &linda->read_happened; |
| 324 | } | 320 | } |
| 325 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | 321 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached |
| 326 | try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); | 322 | try_again = SIGNAL_WAIT(&linda->read_happened, &K->keeper_cs, timeout); |
| 327 | if( s != nullptr) | 323 | if (lane != nullptr) |
| 328 | { | 324 | { |
| 329 | s->waiting_on = nullptr; | 325 | lane->waiting_on = nullptr; |
| 330 | s->status = prev_status; | 326 | lane->status = prev_status; |
| 331 | } | 327 | } |
| 332 | } | 328 | } |
| 333 | } | 329 | } |
| 334 | STACK_CHECK( KL, 0); | 330 | STACK_CHECK(KL, 0); |
| 335 | } | 331 | } |
| 336 | 332 | ||
| 337 | if( pushed < 0) | 333 | if (pushed < 0) |
| 338 | { | 334 | { |
| 339 | return luaL_error( L, "tried to copy unsupported types"); | 335 | return luaL_error(L, "tried to copy unsupported types"); |
| 340 | } | 336 | } |
| 341 | 337 | ||
| 342 | switch( cancel) | 338 | switch (cancel) |
| 343 | { | 339 | { |
| 344 | case CancelRequest::Soft: | 340 | case CancelRequest::Soft: |
| 345 | // if user wants to soft-cancel, the call returns lanes.cancel_error | 341 | // if user wants to soft-cancel, the call returns lanes.cancel_error |
| @@ -351,7 +347,7 @@ LUAG_FUNC( linda_send) | |||
| 351 | raise_cancel_error(L); // raises an error and doesn't return | 347 | raise_cancel_error(L); // raises an error and doesn't return |
| 352 | 348 | ||
| 353 | default: | 349 | default: |
| 354 | lua_pushboolean( L, ret); // true (success) or false (timeout) | 350 | lua_pushboolean(L, ret); // true (success) or false (timeout) |
| 355 | return 1; | 351 | return 1; |
| 356 | } | 352 | } |
| 357 | } | 353 | } |
| @@ -369,132 +365,130 @@ LUAG_FUNC( linda_send) | |||
| 369 | * returns the actual consumed values, or nil if there weren't enough values to consume | 365 | * returns the actual consumed values, or nil if there weren't enough values to consume |
| 370 | * | 366 | * |
| 371 | */ | 367 | */ |
| 372 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | 368 | // xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator |
| 373 | LUAG_FUNC( linda_receive) | 369 | static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull }; |
| 370 | LUAG_FUNC(linda_receive) | ||
| 374 | { | 371 | { |
| 375 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 372 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 376 | int pushed, expected_pushed_min, expected_pushed_max; | ||
| 377 | CancelRequest cancel{ CancelRequest::None }; | ||
| 378 | keeper_api_t keeper_receive; | ||
| 379 | |||
| 380 | time_d timeout = -1.0; | ||
| 381 | int key_i = 2; | ||
| 382 | 373 | ||
| 383 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 374 | time_d timeout{ -1.0 }; |
| 375 | int key_i{ 2 }; | ||
| 376 | |||
| 377 | if (lua_type(L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
| 384 | { | 378 | { |
| 385 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | 379 | timeout = SIGNAL_TIMEOUT_PREPARE(lua_tonumber(L, 2)); |
| 386 | ++ key_i; | 380 | ++key_i; |
| 387 | } | 381 | } |
| 388 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | 382 | else if (lua_isnil(L, 2)) // alternate explicit "no timeout" by passing nil before the key |
| 389 | { | 383 | { |
| 390 | ++ key_i; | 384 | ++key_i; |
| 391 | } | 385 | } |
| 392 | 386 | ||
| 387 | keeper_api_t keeper_receive; | ||
| 388 | int expected_pushed_min{ 0 }, expected_pushed_max{ 0 }; | ||
| 393 | // are we in batched mode? | 389 | // are we in batched mode? |
| 390 | BATCH_SENTINEL.push(L); | ||
| 391 | int const is_batched{ lua501_equal(L, key_i, -1) }; | ||
| 392 | lua_pop(L, 1); | ||
| 393 | if (is_batched) | ||
| 394 | { | ||
| 395 | // no need to pass linda.batched in the keeper state | ||
| 396 | ++key_i; | ||
| 397 | // make sure the keys are of a valid type | ||
| 398 | check_key_types(L, key_i, key_i); | ||
| 399 | // receive multiple values from a single slot | ||
| 400 | keeper_receive = KEEPER_API(receive_batched); | ||
| 401 | // we expect a user-defined amount of return value | ||
| 402 | expected_pushed_min = (int) luaL_checkinteger(L, key_i + 1); | ||
| 403 | expected_pushed_max = (int) luaL_optinteger(L, key_i + 2, expected_pushed_min); | ||
| 404 | // don't forget to count the key in addition to the values | ||
| 405 | ++expected_pushed_min; | ||
| 406 | ++expected_pushed_max; | ||
| 407 | if (expected_pushed_min > expected_pushed_max) | ||
| 408 | { | ||
| 409 | return luaL_error(L, "batched min/max error"); | ||
| 410 | } | ||
| 411 | } | ||
| 412 | else | ||
| 413 | { | ||
| 414 | // make sure the keys are of a valid type | ||
| 415 | check_key_types(L, key_i, lua_gettop(L)); | ||
| 416 | // receive a single value, checking multiple slots | ||
| 417 | keeper_receive = KEEPER_API(receive); | ||
| 418 | // we expect a single (value, key) pair of returned values | ||
| 419 | expected_pushed_min = expected_pushed_max = 2; | ||
| 420 | } | ||
| 421 | |||
| 422 | Lane* const lane{ get_lane_from_registry(L) }; | ||
| 423 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | ||
| 424 | if (K == nullptr) | ||
| 425 | return 0; | ||
| 426 | CancelRequest cancel{ CancelRequest::None }; | ||
| 427 | int pushed{ 0 }; | ||
| 428 | for (bool try_again{ true };;) | ||
| 394 | { | 429 | { |
| 395 | int is_batched; | 430 | if (lane != nullptr) |
| 396 | lua_pushliteral( L, BATCH_SENTINEL); | ||
| 397 | is_batched = lua501_equal( L, key_i, -1); | ||
| 398 | lua_pop( L, 1); | ||
| 399 | if( is_batched) | ||
| 400 | { | 431 | { |
| 401 | // no need to pass linda.batched in the keeper state | 432 | cancel = lane->cancel_request; |
| 402 | ++ key_i; | ||
| 403 | // make sure the keys are of a valid type | ||
| 404 | check_key_types( L, key_i, key_i); | ||
| 405 | // receive multiple values from a single slot | ||
| 406 | keeper_receive = KEEPER_API( receive_batched); | ||
| 407 | // we expect a user-defined amount of return value | ||
| 408 | expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); | ||
| 409 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); | ||
| 410 | // don't forget to count the key in addition to the values | ||
| 411 | ++ expected_pushed_min; | ||
| 412 | ++ expected_pushed_max; | ||
| 413 | if( expected_pushed_min > expected_pushed_max) | ||
| 414 | { | ||
| 415 | return luaL_error( L, "batched min/max error"); | ||
| 416 | } | ||
| 417 | } | 433 | } |
| 418 | else | 434 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; |
| 435 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
| 436 | if (!try_again || cancel != CancelRequest::None) | ||
| 419 | { | 437 | { |
| 420 | // make sure the keys are of a valid type | 438 | pushed = 0; |
| 421 | check_key_types( L, key_i, lua_gettop( L)); | 439 | break; |
| 422 | // receive a single value, checking multiple slots | ||
| 423 | keeper_receive = KEEPER_API( receive); | ||
| 424 | // we expect a single (value, key) pair of returned values | ||
| 425 | expected_pushed_min = expected_pushed_max = 2; | ||
| 426 | } | 440 | } |
| 427 | } | ||
| 428 | 441 | ||
| 429 | { | 442 | // all arguments of receive() but the first are passed to the keeper's receive function |
| 430 | Lane* const s{ get_lane_from_registry(L) }; | 443 | pushed = keeper_call(linda->U, K->L, keeper_receive, L, linda, key_i); |
| 431 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 444 | if (pushed < 0) |
| 432 | if( K == nullptr) return 0; | ||
| 433 | for (bool try_again{ true };;) | ||
| 434 | { | 445 | { |
| 435 | if( s != nullptr) | 446 | break; |
| 436 | { | 447 | } |
| 437 | cancel = s->cancel_request; | 448 | if (pushed > 0) |
| 438 | } | 449 | { |
| 439 | cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; | 450 | ASSERT_L(pushed >= expected_pushed_min && pushed <= expected_pushed_max); |
| 440 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | 451 | // replace sentinels with real nils |
| 441 | if (!try_again || cancel != CancelRequest::None) | 452 | keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, eLM_FromKeeper); |
| 442 | { | 453 | // To be done from within the 'K' locking area |
| 443 | pushed = 0; | 454 | // |
| 444 | break; | 455 | SIGNAL_ALL(&linda->read_happened); |
| 445 | } | 456 | break; |
| 457 | } | ||
| 446 | 458 | ||
| 447 | // all arguments of receive() but the first are passed to the keeper's receive function | 459 | if (timeout == 0.0) |
| 448 | pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); | 460 | { |
| 449 | if( pushed < 0) | 461 | break; /* instant timeout */ |
| 450 | { | 462 | } |
| 451 | break; | ||
| 452 | } | ||
| 453 | if( pushed > 0) | ||
| 454 | { | ||
| 455 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); | ||
| 456 | // replace sentinels with real nils | ||
| 457 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
| 458 | // To be done from within the 'K' locking area | ||
| 459 | // | ||
| 460 | SIGNAL_ALL( &linda->read_happened); | ||
| 461 | break; | ||
| 462 | } | ||
| 463 | 463 | ||
| 464 | if( timeout == 0.0) | 464 | // nothing received, wait until timeout or signalled that we should try again |
| 465 | { | ||
| 466 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
| 467 | if (lane != nullptr) | ||
| 465 | { | 468 | { |
| 466 | break; /* instant timeout */ | 469 | // change status of lane to "waiting" |
| 470 | prev_status = lane->status; // RUNNING, most likely | ||
| 471 | ASSERT_L(prev_status == RUNNING); // but check, just in case | ||
| 472 | lane->status = WAITING; | ||
| 473 | ASSERT_L(lane->waiting_on == nullptr); | ||
| 474 | lane->waiting_on = &linda->write_happened; | ||
| 467 | } | 475 | } |
| 468 | 476 | // not enough data to read: wakeup when data was sent, or when timeout is reached | |
| 469 | // nothing received, wait until timeout or signalled that we should try again | 477 | try_again = SIGNAL_WAIT(&linda->write_happened, &K->keeper_cs, timeout); |
| 478 | if (lane != nullptr) | ||
| 470 | { | 479 | { |
| 471 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 480 | lane->waiting_on = nullptr; |
| 472 | if( s != nullptr) | 481 | lane->status = prev_status; |
| 473 | { | ||
| 474 | // change status of lane to "waiting" | ||
| 475 | prev_status = s->status; // RUNNING, most likely | ||
| 476 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
| 477 | s->status = WAITING; | ||
| 478 | ASSERT_L( s->waiting_on == nullptr); | ||
| 479 | s->waiting_on = &linda->write_happened; | ||
| 480 | } | ||
| 481 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
| 482 | try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); | ||
| 483 | if( s != nullptr) | ||
| 484 | { | ||
| 485 | s->waiting_on = nullptr; | ||
| 486 | s->status = prev_status; | ||
| 487 | } | ||
| 488 | } | 482 | } |
| 489 | } | 483 | } |
| 490 | } | 484 | } |
| 491 | 485 | ||
| 492 | if( pushed < 0) | 486 | if (pushed < 0) |
| 493 | { | 487 | { |
| 494 | return luaL_error( L, "tried to copy unsupported types"); | 488 | return luaL_error(L, "tried to copy unsupported types"); |
| 495 | } | 489 | } |
| 496 | 490 | ||
| 497 | switch( cancel) | 491 | switch (cancel) |
| 498 | { | 492 | { |
| 499 | case CancelRequest::Soft: | 493 | case CancelRequest::Soft: |
| 500 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | 494 | // if user wants to soft-cancel, the call returns CANCEL_ERROR |
| @@ -520,53 +514,49 @@ LUAG_FUNC( linda_receive) | |||
| 520 | * | 514 | * |
| 521 | * Existing slot value is replaced, and possible queued entries removed. | 515 | * Existing slot value is replaced, and possible queued entries removed. |
| 522 | */ | 516 | */ |
| 523 | LUAG_FUNC( linda_set) | 517 | LUAG_FUNC(linda_set) |
| 524 | { | 518 | { |
| 525 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 519 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 526 | int pushed; | ||
| 527 | bool const has_value{ lua_gettop(L) > 2 }; | 520 | bool const has_value{ lua_gettop(L) > 2 }; |
| 528 | |||
| 529 | // make sure the key is of a valid type (throws an error if not the case) | 521 | // make sure the key is of a valid type (throws an error if not the case) |
| 530 | check_key_types( L, 2, 2); | 522 | check_key_types(L, 2, 2); |
| 531 | 523 | ||
| 524 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | ||
| 525 | int pushed{ 0 }; | ||
| 526 | if (linda->simulate_cancel == CancelRequest::None) | ||
| 532 | { | 527 | { |
| 533 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 528 | if (has_value) |
| 534 | 529 | { | |
| 535 | if (linda->simulate_cancel == CancelRequest::None) | 530 | // convert nils to some special non-nil sentinel in sent values |
| 531 | keeper_toggle_nil_sentinels(L, 3, eLM_ToKeeper); | ||
| 532 | } | ||
| 533 | pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L, linda, 2); | ||
| 534 | if (pushed >= 0) // no error? | ||
| 536 | { | 535 | { |
| 537 | if( has_value) | 536 | ASSERT_L(pushed == 0 || pushed == 1); |
| 537 | |||
| 538 | if (has_value) | ||
| 538 | { | 539 | { |
| 539 | // convert nils to some special non-nil sentinel in sent values | 540 | // we put some data in the slot, tell readers that they should wake |
| 540 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | 541 | SIGNAL_ALL(&linda->write_happened); // To be done from within the 'K' locking area |
| 541 | } | 542 | } |
| 542 | pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); | 543 | if (pushed == 1) |
| 543 | if( pushed >= 0) // no error? | ||
| 544 | { | 544 | { |
| 545 | ASSERT_L( pushed == 0 || pushed == 1); | 545 | // the key was full, but it is no longer the case, tell writers they should wake |
| 546 | 546 | ASSERT_L(lua_type(L, -1) == LUA_TBOOLEAN && lua_toboolean(L, -1) == 1); | |
| 547 | if( has_value) | 547 | SIGNAL_ALL(&linda->read_happened); // To be done from within the 'K' locking area |
| 548 | { | ||
| 549 | // we put some data in the slot, tell readers that they should wake | ||
| 550 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
| 551 | } | ||
| 552 | if( pushed == 1) | ||
| 553 | { | ||
| 554 | // the key was full, but it is no longer the case, tell writers they should wake | ||
| 555 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
| 556 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
| 557 | } | ||
| 558 | } | 548 | } |
| 559 | } | 549 | } |
| 560 | else // linda is cancelled | 550 | } |
| 561 | { | 551 | else // linda is cancelled |
| 562 | // do nothing and return lanes.cancel_error | 552 | { |
| 563 | CANCEL_ERROR.push(L); | 553 | // do nothing and return lanes.cancel_error |
| 564 | pushed = 1; | 554 | CANCEL_ERROR.push(L); |
| 565 | } | 555 | pushed = 1; |
| 566 | } | 556 | } |
| 567 | 557 | ||
| 568 | // must trigger any error after keeper state has been released | 558 | // must trigger any error after keeper state has been released |
| 569 | return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; | 559 | return (pushed < 0) ? luaL_error(L, "tried to copy unsupported types") : pushed; |
| 570 | } | 560 | } |
| 571 | 561 | ||
| 572 | // ################################################################################################# | 562 | // ################################################################################################# |
| @@ -576,21 +566,17 @@ LUAG_FUNC( linda_set) | |||
| 576 | * | 566 | * |
| 577 | * Get a count of the pending elements in the specified keys | 567 | * Get a count of the pending elements in the specified keys |
| 578 | */ | 568 | */ |
| 579 | LUAG_FUNC( linda_count) | 569 | LUAG_FUNC(linda_count) |
| 580 | { | 570 | { |
| 581 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 571 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 582 | int pushed; | ||
| 583 | |||
| 584 | // make sure the keys are of a valid type | 572 | // make sure the keys are of a valid type |
| 585 | check_key_types( L, 2, lua_gettop( L)); | 573 | check_key_types(L, 2, lua_gettop(L)); |
| 586 | 574 | ||
| 575 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | ||
| 576 | int const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) }; | ||
| 577 | if (pushed < 0) | ||
| 587 | { | 578 | { |
| 588 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 579 | return luaL_error(L, "tried to count an invalid key"); |
| 589 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); | ||
| 590 | if( pushed < 0) | ||
| 591 | { | ||
| 592 | return luaL_error( L, "tried to count an invalid key"); | ||
| 593 | } | ||
| 594 | } | 580 | } |
| 595 | return pushed; | 581 | return pushed; |
| 596 | } | 582 | } |
| @@ -602,39 +588,36 @@ LUAG_FUNC( linda_count) | |||
| 602 | * | 588 | * |
| 603 | * Get one or more values from Linda. | 589 | * Get one or more values from Linda. |
| 604 | */ | 590 | */ |
| 605 | LUAG_FUNC( linda_get) | 591 | LUAG_FUNC(linda_get) |
| 606 | { | 592 | { |
| 607 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 593 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 608 | int pushed; | 594 | lua_Integer const count{ luaL_optinteger(L, 3, 1) }; |
| 609 | lua_Integer count = luaL_optinteger( L, 3, 1); | 595 | luaL_argcheck(L, count >= 1, 3, "count should be >= 1"); |
| 610 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | 596 | luaL_argcheck(L, lua_gettop(L) <= 3, 4, "too many arguments"); |
| 611 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
| 612 | |||
| 613 | // make sure the key is of a valid type (throws an error if not the case) | 597 | // make sure the key is of a valid type (throws an error if not the case) |
| 614 | check_key_types( L, 2, 2); | 598 | check_key_types(L, 2, 2); |
| 599 | |||
| 600 | int pushed{ 0 }; | ||
| 601 | if (linda->simulate_cancel == CancelRequest::None) | ||
| 615 | { | 602 | { |
| 616 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 603 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
| 617 | 604 | pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2); | |
| 618 | if (linda->simulate_cancel == CancelRequest::None) | 605 | if (pushed > 0) |
| 619 | { | 606 | { |
| 620 | pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); | 607 | keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, eLM_FromKeeper); |
| 621 | if( pushed > 0) | ||
| 622 | { | ||
| 623 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
| 624 | } | ||
| 625 | } | ||
| 626 | else // linda is cancelled | ||
| 627 | { | ||
| 628 | // do nothing and return lanes.cancel_error | ||
| 629 | CANCEL_ERROR.push(L); | ||
| 630 | pushed = 1; | ||
| 631 | } | ||
| 632 | // an error can be raised if we attempt to read an unregistered function | ||
| 633 | if( pushed < 0) | ||
| 634 | { | ||
| 635 | return luaL_error( L, "tried to copy unsupported types"); | ||
| 636 | } | 608 | } |
| 637 | } | 609 | } |
| 610 | else // linda is cancelled | ||
| 611 | { | ||
| 612 | // do nothing and return lanes.cancel_error | ||
| 613 | CANCEL_ERROR.push(L); | ||
| 614 | pushed = 1; | ||
| 615 | } | ||
| 616 | // an error can be raised if we attempt to read an unregistered function | ||
| 617 | if (pushed < 0) | ||
| 618 | { | ||
| 619 | return luaL_error(L, "tried to copy unsupported types"); | ||
| 620 | } | ||
| 638 | 621 | ||
| 639 | return pushed; | 622 | return pushed; |
| 640 | } | 623 | } |
| @@ -650,8 +633,6 @@ LUAG_FUNC( linda_get) | |||
| 650 | LUAG_FUNC( linda_limit) | 633 | LUAG_FUNC( linda_limit) |
| 651 | { | 634 | { |
| 652 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 635 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 653 | int pushed; | ||
| 654 | |||
| 655 | // make sure we got 3 arguments: the linda, a key and a limit | 636 | // make sure we got 3 arguments: the linda, a key and a limit |
| 656 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | 637 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); |
| 657 | // make sure we got a numeric limit | 638 | // make sure we got a numeric limit |
| @@ -659,26 +640,24 @@ LUAG_FUNC( linda_limit) | |||
| 659 | // make sure the key is of a valid type | 640 | // make sure the key is of a valid type |
| 660 | check_key_types( L, 2, 2); | 641 | check_key_types( L, 2, 2); |
| 661 | 642 | ||
| 643 | int pushed{ 0 }; | ||
| 644 | if (linda->simulate_cancel == CancelRequest::None) | ||
| 662 | { | 645 | { |
| 663 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; | 646 | Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; |
| 664 | 647 | pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2); | |
| 665 | if (linda->simulate_cancel == CancelRequest::None) | 648 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads |
| 666 | { | 649 | if( pushed == 1) |
| 667 | pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); | ||
| 668 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | ||
| 669 | if( pushed == 1) | ||
| 670 | { | ||
| 671 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
| 672 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
| 673 | } | ||
| 674 | } | ||
| 675 | else // linda is cancelled | ||
| 676 | { | 650 | { |
| 677 | // do nothing and return lanes.cancel_error | 651 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); |
| 678 | CANCEL_ERROR.push(L); | 652 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area |
| 679 | pushed = 1; | ||
| 680 | } | 653 | } |
| 681 | } | 654 | } |
| 655 | else // linda is cancelled | ||
| 656 | { | ||
| 657 | // do nothing and return lanes.cancel_error | ||
| 658 | CANCEL_ERROR.push(L); | ||
| 659 | pushed = 1; | ||
| 660 | } | ||
| 682 | // propagate pushed boolean if any | 661 | // propagate pushed boolean if any |
| 683 | return pushed; | 662 | return pushed; |
| 684 | } | 663 | } |
| @@ -690,35 +669,34 @@ LUAG_FUNC( linda_limit) | |||
| 690 | * | 669 | * |
| 691 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | 670 | * Signal linda so that waiting threads wake up as if their own lane was cancelled |
| 692 | */ | 671 | */ |
| 693 | LUAG_FUNC( linda_cancel) | 672 | LUAG_FUNC(linda_cancel) |
| 694 | { | 673 | { |
| 695 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 674 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 696 | char const* who = luaL_optstring( L, 2, "both"); | 675 | char const* who = luaL_optstring(L, 2, "both"); |
| 697 | |||
| 698 | // make sure we got 3 arguments: the linda, a key and a limit | 676 | // make sure we got 3 arguments: the linda, a key and a limit |
| 699 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); | 677 | luaL_argcheck(L, lua_gettop(L) <= 2, 2, "wrong number of arguments"); |
| 700 | 678 | ||
| 701 | linda->simulate_cancel = CancelRequest::Soft; | 679 | linda->simulate_cancel = CancelRequest::Soft; |
| 702 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | 680 | if (strcmp(who, "both") == 0) // tell everyone writers to wake up |
| 703 | { | 681 | { |
| 704 | SIGNAL_ALL( &linda->write_happened); | 682 | SIGNAL_ALL(&linda->write_happened); |
| 705 | SIGNAL_ALL( &linda->read_happened); | 683 | SIGNAL_ALL(&linda->read_happened); |
| 706 | } | 684 | } |
| 707 | else if( strcmp( who, "none") == 0) // reset flag | 685 | else if (strcmp(who, "none") == 0) // reset flag |
| 708 | { | 686 | { |
| 709 | linda->simulate_cancel = CancelRequest::None; | 687 | linda->simulate_cancel = CancelRequest::None; |
| 710 | } | 688 | } |
| 711 | else if( strcmp( who, "read") == 0) // tell blocked readers to wake up | 689 | else if (strcmp(who, "read") == 0) // tell blocked readers to wake up |
| 712 | { | 690 | { |
| 713 | SIGNAL_ALL( &linda->write_happened); | 691 | SIGNAL_ALL(&linda->write_happened); |
| 714 | } | 692 | } |
| 715 | else if( strcmp( who, "write") == 0) // tell blocked writers to wake up | 693 | else if (strcmp(who, "write") == 0) // tell blocked writers to wake up |
| 716 | { | 694 | { |
| 717 | SIGNAL_ALL( &linda->read_happened); | 695 | SIGNAL_ALL(&linda->read_happened); |
| 718 | } | 696 | } |
| 719 | else | 697 | else |
| 720 | { | 698 | { |
| 721 | return luaL_error( L, "unknown wake hint '%s'", who); | 699 | return luaL_error(L, "unknown wake hint '%s'", who); |
| 722 | } | 700 | } |
| 723 | return 0; | 701 | return 0; |
| 724 | } | 702 | } |
| @@ -735,10 +713,10 @@ LUAG_FUNC( linda_cancel) | |||
| 735 | * different userdata and won't be known to be essentially the same deep one | 713 | * different userdata and won't be known to be essentially the same deep one |
| 736 | * without this. | 714 | * without this. |
| 737 | */ | 715 | */ |
| 738 | LUAG_FUNC( linda_deep) | 716 | LUAG_FUNC(linda_deep) |
| 739 | { | 717 | { |
| 740 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 718 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 741 | lua_pushlightuserdata( L, linda); // just the address | 719 | lua_pushlightuserdata(L, linda); // just the address |
| 742 | return 1; | 720 | return 1; |
| 743 | } | 721 | } |
| 744 | 722 | ||
| @@ -756,21 +734,21 @@ template <bool OPT> | |||
| 756 | static int linda_tostring(lua_State* L, int idx_) | 734 | static int linda_tostring(lua_State* L, int idx_) |
| 757 | { | 735 | { |
| 758 | Linda* const linda{ lua_toLinda<OPT>(L, idx_) }; | 736 | Linda* const linda{ lua_toLinda<OPT>(L, idx_) }; |
| 759 | if( linda != nullptr) | 737 | if (linda != nullptr) |
| 760 | { | 738 | { |
| 761 | char text[128]; | 739 | char text[128]; |
| 762 | int len; | 740 | int len; |
| 763 | if( linda->getName()) | 741 | if (linda->getName()) |
| 764 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->getName()); | 742 | len = sprintf(text, "Linda: %.*s", (int) sizeof(text) - 8, linda->getName()); |
| 765 | else | 743 | else |
| 766 | len = sprintf( text, "Linda: %p", linda); | 744 | len = sprintf(text, "Linda: %p", linda); |
| 767 | lua_pushlstring( L, text, len); | 745 | lua_pushlstring(L, text, len); |
| 768 | return 1; | 746 | return 1; |
| 769 | } | 747 | } |
| 770 | return 0; | 748 | return 0; |
| 771 | } | 749 | } |
| 772 | 750 | ||
| 773 | LUAG_FUNC( linda_tostring) | 751 | LUAG_FUNC(linda_tostring) |
| 774 | { | 752 | { |
| 775 | return linda_tostring<false>(L, 1); | 753 | return linda_tostring<false>(L, 1); |
| 776 | } | 754 | } |
| @@ -784,25 +762,25 @@ LUAG_FUNC( linda_tostring) | |||
| 784 | * | 762 | * |
| 785 | * Useful for concatenation or debugging purposes | 763 | * Useful for concatenation or debugging purposes |
| 786 | */ | 764 | */ |
| 787 | LUAG_FUNC( linda_concat) | 765 | LUAG_FUNC(linda_concat) |
| 788 | { // linda1? linda2? | 766 | { // linda1? linda2? |
| 789 | bool atLeastOneLinda{ false }; | 767 | bool atLeastOneLinda{ false }; |
| 790 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | 768 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. |
| 791 | if( linda_tostring<true>( L, 1)) | 769 | if (linda_tostring<true>(L, 1)) |
| 792 | { | 770 | { |
| 793 | atLeastOneLinda = true; | 771 | atLeastOneLinda = true; |
| 794 | lua_replace( L, 1); | 772 | lua_replace(L, 1); |
| 795 | } | 773 | } |
| 796 | if( linda_tostring<true>( L, 2)) | 774 | if (linda_tostring<true>(L, 2)) |
| 797 | { | 775 | { |
| 798 | atLeastOneLinda = true; | 776 | atLeastOneLinda = true; |
| 799 | lua_replace( L, 2); | 777 | lua_replace(L, 2); |
| 800 | } | 778 | } |
| 801 | if( !atLeastOneLinda) // should not be possible | 779 | if (!atLeastOneLinda) // should not be possible |
| 802 | { | 780 | { |
| 803 | return luaL_error( L, "internal error: linda_concat called on non-Linda"); | 781 | return luaL_error(L, "internal error: linda_concat called on non-Linda"); |
| 804 | } | 782 | } |
| 805 | lua_concat( L, 2); | 783 | lua_concat(L, 2); |
| 806 | return 1; | 784 | return 1; |
| 807 | } | 785 | } |
| 808 | 786 | ||
| @@ -812,17 +790,19 @@ LUAG_FUNC( linda_concat) | |||
| 812 | * table = linda:dump() | 790 | * table = linda:dump() |
| 813 | * return a table listing all pending data inside the linda | 791 | * return a table listing all pending data inside the linda |
| 814 | */ | 792 | */ |
| 815 | LUAG_FUNC( linda_dump) | 793 | LUAG_FUNC(linda_dump) |
| 816 | { | 794 | { |
| 817 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 795 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 818 | return keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()); | 796 | return keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()); |
| 819 | } | 797 | } |
| 820 | 798 | ||
| 799 | // ################################################################################################# | ||
| 800 | |||
| 821 | /* | 801 | /* |
| 822 | * table = linda:dump() | 802 | * table = linda:dump() |
| 823 | * return a table listing all pending data inside the linda | 803 | * return a table listing all pending data inside the linda |
| 824 | */ | 804 | */ |
| 825 | LUAG_FUNC( linda_towatch) | 805 | LUAG_FUNC(linda_towatch) |
| 826 | { | 806 | { |
| 827 | Linda* const linda{ lua_toLinda<false>(L, 1) }; | 807 | Linda* const linda{ lua_toLinda<false>(L, 1) }; |
| 828 | int pushed{ keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()) }; | 808 | int pushed{ keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()) }; |
| @@ -834,6 +814,8 @@ LUAG_FUNC( linda_towatch) | |||
| 834 | return pushed; | 814 | return pushed; |
| 835 | } | 815 | } |
| 836 | 816 | ||
| 817 | // ################################################################################################# | ||
| 818 | |||
| 837 | /* | 819 | /* |
| 838 | * Identity function of a shared userdata object. | 820 | * Identity function of a shared userdata object. |
| 839 | * | 821 | * |
| @@ -868,51 +850,51 @@ static void* linda_id( lua_State* L, DeepOp op_) | |||
| 868 | char const* linda_name = nullptr; | 850 | char const* linda_name = nullptr; |
| 869 | unsigned long linda_group = 0; | 851 | unsigned long linda_group = 0; |
| 870 | // should have a string and/or a number of the stack as parameters (name and group) | 852 | // should have a string and/or a number of the stack as parameters (name and group) |
| 871 | switch( lua_gettop( L)) | 853 | switch (lua_gettop(L)) |
| 872 | { | 854 | { |
| 873 | default: // 0 | 855 | default: // 0 |
| 874 | break; | 856 | break; |
| 875 | 857 | ||
| 876 | case 1: // 1 parameter, either a name or a group | 858 | case 1: // 1 parameter, either a name or a group |
| 877 | if( lua_type( L, -1) == LUA_TSTRING) | 859 | if (lua_type(L, -1) == LUA_TSTRING) |
| 878 | { | 860 | { |
| 879 | linda_name = lua_tolstring( L, -1, &name_len); | 861 | linda_name = lua_tolstring(L, -1, &name_len); |
| 880 | } | 862 | } |
| 881 | else | 863 | else |
| 882 | { | 864 | { |
| 883 | linda_group = (unsigned long) lua_tointeger( L, -1); | 865 | linda_group = (unsigned long) lua_tointeger(L, -1); |
| 884 | } | 866 | } |
| 885 | break; | 867 | break; |
| 886 | 868 | ||
| 887 | case 2: // 2 parameters, a name and group, in that order | 869 | case 2: // 2 parameters, a name and group, in that order |
| 888 | linda_name = lua_tolstring( L, -2, &name_len); | 870 | linda_name = lua_tolstring(L, -2, &name_len); |
| 889 | linda_group = (unsigned long) lua_tointeger( L, -1); | 871 | linda_group = (unsigned long) lua_tointeger(L, -1); |
| 890 | break; | 872 | break; |
| 891 | } | 873 | } |
| 892 | 874 | ||
| 893 | /* The deep data is allocated separately of Lua stack; we might no | 875 | /* The deep data is allocated separately of Lua stack; we might no |
| 894 | * longer be around when last reference to it is being released. | 876 | * longer be around when last reference to it is being released. |
| 895 | * One can use any memory allocation scheme. | 877 | * One can use any memory allocation scheme. |
| 896 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda | 878 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda |
| 897 | */ | 879 | */ |
| 898 | Universe* const U{ universe_get(L) }; | 880 | Universe* const U{ universe_get(L) }; |
| 899 | Linda* s{ new (U) Linda{ U, linda_group, linda_name, name_len } }; | 881 | Linda* linda{ new (U) Linda{ U, linda_group, linda_name, name_len } }; |
| 900 | return s; | 882 | return linda; |
| 901 | } | 883 | } |
| 902 | 884 | ||
| 903 | case eDO_delete: | 885 | case eDO_delete: |
| 904 | { | 886 | { |
| 905 | Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) }; | 887 | Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) }; |
| 906 | ASSERT_L( linda); | 888 | ASSERT_L(linda); |
| 907 | 889 | ||
| 908 | // Clean associated structures in the keeper state. | 890 | // Clean associated structures in the keeper state. |
| 909 | Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; | 891 | Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; |
| 910 | if( K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | 892 | if (K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) |
| 911 | { | 893 | { |
| 912 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | 894 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... |
| 913 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); | 895 | keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0); |
| 914 | } | 896 | } |
| 915 | keeper_release( K); | 897 | keeper_release(K); |
| 916 | 898 | ||
| 917 | delete linda; // operator delete overload ensures things go as expected | 899 | delete linda; // operator delete overload ensures things go as expected |
| 918 | return nullptr; | 900 | return nullptr; |
| @@ -920,73 +902,72 @@ static void* linda_id( lua_State* L, DeepOp op_) | |||
| 920 | 902 | ||
| 921 | case eDO_metatable: | 903 | case eDO_metatable: |
| 922 | { | 904 | { |
| 923 | |||
| 924 | STACK_CHECK_START_REL(L, 0); | 905 | STACK_CHECK_START_REL(L, 0); |
| 925 | lua_newtable( L); | 906 | lua_newtable(L); |
| 926 | // metatable is its own index | 907 | // metatable is its own index |
| 927 | lua_pushvalue( L, -1); | 908 | lua_pushvalue(L, -1); |
| 928 | lua_setfield( L, -2, "__index"); | 909 | lua_setfield(L, -2, "__index"); |
| 929 | 910 | ||
| 930 | // protect metatable from external access | 911 | // protect metatable from external access |
| 931 | lua_pushliteral( L, "Linda"); | 912 | lua_pushliteral(L, "Linda"); |
| 932 | lua_setfield( L, -2, "__metatable"); | 913 | lua_setfield(L, -2, "__metatable"); |
| 933 | 914 | ||
| 934 | lua_pushcfunction( L, LG_linda_tostring); | 915 | lua_pushcfunction(L, LG_linda_tostring); |
| 935 | lua_setfield( L, -2, "__tostring"); | 916 | lua_setfield(L, -2, "__tostring"); |
| 936 | 917 | ||
| 937 | // Decoda __towatch support | 918 | // Decoda __towatch support |
| 938 | lua_pushcfunction( L, LG_linda_towatch); | 919 | lua_pushcfunction(L, LG_linda_towatch); |
| 939 | lua_setfield( L, -2, "__towatch"); | 920 | lua_setfield(L, -2, "__towatch"); |
| 940 | 921 | ||
| 941 | lua_pushcfunction( L, LG_linda_concat); | 922 | lua_pushcfunction(L, LG_linda_concat); |
| 942 | lua_setfield( L, -2, "__concat"); | 923 | lua_setfield(L, -2, "__concat"); |
| 943 | 924 | ||
| 944 | // protected calls, to ensure associated keeper is always released even in case of error | 925 | // protected calls, to ensure associated keeper is always released even in case of error |
| 945 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | 926 | // all function are the protected call wrapper, where the actual operation is provided as upvalue |
| 946 | // note that this kind of thing can break function lookup as we use the function pointer here and there | 927 | // note that this kind of thing can break function lookup as we use the function pointer here and there |
| 947 | 928 | ||
| 948 | lua_pushcfunction( L, LG_linda_send); | 929 | lua_pushcfunction(L, LG_linda_send); |
| 949 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 930 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 950 | lua_setfield( L, -2, "send"); | 931 | lua_setfield(L, -2, "send"); |
| 951 | 932 | ||
| 952 | lua_pushcfunction( L, LG_linda_receive); | 933 | lua_pushcfunction(L, LG_linda_receive); |
| 953 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 934 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 954 | lua_setfield( L, -2, "receive"); | 935 | lua_setfield(L, -2, "receive"); |
| 955 | 936 | ||
| 956 | lua_pushcfunction( L, LG_linda_limit); | 937 | lua_pushcfunction(L, LG_linda_limit); |
| 957 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 938 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 958 | lua_setfield( L, -2, "limit"); | 939 | lua_setfield(L, -2, "limit"); |
| 959 | 940 | ||
| 960 | lua_pushcfunction( L, LG_linda_set); | 941 | lua_pushcfunction(L, LG_linda_set); |
| 961 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 942 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 962 | lua_setfield( L, -2, "set"); | 943 | lua_setfield(L, -2, "set"); |
| 963 | 944 | ||
| 964 | lua_pushcfunction( L, LG_linda_count); | 945 | lua_pushcfunction(L, LG_linda_count); |
| 965 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 946 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 966 | lua_setfield( L, -2, "count"); | 947 | lua_setfield(L, -2, "count"); |
| 967 | 948 | ||
| 968 | lua_pushcfunction( L, LG_linda_get); | 949 | lua_pushcfunction(L, LG_linda_get); |
| 969 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 950 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 970 | lua_setfield( L, -2, "get"); | 951 | lua_setfield(L, -2, "get"); |
| 971 | 952 | ||
| 972 | lua_pushcfunction( L, LG_linda_cancel); | 953 | lua_pushcfunction(L, LG_linda_cancel); |
| 973 | lua_setfield( L, -2, "cancel"); | 954 | lua_setfield(L, -2, "cancel"); |
| 974 | 955 | ||
| 975 | lua_pushcfunction( L, LG_linda_deep); | 956 | lua_pushcfunction(L, LG_linda_deep); |
| 976 | lua_setfield( L, -2, "deep"); | 957 | lua_setfield(L, -2, "deep"); |
| 977 | 958 | ||
| 978 | lua_pushcfunction( L, LG_linda_dump); | 959 | lua_pushcfunction(L, LG_linda_dump); |
| 979 | lua_pushcclosure( L, LG_linda_protected_call, 1); | 960 | lua_pushcclosure(L, LG_linda_protected_call, 1); |
| 980 | lua_setfield( L, -2, "dump"); | 961 | lua_setfield(L, -2, "dump"); |
| 981 | 962 | ||
| 982 | // some constants | 963 | // some constants |
| 983 | lua_pushliteral( L, BATCH_SENTINEL); | 964 | BATCH_SENTINEL.push(L); |
| 984 | lua_setfield( L, -2, "batched"); | 965 | lua_setfield(L, -2, "batched"); |
| 985 | 966 | ||
| 986 | NIL_SENTINEL.push(L); | 967 | NIL_SENTINEL.push(L); |
| 987 | lua_setfield( L, -2, "null"); | 968 | lua_setfield(L, -2, "null"); |
| 988 | 969 | ||
| 989 | STACK_CHECK( L, 1); | 970 | STACK_CHECK(L, 1); |
| 990 | return nullptr; | 971 | return nullptr; |
| 991 | } | 972 | } |
| 992 | 973 | ||
| @@ -1001,24 +982,26 @@ static void* linda_id( lua_State* L, DeepOp op_) | |||
| 1001 | } | 982 | } |
| 1002 | } | 983 | } |
| 1003 | 984 | ||
| 985 | // ################################################################################################# | ||
| 986 | |||
| 1004 | /* | 987 | /* |
| 1005 | * ud = lanes.linda( [name[,group]]) | 988 | * ud = lanes.linda( [name[,group]]) |
| 1006 | * | 989 | * |
| 1007 | * returns a linda object, or raises an error if creation failed | 990 | * returns a linda object, or raises an error if creation failed |
| 1008 | */ | 991 | */ |
| 1009 | LUAG_FUNC( linda) | 992 | LUAG_FUNC(linda) |
| 1010 | { | 993 | { |
| 1011 | int const top = lua_gettop( L); | 994 | int const top = lua_gettop(L); |
| 1012 | luaL_argcheck( L, top <= 2, top, "too many arguments"); | 995 | luaL_argcheck(L, top <= 2, top, "too many arguments"); |
| 1013 | if( top == 1) | 996 | if (top == 1) |
| 1014 | { | 997 | { |
| 1015 | int const t = lua_type( L, 1); | 998 | int const t = lua_type(L, 1); |
| 1016 | luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); | 999 | luaL_argcheck(L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); |
| 1017 | } | 1000 | } |
| 1018 | else if( top == 2) | 1001 | else if (top == 2) |
| 1019 | { | 1002 | { |
| 1020 | luaL_checktype( L, 1, LUA_TSTRING); | 1003 | luaL_checktype(L, 1, LUA_TSTRING); |
| 1021 | luaL_checktype( L, 2, LUA_TNUMBER); | 1004 | luaL_checktype(L, 2, LUA_TNUMBER); |
| 1022 | } | 1005 | } |
| 1023 | return luaG_newdeepuserdata( L, linda_id, 0); | 1006 | return luaG_newdeepuserdata(L, linda_id, 0); |
| 1024 | } | 1007 | } |
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 @@ | |||
| 3 | #include "compat.h" | 3 | #include "compat.h" |
| 4 | #include "macros_and_utils.h" | 4 | #include "macros_and_utils.h" |
| 5 | 5 | ||
| 6 | #include <bit> | ||
| 7 | |||
| 6 | class UniqueKey | 8 | class UniqueKey |
| 7 | { | 9 | { |
| 8 | private: | 10 | private: |
| @@ -31,13 +33,11 @@ class UniqueKey | |||
| 31 | 33 | ||
| 32 | void push(lua_State* const L) const | 34 | void push(lua_State* const L) const |
| 33 | { | 35 | { |
| 34 | // unfortunately, converting a scalar to a pointer must go through a C cast | 36 | lua_pushlightuserdata(L, std::bit_cast<void*>(m_storage)); |
| 35 | lua_pushlightuserdata(L, (void*) m_storage); | ||
| 36 | } | 37 | } |
| 37 | bool equals(lua_State* const L, int i) const | 38 | bool equals(lua_State* const L, int i) const |
| 38 | { | 39 | { |
| 39 | // unfortunately, converting a scalar to a pointer must go through a C cast | 40 | return lua_touserdata(L, i) == std::bit_cast<void*>(m_storage); |
| 40 | return lua_touserdata(L, i) == (void*) m_storage; | ||
| 41 | } | 41 | } |
| 42 | void query_registry(lua_State* const L) const | 42 | void query_registry(lua_State* const L) const |
| 43 | { | 43 | { |
