diff options
| author | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-01-09 12:07:41 +0100 |
|---|---|---|
| committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-01-09 12:07:41 +0100 |
| commit | cc03c619dafe39cab231045f3c8592398e4b6944 (patch) | |
| tree | de471018fa75ad0255b3f986609c17af10eefcfa /src | |
| parent | b335cbcc9f07dc71999b885ffa2962c0ec00f5eb (diff) | |
| download | lanes-cc03c619dafe39cab231045f3c8592398e4b6944.tar.gz lanes-cc03c619dafe39cab231045f3c8592398e4b6944.tar.bz2 lanes-cc03c619dafe39cab231045f3c8592398e4b6944.zip | |
Linda fixes
* bumped version to 3.7.7
* fix crash when calling linda:count() on unknown keys
* purge key storage with linda:set( key, nil) on an unlimited key to
reduce memory usage with lots of keys
* linda:limit() wakes write-blocked threads if necessary when the new
limit enables writes to occur again
* linda:set() wakes write-blocked threads if necessary if the operation
created some room to write into
Diffstat (limited to 'src')
| -rw-r--r-- | src/keeper.c | 96 | ||||
| -rw-r--r-- | src/lanes.c | 55 |
2 files changed, 96 insertions, 55 deletions
diff --git a/src/keeper.c b/src/keeper.c index 4c90069..6d5ecbf 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
| @@ -95,7 +95,7 @@ static void fifo_new( lua_State* L) | |||
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | // in: expect fifo ... on top of the stack | 97 | // in: expect fifo ... on top of the stack |
| 98 | // out: nothing, removes all pushed values on the stack | 98 | // out: nothing, removes all pushed values from the stack |
| 99 | static void fifo_push( lua_State* L, keeper_fifo* fifo, int _count) | 99 | static void fifo_push( lua_State* L, keeper_fifo* fifo, int _count) |
| 100 | { | 100 | { |
| 101 | int idx = lua_gettop( L) - _count; | 101 | int idx = lua_gettop( L) - _count; |
| @@ -349,7 +349,7 @@ int keepercall_receive_batched( lua_State* L) | |||
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | // in: linda_ud key n | 351 | // in: linda_ud key n |
| 352 | // out: nothing | 352 | // out: true or nil |
| 353 | int keepercall_limit( lua_State* L) | 353 | int keepercall_limit( lua_State* L) |
| 354 | { | 354 | { |
| 355 | keeper_fifo* fifo; | 355 | keeper_fifo* fifo; |
| @@ -358,22 +358,37 @@ int keepercall_limit( lua_State* L) | |||
| 358 | lua_replace( L, 1); // fifos key n | 358 | lua_replace( L, 1); // fifos key n |
| 359 | lua_pop( L, 1); // fifos key | 359 | lua_pop( L, 1); // fifos key |
| 360 | lua_pushvalue( L, -1); // fifos key key | 360 | lua_pushvalue( L, -1); // fifos key key |
| 361 | lua_rawget( L, -3); // fifos key fifo | 361 | lua_rawget( L, -3); // fifos key fifo|nil |
| 362 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | 362 | fifo = (keeper_fifo*) lua_touserdata( L, -1); |
| 363 | if( fifo == NULL) | 363 | if( fifo == NULL) |
| 364 | { | 364 | { // fifos key nil |
| 365 | lua_pop( L, 1); // fifos key | 365 | lua_pop( L, 1); // fifos key |
| 366 | fifo_new( L); // fifos key fifo | 366 | fifo_new( L); // fifos key fifo |
| 367 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | 367 | fifo = (keeper_fifo*) lua_touserdata( L, -1); |
| 368 | lua_rawset( L, -3); // fifos | 368 | lua_rawset( L, -3); // fifos |
| 369 | } | 369 | } |
| 370 | // remove any clutter on the stack | ||
| 371 | lua_settop( L, 0); | ||
| 372 | // return true if we decide that blocked threads waiting to write on that key should be awakened | ||
| 373 | // this is the case if we detect the key was full but it is no longer the case | ||
| 374 | if( | ||
| 375 | ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit | ||
| 376 | && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit | ||
| 377 | ) | ||
| 378 | { | ||
| 379 | lua_pushboolean( L, 1); | ||
| 380 | } | ||
| 381 | // set the new limit | ||
| 370 | fifo->limit = limit; | 382 | fifo->limit = limit; |
| 371 | return 0; | 383 | // return 0 or 1 value |
| 384 | return lua_gettop( L); | ||
| 372 | } | 385 | } |
| 373 | 386 | ||
| 374 | //in: linda_ud key [val] | 387 | //in: linda_ud key [val] |
| 388 | //out: true or nil | ||
| 375 | int keepercall_set( lua_State* L) | 389 | int keepercall_set( lua_State* L) |
| 376 | { | 390 | { |
| 391 | bool_t should_wake_writers = FALSE; | ||
| 377 | STACK_GROW( L, 6); | 392 | STACK_GROW( L, 6); |
| 378 | // make sure we have a value on the stack | 393 | // make sure we have a value on the stack |
| 379 | if( lua_gettop( L) == 2) // ud key val? | 394 | if( lua_gettop( L) == 2) // ud key val? |
| @@ -391,16 +406,18 @@ int keepercall_set( lua_State* L) | |||
| 391 | lua_pushvalue( L, -2); // fifos key val key | 406 | lua_pushvalue( L, -2); // fifos key val key |
| 392 | lua_rawget( L, 1); // fifos key val fifo|nil | 407 | lua_rawget( L, 1); // fifos key val fifo|nil |
| 393 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | 408 | fifo = (keeper_fifo*) lua_touserdata( L, -1); |
| 394 | if( fifo == NULL) // might be NULL if we set a nonexistent key to nil | 409 | if( fifo == NULL) // can be NULL if we store a value at a new key |
| 395 | { | 410 | { // fifos key val nil |
| 396 | lua_pop( L, 1); // fifos key val | 411 | lua_pop( L, 1); // fifos key val |
| 397 | fifo_new( L); // fifos key val fifo | 412 | fifo_new( L); // fifos key val fifo |
| 398 | lua_pushvalue( L, 2); // fifos key val fifo key | 413 | lua_pushvalue( L, 2); // fifos key val fifo key |
| 399 | lua_pushvalue( L, -2); // fifos key val fifo key fifo | 414 | lua_pushvalue( L, -2); // fifos key val fifo key fifo |
| 400 | lua_rawset( L, 1); // fifos key val fifo | 415 | lua_rawset( L, 1); // fifos key val fifo |
| 401 | } | 416 | } |
| 402 | else // the fifo exists, we just want to clear its contents | 417 | else // the fifo exists, we just want to update its contents |
| 403 | { | 418 | { // fifos key val fifo |
| 419 | // we create room if the fifo was full but it is no longer the case | ||
| 420 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); | ||
| 404 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 421 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
| 405 | lua_newtable( L); // fifos key val fifo {} | 422 | lua_newtable( L); // fifos key val fifo {} |
| 406 | lua_setuservalue( L, -2); // fifos key val fifo | 423 | lua_setuservalue( L, -2); // fifos key val fifo |
| @@ -411,22 +428,35 @@ int keepercall_set( lua_State* L) | |||
| 411 | lua_insert( L, -2); // fifos key fifo val | 428 | lua_insert( L, -2); // fifos key fifo val |
| 412 | fifo_push( L, fifo, 1); // fifos key fifo | 429 | fifo_push( L, fifo, 1); // fifos key fifo |
| 413 | } | 430 | } |
| 414 | else // val == nil // fifos key nil | 431 | else // val == nil: we clear the key contents |
| 415 | { | 432 | { // fifos key nil |
| 416 | keeper_fifo* fifo; | 433 | keeper_fifo* fifo; |
| 417 | lua_pop( L, 1); // fifos key | 434 | lua_pop( L, 1); // fifos key |
| 418 | lua_rawget( L, 1); // fifos fifo|nil | 435 | lua_pushvalue( L, -1); // fifos key key |
| 436 | lua_rawget( L, 1); // fifos key fifo|nil | ||
| 419 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 437 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
| 420 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | 438 | fifo = (keeper_fifo*) lua_touserdata( L, -1); |
| 421 | if( fifo != NULL) // might be NULL if we set a nonexistent key to nil | 439 | if( fifo != NULL) // might be NULL if we set a nonexistent key to nil |
| 422 | { | 440 | { // fifos key fifo |
| 423 | lua_newtable( L); // fifos fifo {} | 441 | if( fifo->limit < 0) // fifo limit value is the default (unlimited): we can totally remove it |
| 424 | lua_setuservalue( L, -2); // fifos fifo | 442 | { |
| 425 | fifo->first = 1; | 443 | lua_pop( L, 1); // fifos key |
| 426 | fifo->count = 0; | 444 | lua_pushnil( L); // fifos key nil |
| 445 | lua_rawset( L, -3); // fifos | ||
| 446 | } | ||
| 447 | else | ||
| 448 | { | ||
| 449 | // we create room if the fifo was full but it is no longer the case | ||
| 450 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); | ||
| 451 | lua_remove( L, -2); // fifos fifo | ||
| 452 | lua_newtable( L); // fifos fifo {} | ||
| 453 | lua_setuservalue( L, -2); // fifos fifo | ||
| 454 | fifo->first = 1; | ||
| 455 | fifo->count = 0; | ||
| 456 | } | ||
| 427 | } | 457 | } |
| 428 | } | 458 | } |
| 429 | return 0; | 459 | return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0; |
| 430 | } | 460 | } |
| 431 | 461 | ||
| 432 | // in: linda_ud key | 462 | // in: linda_ud key |
| @@ -476,11 +506,18 @@ int keepercall_count( lua_State* L) | |||
| 476 | { | 506 | { |
| 477 | keeper_fifo* fifo; | 507 | keeper_fifo* fifo; |
| 478 | lua_replace( L, 1); // fifos key | 508 | lua_replace( L, 1); // fifos key |
| 479 | lua_rawget( L, -2); // fifos fifo | 509 | lua_rawget( L, -2); // fifos fifo|nil |
| 480 | fifo = prepare_fifo_access( L, -1); // fifos fifo | 510 | if( lua_isnil( L, -1)) // the key is unknown |
| 481 | lua_pushinteger( L, fifo->count); // fifos fifo count | 511 | { // fifos nil |
| 482 | lua_replace( L, -3); // count fifo | 512 | lua_remove( L, -2); // nil |
| 483 | lua_pop( L, 1); // count | 513 | } |
| 514 | else // the key is known | ||
| 515 | { // fifos fifo | ||
| 516 | fifo = prepare_fifo_access( L, -1); // fifos fifo | ||
| 517 | lua_pushinteger( L, fifo->count); // fifos fifo count | ||
| 518 | lua_replace( L, -3); // count fifo | ||
| 519 | lua_pop( L, 1); // count | ||
| 520 | } | ||
| 484 | } | 521 | } |
| 485 | break; | 522 | break; |
| 486 | 523 | ||
| @@ -494,21 +531,22 @@ int keepercall_count( lua_State* L) | |||
| 494 | { | 531 | { |
| 495 | keeper_fifo* fifo; | 532 | keeper_fifo* fifo; |
| 496 | lua_pushvalue( L, -1); // out fifos keys key | 533 | lua_pushvalue( L, -1); // out fifos keys key |
| 497 | lua_rawget( L, 2); // out fifos keys fifo | 534 | lua_rawget( L, 2); // out fifos keys fifo|nil |
| 498 | fifo = prepare_fifo_access( L, -1); // out fifos keys fifo | 535 | fifo = prepare_fifo_access( L, -1); // out fifos keys fifo|nil |
| 499 | lua_pop( L, 1); // out fifos keys | 536 | lua_pop( L, 1); // out fifos keys |
| 500 | if( fifo != NULL) | 537 | if( fifo != NULL) // the key is known |
| 501 | { | 538 | { |
| 502 | lua_pushinteger( L, fifo->count); // out fifos keys count | 539 | lua_pushinteger( L, fifo->count); // out fifos keys count |
| 503 | lua_rawset( L, 1); // out fifos keys | 540 | lua_rawset( L, 1); // out fifos keys |
| 504 | } | 541 | } |
| 505 | else | 542 | else // the key is unknown |
| 506 | { | 543 | { |
| 507 | lua_pop( L, 1); // out fifos keys | 544 | lua_pop( L, 1); // out fifos keys |
| 508 | } | 545 | } |
| 509 | } | 546 | } |
| 510 | lua_pop( L, 1); // out | 547 | lua_pop( L, 1); // out |
| 511 | } | 548 | } |
| 549 | ASSERT_L( lua_gettop( L) == 1); | ||
| 512 | return 1; | 550 | return 1; |
| 513 | } | 551 | } |
| 514 | 552 | ||
| @@ -681,7 +719,7 @@ void keeper_toggle_nil_sentinels( lua_State* L, int _val_i, int _nil_to_sentinel | |||
| 681 | * | 719 | * |
| 682 | * Returns: number of return values (pushed to 'L') or -1 in case of error | 720 | * Returns: number of return values (pushed to 'L') or -1 in case of error |
| 683 | */ | 721 | */ |
| 684 | int keeper_call( lua_State *K, keeper_api_t _func, lua_State *L, void *linda, uint_t starting_index) | 722 | int keeper_call( lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) |
| 685 | { | 723 | { |
| 686 | int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0; | 724 | int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0; |
| 687 | int const Ktos = lua_gettop( K); | 725 | int const Ktos = lua_gettop( K); |
| @@ -689,7 +727,7 @@ int keeper_call( lua_State *K, keeper_api_t _func, lua_State *L, void *linda, ui | |||
| 689 | 727 | ||
| 690 | STACK_GROW( K, 2); | 728 | STACK_GROW( K, 2); |
| 691 | 729 | ||
| 692 | PUSH_KEEPER_FUNC( K, _func); | 730 | PUSH_KEEPER_FUNC( K, func_); |
| 693 | 731 | ||
| 694 | lua_pushlightuserdata( K, linda); | 732 | lua_pushlightuserdata( K, linda); |
| 695 | 733 | ||
diff --git a/src/lanes.c b/src/lanes.c index 3ccf915..d852a20 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
| @@ -52,7 +52,7 @@ | |||
| 52 | * ... | 52 | * ... |
| 53 | */ | 53 | */ |
| 54 | 54 | ||
| 55 | char const* VERSION = "3.7.6"; | 55 | char const* VERSION = "3.7.7"; |
| 56 | 56 | ||
| 57 | /* | 57 | /* |
| 58 | =============================================================================== | 58 | =============================================================================== |
| @@ -672,7 +672,7 @@ LUAG_FUNC( linda_receive) | |||
| 672 | 672 | ||
| 673 | 673 | ||
| 674 | /* | 674 | /* |
| 675 | * = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) | 675 | * [true] = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) |
| 676 | * | 676 | * |
| 677 | * Set a value to Linda. | 677 | * Set a value to Linda. |
| 678 | * TODO: what do we do if we set to non-nil and limit is 0? | 678 | * TODO: what do we do if we set to non-nil and limit is 0? |
| @@ -682,6 +682,7 @@ LUAG_FUNC( linda_receive) | |||
| 682 | LUAG_FUNC( linda_set) | 682 | LUAG_FUNC( linda_set) |
| 683 | { | 683 | { |
| 684 | struct s_Linda *linda = lua_toLinda( L, 1); | 684 | struct s_Linda *linda = lua_toLinda( L, 1); |
| 685 | int pushed; | ||
| 685 | bool_t has_value = !lua_isnil( L, 3); | 686 | bool_t has_value = !lua_isnil( L, 3); |
| 686 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 687 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
| 687 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | 688 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); |
| @@ -690,31 +691,31 @@ LUAG_FUNC( linda_set) | |||
| 690 | check_key_types( L, 2, 2); | 691 | check_key_types( L, 2, 2); |
| 691 | 692 | ||
| 692 | { | 693 | { |
| 693 | int pushed; | 694 | struct s_Keeper* K = keeper_acquire( linda); |
| 694 | struct s_Keeper *K = keeper_acquire( linda); | ||
| 695 | if( K == NULL) return 0; | 695 | if( K == NULL) return 0; |
| 696 | // no nil->sentinel toggling, we really clear the linda contents for the given key with a set() | 696 | // no nil->sentinel toggling, we really clear the linda contents for the given key with a set() |
| 697 | pushed = keeper_call( K->L, KEEPER_API( set), L, linda, 2); | 697 | pushed = keeper_call( K->L, KEEPER_API( set), L, linda, 2); |
| 698 | if( pushed >= 0) // no error? | 698 | if( pushed >= 0) // no error? |
| 699 | { | 699 | { |
| 700 | ASSERT_L( pushed == 0); | 700 | ASSERT_L( pushed == 0 || pushed == 1); |
| 701 | 701 | ||
| 702 | /* Set the signal from within 'K' locking. | ||
| 703 | */ | ||
| 704 | if( has_value) | 702 | if( has_value) |
| 705 | { | 703 | { |
| 706 | SIGNAL_ALL( &linda->write_happened); | 704 | // we put some data in the slot, tell readers that they should wake |
| 705 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
| 706 | } | ||
| 707 | if( pushed == 1) | ||
| 708 | { | ||
| 709 | // the key was full, but it is no longer the case, tell writers they should wake | ||
| 710 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
| 711 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
| 707 | } | 712 | } |
| 708 | } | 713 | } |
| 709 | keeper_release( K); | 714 | keeper_release( K); |
| 710 | // must trigger error after keeper state has been released | ||
| 711 | if( pushed < 0) | ||
| 712 | { | ||
| 713 | return luaL_error( L, "tried to copy unsupported types"); | ||
| 714 | } | ||
| 715 | } | 715 | } |
| 716 | 716 | ||
| 717 | return 0; | 717 | // must trigger any error after keeper state has been released |
| 718 | return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; | ||
| 718 | } | 719 | } |
| 719 | 720 | ||
| 720 | 721 | ||
| @@ -783,18 +784,20 @@ LUAG_FUNC( linda_get) | |||
| 783 | 784 | ||
| 784 | 785 | ||
| 785 | /* | 786 | /* |
| 786 | * = linda_limit( linda_ud, key_num|str|bool|lightuserdata, uint [, ...] ) | 787 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int [, bool] ) |
| 787 | * | 788 | * |
| 788 | * Set limits to 1 or more Linda keys. | 789 | * Set limit to 1 Linda keys. |
| 790 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | ||
| 789 | */ | 791 | */ |
| 790 | LUAG_FUNC( linda_limit) | 792 | LUAG_FUNC( linda_limit) |
| 791 | { | 793 | { |
| 792 | struct s_Linda* linda= lua_toLinda( L, 1); | 794 | struct s_Linda* linda = lua_toLinda( L, 1); |
| 793 | int pushed; | 795 | int pushed; |
| 796 | bool_t wake_writers = FALSE; | ||
| 794 | 797 | ||
| 798 | // make sure we got at most 4 arguments: the linda, a key, a limit, and an optional wake-up flag. | ||
| 795 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 799 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
| 796 | // make sure we got a key and a limit | 800 | luaL_argcheck( L, lua_gettop( L) <= 4, 2, "wrong number of arguments"); |
| 797 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | ||
| 798 | // make sure we got a numeric limit | 801 | // make sure we got a numeric limit |
| 799 | luaL_checknumber( L, 3); | 802 | luaL_checknumber( L, 3); |
| 800 | // make sure the key is of a valid type | 803 | // make sure the key is of a valid type |
| @@ -804,16 +807,16 @@ LUAG_FUNC( linda_limit) | |||
| 804 | struct s_Keeper* K = keeper_acquire( linda); | 807 | struct s_Keeper* K = keeper_acquire( linda); |
| 805 | if( K == NULL) return 0; | 808 | if( K == NULL) return 0; |
| 806 | pushed = keeper_call( K->L, KEEPER_API( limit), L, linda, 2); | 809 | pushed = keeper_call( K->L, KEEPER_API( limit), L, linda, 2); |
| 807 | ASSERT_L( pushed <= 0); // either error or no return values | 810 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads |
| 808 | keeper_release( K); | 811 | if( pushed == 1) |
| 809 | // must trigger error after keeper state has been released | ||
| 810 | if( pushed < 0) | ||
| 811 | { | 812 | { |
| 812 | return luaL_error( L, "tried to copy unsupported types"); | 813 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); |
| 814 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
| 813 | } | 815 | } |
| 816 | keeper_release( K); | ||
| 814 | } | 817 | } |
| 815 | 818 | // propagate pushed boolean if any | |
| 816 | return 0; | 819 | return pushed; |
| 817 | } | 820 | } |
| 818 | 821 | ||
| 819 | 822 | ||
