diff options
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | docs/index.html | 24 | ||||
-rw-r--r-- | src/keeper.c | 102 | ||||
-rw-r--r-- | src/keeper.h | 2 | ||||
-rw-r--r-- | src/lanes.c | 44 | ||||
-rw-r--r-- | src/lanes.lua | 2 |
6 files changed, 101 insertions, 78 deletions
@@ -1,5 +1,10 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 91: BGe 20-Jan-14 | ||
4 | * version 3.8.0 | ||
5 | * linda:set() accepts multiple values to set in the specified slot | ||
6 | * linda:get() accepts an optional count to peek several values at once | ||
7 | |||
3 | CHANGE 90: BGe 16-Jan-14 | 8 | CHANGE 90: BGe 16-Jan-14 |
4 | * version 3.7.8 | 9 | * version 3.7.8 |
5 | * lane:cancel() now accepts a boolean second argument when soft cancelling (negative timeout) to wake the thread if necessary | 10 | * lane:cancel() now accepts a boolean second argument when soft cancelling (negative timeout) to wake the thread if necessary |
diff --git a/docs/index.html b/docs/index.html index 3b64cb4..da37cef 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -70,7 +70,7 @@ | |||
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 16-Jan-14, and applies to version <tt>3.7.8</tt>. | 73 | This document was revised on 20-Jan-14, and applies to version <tt>3.8.0</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -1068,23 +1068,31 @@ | |||
1068 | </p> | 1068 | </p> |
1069 | 1069 | ||
1070 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1070 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1071 | [true] = linda_h:set( key, [val]) | 1071 | [bool] = linda_h:set( key [, val [, ...]]) |
1072 | 1072 | ||
1073 | [val] = linda_h:get( key) | 1073 | [val [, ...]] = linda_h:get( key [, count = 1]) |
1074 | </pre></td></tr></table> | 1074 | </pre></td></tr></table> |
1075 | 1075 | ||
1076 | <p> | 1076 | <p> |
1077 | The table access methods are for accessing a slot without queuing or consuming. They can be used for making shared tables of storage among the lanes. | 1077 | The table access methods are for accessing a slot without queuing or consuming. They can be used for making shared tables of storage among the lanes. |
1078 | <br/> | ||
1079 | Writing to a slot never blocks because it ignores the limit. It overwrites existing value and clears any possible queued entries. | ||
1080 | <br/> | ||
1081 | Reading doesn't block either because <tt>get()</tt> returns whatever is available (which can be nothing), up to the specified count. | ||
1082 | <br/> | ||
1083 | Table access and <tt>send()</tt>/<tt>receive()</tt> can be used together; reading a slot essentially peeks the next outcoming value of a queue. | ||
1078 | </p> | 1084 | </p> |
1079 | 1085 | ||
1080 | <p> | 1086 | <p> |
1081 | Writing to a slot never blocks because it ignores the limit. It overwrites existing value and clears any possible queued entries. | 1087 | <tt>set()</tt> signals the linda for write if a value is stored. |
1082 | <br/> | 1088 | <br/> |
1083 | <tt>set()</tt> signals the linda from write if a value is stored. | 1089 | (Since version 3.7.7) if the key was full but the new data count of the key after <tt>set()</tt> is below its limit, <tt>set()</tt> returns <tt>true</tt> and the linda is also signaled for read so that send()-blocked threads are awakened. |
1084 | <br/> | 1090 | </p> |
1085 | (Since version 3.7.7) if the key was full but the new data count of the key after <tt>set()</tt> is below its limit, <tt>set()</tt> returns <tt>true</tt> and the linda is also signaled for read so that send()-blocked threads are awakened. | 1091 | |
1092 | <p> | ||
1093 | Since version 3.8.0, <tt>set()</tt> can write several values at the specified key, writing <tt>nil</tt> values is now possible, and clearing the contents at the specified key is done by not providing any value. | ||
1086 | <br/> | 1094 | <br/> |
1087 | Table access and <tt>send()</tt>/<tt>receive()</tt> can be used together; reading a slot essentially peeks the next outcoming value of a queue. | 1095 | Also, <tt>get()</tt> can read several values at once. If the key contains no data, <tt>get()</tt> returns no value. This can be used to separate the case when reading stored <tt>nil</tt> values. |
1088 | </p> | 1096 | </p> |
1089 | 1097 | ||
1090 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1098 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
diff --git a/src/keeper.c b/src/keeper.c index 6d5ecbf..b0db8b4 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -115,11 +115,11 @@ static void fifo_push( lua_State* L, keeper_fifo* fifo, int _count) | |||
115 | // expects exactly 1 value on the stack! | 115 | // expects exactly 1 value on the stack! |
116 | // currently only called with a count of 1, but this may change in the future | 116 | // currently only called with a count of 1, but this may change in the future |
117 | // function assumes that there is enough data in the fifo to satisfy the request | 117 | // function assumes that there is enough data in the fifo to satisfy the request |
118 | static void fifo_peek( lua_State* L, keeper_fifo* fifo, int _count) | 118 | static void fifo_peek( lua_State* L, keeper_fifo* fifo, int count_) |
119 | { | 119 | { |
120 | int i; | 120 | int i; |
121 | STACK_GROW( L, _count); | 121 | STACK_GROW( L, count_); |
122 | for( i = 0; i < _count; ++ i) | 122 | for( i = 0; i < count_; ++ i) |
123 | { | 123 | { |
124 | lua_rawgeti( L, 1, fifo->first + i); | 124 | lua_rawgeti( L, 1, fifo->first + i); |
125 | } | 125 | } |
@@ -384,54 +384,21 @@ int keepercall_limit( lua_State* L) | |||
384 | return lua_gettop( L); | 384 | return lua_gettop( L); |
385 | } | 385 | } |
386 | 386 | ||
387 | //in: linda_ud key [val] | 387 | //in: linda_ud key [[val] ...] |
388 | //out: true or nil | 388 | //out: true or nil |
389 | int keepercall_set( lua_State* L) | 389 | int keepercall_set( lua_State* L) |
390 | { | 390 | { |
391 | bool_t should_wake_writers = FALSE; | 391 | bool_t should_wake_writers = FALSE; |
392 | STACK_GROW( L, 6); | 392 | STACK_GROW( L, 6); |
393 | // make sure we have a value on the stack | ||
394 | if( lua_gettop( L) == 2) // ud key val? | ||
395 | { | ||
396 | lua_pushnil( L); // ud key nil | ||
397 | } | ||
398 | 393 | ||
399 | // retrieve fifos associated with the linda | 394 | // retrieve fifos associated with the linda |
400 | push_table( L, 1); // ud key val fifos | 395 | push_table( L, 1); // ud key [val [, ...]] fifos |
401 | lua_replace( L, 1); // fifos key val | 396 | lua_replace( L, 1); // fifos key [val [, ...]] |
402 | 397 | ||
403 | if( !lua_isnil( L, 3)) // set/replace contents stored at the specified key? | 398 | // make sure we have a value on the stack |
399 | if( lua_gettop( L) == 2) // fifos key | ||
404 | { | 400 | { |
405 | keeper_fifo* fifo; | 401 | keeper_fifo* fifo; |
406 | lua_pushvalue( L, -2); // fifos key val key | ||
407 | lua_rawget( L, 1); // fifos key val fifo|nil | ||
408 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
409 | if( fifo == NULL) // can be NULL if we store a value at a new key | ||
410 | { // fifos key val nil | ||
411 | lua_pop( L, 1); // fifos key val | ||
412 | fifo_new( L); // fifos key val fifo | ||
413 | lua_pushvalue( L, 2); // fifos key val fifo key | ||
414 | lua_pushvalue( L, -2); // fifos key val fifo key fifo | ||
415 | lua_rawset( L, 1); // fifos key val fifo | ||
416 | } | ||
417 | else // the fifo exists, we just want to update its contents | ||
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); | ||
421 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | ||
422 | lua_newtable( L); // fifos key val fifo {} | ||
423 | lua_setuservalue( L, -2); // fifos key val fifo | ||
424 | fifo->first = 1; | ||
425 | fifo->count = 0; | ||
426 | } | ||
427 | fifo = prepare_fifo_access( L, -1); | ||
428 | lua_insert( L, -2); // fifos key fifo val | ||
429 | fifo_push( L, fifo, 1); // fifos key fifo | ||
430 | } | ||
431 | else // val == nil: we clear the key contents | ||
432 | { // fifos key nil | ||
433 | keeper_fifo* fifo; | ||
434 | lua_pop( L, 1); // fifos key | ||
435 | lua_pushvalue( L, -1); // fifos key key | 402 | lua_pushvalue( L, -1); // fifos key key |
436 | lua_rawget( L, 1); // fifos key fifo|nil | 403 | lua_rawget( L, 1); // fifos key fifo|nil |
437 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 404 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
@@ -456,13 +423,51 @@ int keepercall_set( lua_State* L) | |||
456 | } | 423 | } |
457 | } | 424 | } |
458 | } | 425 | } |
426 | else // set/replace contents stored at the specified key? | ||
427 | { | ||
428 | int count = lua_gettop( L) - 2; // number of items we want to store | ||
429 | keeper_fifo* fifo; // fifos key [val [, ...]] | ||
430 | lua_pushvalue( L, 2); // fifos key [val [, ...]] key | ||
431 | lua_rawget( L, 1); // fifos key [val [, ...]] fifo|nil | ||
432 | fifo = (keeper_fifo*) lua_touserdata( L, -1); | ||
433 | if( fifo == NULL) // can be NULL if we store a value at a new key | ||
434 | { // fifos key [val [, ...]] nil | ||
435 | // no need to wake writers in that case, because a writer can't wait on an inexistent key | ||
436 | lua_pop( L, 1); // fifos key [val [, ...]] | ||
437 | fifo_new( L); // fifos key [val [, ...]] fifo | ||
438 | lua_pushvalue( L, 2); // fifos key [val [, ...]] fifo key | ||
439 | lua_pushvalue( L, -2); // fifos key [val [, ...]] fifo key fifo | ||
440 | lua_rawset( L, 1); // fifos key [val [, ...]] fifo | ||
441 | } | ||
442 | else // the fifo exists, we just want to update its contents | ||
443 | { // fifos key [val [, ...]] fifo | ||
444 | // we create room if the fifo was full but it is no longer the case | ||
445 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); | ||
446 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | ||
447 | lua_newtable( L); // fifos key [val [, ...]] fifo {} | ||
448 | lua_setuservalue( L, -2); // fifos key [val [, ...]] fifo | ||
449 | fifo->first = 1; | ||
450 | fifo->count = 0; | ||
451 | } | ||
452 | fifo = prepare_fifo_access( L, -1); | ||
453 | // move the fifo below the values we want to store | ||
454 | lua_insert( L, 3); // fifos key fifo [val [, ...]] | ||
455 | fifo_push( L, fifo, count); // fifos key fifo | ||
456 | } | ||
459 | return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0; | 457 | return should_wake_writers ? (lua_pushboolean( L, 1), 1) : 0; |
460 | } | 458 | } |
461 | 459 | ||
462 | // in: linda_ud key | 460 | // in: linda_ud key [count] |
461 | // out: at most <count> values | ||
463 | int keepercall_get( lua_State* L) | 462 | int keepercall_get( lua_State* L) |
464 | { | 463 | { |
465 | keeper_fifo* fifo; | 464 | keeper_fifo* fifo; |
465 | int count = 1; | ||
466 | if( lua_gettop( L) == 3) // ud key count | ||
467 | { | ||
468 | count = lua_tointeger( L, 3); | ||
469 | lua_pop( L, 1); // ud key | ||
470 | } | ||
466 | push_table( L, 1); // ud key fifos | 471 | push_table( L, 1); // ud key fifos |
467 | lua_replace( L, 1); // fifos key | 472 | lua_replace( L, 1); // fifos key |
468 | lua_rawget( L, 1); // fifos fifo | 473 | lua_rawget( L, 1); // fifos fifo |
@@ -470,9 +475,10 @@ int keepercall_get( lua_State* L) | |||
470 | if( fifo != NULL && fifo->count > 0) | 475 | if( fifo != NULL && fifo->count > 0) |
471 | { | 476 | { |
472 | lua_remove( L, 1); // fifo | 477 | lua_remove( L, 1); // fifo |
473 | // read one value off the fifo | 478 | count = __min( count, fifo->count); |
474 | fifo_peek( L, fifo, 1); // fifo ... | 479 | // read <count> value off the fifo |
475 | return 1; | 480 | fifo_peek( L, fifo, count); // fifo ... |
481 | return count; | ||
476 | } | 482 | } |
477 | // no fifo was ever registered for this key, or it is empty | 483 | // no fifo was ever registered for this key, or it is empty |
478 | return 0; | 484 | return 0; |
@@ -682,16 +688,16 @@ void keeper_release( struct s_Keeper* K) | |||
682 | if( K) MUTEX_UNLOCK( &K->lock_); | 688 | if( K) MUTEX_UNLOCK( &K->lock_); |
683 | } | 689 | } |
684 | 690 | ||
685 | void keeper_toggle_nil_sentinels( lua_State* L, int _val_i, int _nil_to_sentinel) | 691 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mode_) |
686 | { | 692 | { |
687 | int i, n = lua_gettop( L); | 693 | int i, n = lua_gettop( L); |
688 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe | 694 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe |
689 | * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours) | 695 | * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours) |
690 | */ | 696 | */ |
691 | void* nil_sentinel = &GNbKeepers; | 697 | void* nil_sentinel = &GNbKeepers; |
692 | for( i = _val_i; i <= n; ++ i) | 698 | for( i = val_i_; i <= n; ++ i) |
693 | { | 699 | { |
694 | if( _nil_to_sentinel) | 700 | if( mode_ == eLM_ToKeeper) |
695 | { | 701 | { |
696 | if( lua_isnil( L, i)) | 702 | if( lua_isnil( L, i)) |
697 | { | 703 | { |
diff --git a/src/keeper.h b/src/keeper.h index 2ef443d..c0fd5d0 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -20,7 +20,7 @@ void close_keepers( void); | |||
20 | 20 | ||
21 | struct s_Keeper *keeper_acquire( const void *ptr); | 21 | struct s_Keeper *keeper_acquire( const void *ptr); |
22 | void keeper_release( struct s_Keeper *K); | 22 | void keeper_release( struct s_Keeper *K); |
23 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel); | 23 | void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, enum eLookupMode const mode_); |
24 | int keeper_push_linda_storage( lua_State* L, void* ptr); | 24 | int keeper_push_linda_storage( lua_State* L, void* ptr); |
25 | 25 | ||
26 | typedef lua_CFunction keeper_api_t; | 26 | typedef lua_CFunction keeper_api_t; |
diff --git a/src/lanes.c b/src/lanes.c index cad8fb1..f62c39f 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.8"; | 55 | char const* VERSION = "3.8.0"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -464,7 +464,7 @@ LUAG_FUNC( linda_send) | |||
464 | } | 464 | } |
465 | 465 | ||
466 | // convert nils to some special non-nil sentinel in sent values | 466 | // convert nils to some special non-nil sentinel in sent values |
467 | keeper_toggle_nil_sentinels( L, key_i + 1, 1); | 467 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); |
468 | 468 | ||
469 | STACK_GROW( L, 1); | 469 | STACK_GROW( L, 1); |
470 | { | 470 | { |
@@ -649,7 +649,7 @@ LUAG_FUNC( linda_receive) | |||
649 | { | 649 | { |
650 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); | 650 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); |
651 | // replace sentinels with real nils | 651 | // replace sentinels with real nils |
652 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0); | 652 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); |
653 | // To be done from within the 'K' locking area | 653 | // To be done from within the 'K' locking area |
654 | // | 654 | // |
655 | SIGNAL_ALL( &linda->read_happened); | 655 | SIGNAL_ALL( &linda->read_happened); |
@@ -725,28 +725,31 @@ LUAG_FUNC( linda_receive) | |||
725 | 725 | ||
726 | 726 | ||
727 | /* | 727 | /* |
728 | * [true] = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) | 728 | * [true] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) |
729 | * | 729 | * |
730 | * Set a value to Linda. | 730 | * Set one or more value to Linda. |
731 | * TODO: what do we do if we set to non-nil and limit is 0? | 731 | * TODO: what do we do if we set to non-nil and limit is 0? |
732 | * | 732 | * |
733 | * Existing slot value is replaced, and possible queue entries removed. | 733 | * Existing slot value is replaced, and possible queued entries removed. |
734 | */ | 734 | */ |
735 | LUAG_FUNC( linda_set) | 735 | LUAG_FUNC( linda_set) |
736 | { | 736 | { |
737 | struct s_Linda *linda = lua_toLinda( L, 1); | 737 | struct s_Linda* const linda = lua_toLinda( L, 1); |
738 | int pushed; | 738 | int pushed; |
739 | bool_t has_value = !lua_isnil( L, 3); | 739 | bool_t has_value = lua_gettop( L) > 2; |
740 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 740 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
741 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
742 | 741 | ||
743 | // make sure the key is of a valid type | 742 | // make sure the key is of a valid type (throws an error if not the case) |
744 | check_key_types( L, 2, 2); | 743 | check_key_types( L, 2, 2); |
745 | 744 | ||
746 | { | 745 | { |
747 | struct s_Keeper* K = keeper_acquire( linda); | 746 | struct s_Keeper* K = keeper_acquire( linda); |
748 | if( K == NULL) return 0; | 747 | if( K == NULL) return 0; |
749 | // no nil->sentinel toggling, we really clear the linda contents for the given key with a set() | 748 | if( has_value) |
749 | { | ||
750 | // convert nils to some special non-nil sentinel in sent values | ||
751 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | ||
752 | } | ||
750 | pushed = keeper_call( K->L, KEEPER_API( set), L, linda, 2); | 753 | pushed = keeper_call( K->L, KEEPER_API( set), L, linda, 2); |
751 | if( pushed >= 0) // no error? | 754 | if( pushed >= 0) // no error? |
752 | { | 755 | { |
@@ -801,31 +804,32 @@ LUAG_FUNC( linda_count) | |||
801 | 804 | ||
802 | 805 | ||
803 | /* | 806 | /* |
804 | * [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) | 807 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) |
805 | * | 808 | * |
806 | * Get a value from Linda. | 809 | * Get one or more values from Linda. |
807 | * TODO: add support to get multiple values? | ||
808 | */ | 810 | */ |
809 | LUAG_FUNC( linda_get) | 811 | LUAG_FUNC( linda_get) |
810 | { | 812 | { |
811 | struct s_Linda *linda= lua_toLinda( L, 1); | 813 | struct s_Linda* const linda = lua_toLinda( L, 1); |
812 | int pushed; | 814 | int pushed; |
815 | int count = luaL_optint( L, 3, 1); | ||
816 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | ||
817 | luaL_argcheck( L, linda, 1, "expected a linda object"); | ||
818 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
813 | 819 | ||
814 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 820 | // make sure the key is of a valid type (throws an error if not the case) |
815 | // make sure the key is of a valid type | ||
816 | check_key_types( L, 2, 2); | 821 | check_key_types( L, 2, 2); |
817 | |||
818 | { | 822 | { |
819 | struct s_Keeper* K = keeper_acquire( linda); | 823 | struct s_Keeper* K = keeper_acquire( linda); |
820 | if( K == NULL) return 0; | 824 | if( K == NULL) return 0; |
821 | pushed = keeper_call( K->L, KEEPER_API( get), L, linda, 2); | 825 | pushed = keeper_call( K->L, KEEPER_API( get), L, linda, 2); |
822 | ASSERT_L( pushed == 0 || pushed == 1); | ||
823 | if( pushed > 0) | 826 | if( pushed > 0) |
824 | { | 827 | { |
825 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0); | 828 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); |
826 | } | 829 | } |
827 | keeper_release( K); | 830 | keeper_release( K); |
828 | // must trigger error after keeper state has been released | 831 | // must trigger error after keeper state has been released |
832 | // (an error can be raised if we attempt to read an unregistered function) | ||
829 | if( pushed < 0) | 833 | if( pushed < 0) |
830 | { | 834 | { |
831 | return luaL_error( L, "tried to copy unsupported types"); | 835 | return luaL_error( L, "tried to copy unsupported types"); |
diff --git a/src/lanes.lua b/src/lanes.lua index e7b9715..9a0287d 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -596,8 +596,8 @@ end -- settings.with_timers | |||
596 | -- | 596 | -- |
597 | -- PUBLIC LANES API | 597 | -- PUBLIC LANES API |
598 | local genlock = function( linda, key, N) | 598 | local genlock = function( linda, key, N) |
599 | linda:set( key) -- clears existing data | ||
599 | linda:limit( key, N) | 600 | linda:limit( key, N) |
600 | linda:set( key, nil) -- clears existing data | ||
601 | 601 | ||
602 | -- | 602 | -- |
603 | -- [true [, ...]= trues(uint) | 603 | -- [true [, ...]= trues(uint) |