aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--docs/index.html30
-rw-r--r--src/keeper.c96
-rw-r--r--src/lanes.c55
4 files changed, 123 insertions, 65 deletions
diff --git a/CHANGES b/CHANGES
index 7fac3e3..0a6889d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,12 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 89: BGe 09-Jan-14
4 * version 3.7.7
5 * fix crash when calling linda:count() on unknown keys
6 * purge key storage with linda:set( key, nil) on an unlimited key to reduce memory usage with lots of keys
7 * linda:limit() wakes write-blocked threads if necessary when the new limit enables writes to occur again
8 * linda:set() wakes write-blocked threads if necessary if the operation created some room to write into
9
3CHANGE 88: BGe 06-Jan-14 10CHANGE 88: BGe 06-Jan-14
4 * version 3.7.6 11 * version 3.7.6
5 * if config.on_state_create() is a C function, call it by direct C closure reconstruction in newly created states 12 * if config.on_state_create() is a C function, call it by direct C closure reconstruction in newly created states
diff --git a/docs/index.html b/docs/index.html
index 931a961..c62f64f 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 06-Jan-14, and applies to version <tt>3.7.6</tt>. 73 This document was revised on 09-Jan-14, and applies to version <tt>3.7.7</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -1010,13 +1010,13 @@
1010<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 1010<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
1011 h = lanes.linda( [opt_name]) 1011 h = lanes.linda( [opt_name])
1012 1012
1013 bool = h:send( [timeout_secs,] key, ... ) 1013 bool = h:send( [timeout_secs,] key, ...)
1014 1014
1015 [key, val] = h:receive( [timeout_secs,] key [, ...]) 1015 [key, val] = h:receive( [timeout_secs,] key [, ...])
1016 1016
1017 [key, val [, ...]] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max]) 1017 [key, val [, ...]] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max])
1018 1018
1019 void = h:limit( key, n_uint) 1019 [true] = h:limit( key, n_uint)
1020</pre></td></tr></table> 1020</pre></td></tr></table>
1021 1021
1022<p> 1022<p>
@@ -1024,7 +1024,11 @@
1024</p> 1024</p>
1025 1025
1026<p> 1026<p>
1027 By default, stack sizes are unlimited but limits can be enforced using the <tt>limit</tt> method. This can be useful to balance execution speeds in a producer/consumer scenario. 1027 By default, stack sizes are unlimited but limits can be enforced using the <tt>limit</tt> method. This can be useful to balance execution speeds in a producer/consumer scenario. Any negative value removes the limit.
1028 <br/>
1029 A limit of 0 is allowed to block everything.
1030 <br/>
1031 (Since version 3.7.7) if the key was full but the limit change added some room, <tt>limit()</tt> returns <tt>true</tt> and the linda is signalled so that <tt>send()</tt>-blocked threads are awakened.
1028</p> 1032</p>
1029 1033
1030<p> 1034<p>
@@ -1036,13 +1040,13 @@
1036</p> 1040</p>
1037 1041
1038<p> 1042<p>
1039 <tt>send</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout. 1043 <tt>send()</tt> returns <tt>true</tt> if the sending succeeded, and <tt>false</tt> if the queue limit was met, and the queue did not empty enough during the given timeout.
1040</p> 1044</p>
1041 1045
1042<p> 1046<p>
1043 Equally, <tt>receive</tt> returns a key and the value extracted from it, or nothing for timeout. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout. 1047 Equally, <tt>receive()</tt> returns a key and the value extracted from it, or nothing for timeout. Note that <tt>nil</tt>s can be sent and received; the <tt>key</tt> value will tell it apart from a timeout.
1044 <br> 1048 <br>
1045 Version 3.4.0 introduces an API change in the returned values: <tt>receive</tt> returns the key followed by the value(s), in that order, and not the other way around. 1049 Version 3.4.0 introduces an API change in the returned values: <tt>receive()</tt> returns the key followed by the value(s), in that order, and not the other way around.
1046</p> 1050</p>
1047 1051
1048<p> 1052<p>
@@ -1055,9 +1059,9 @@
1055</p> 1059</p>
1056 1060
1057<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 1061<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
1058 linda_h:set( key, [val]) 1062 [true] = linda_h:set( key, [val])
1059 1063
1060 [val] = linda_h:get( key ) 1064 [val] = linda_h:get( key)
1061</pre></td></tr></table> 1065</pre></td></tr></table>
1062 1066
1063<p> 1067<p>
@@ -1065,7 +1069,13 @@
1065</p> 1069</p>
1066 1070
1067<p> 1071<p>
1068 Writing to a slot overwrites existing value, and clears any possible queued entries. 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. 1072 Writing to a slot never blocks because it ignores the limit. It overwrites existing value and clears any possible queued entries.
1073 <br/>
1074 <tt>set()</tt> signals the linda from write if a value is stored.
1075 <br/>
1076 (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.
1077 <br/>
1078 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.
1069</p> 1079</p>
1070 1080
1071<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 1081<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
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
99static void fifo_push( lua_State* L, keeper_fifo* fifo, int _count) 99static 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
353int keepercall_limit( lua_State* L) 353int 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
375int keepercall_set( lua_State* L) 389int 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*/
684int keeper_call( lua_State *K, keeper_api_t _func, lua_State *L, void *linda, uint_t starting_index) 722int 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
55char const* VERSION = "3.7.6"; 55char 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)
682LUAG_FUNC( linda_set) 682LUAG_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*/
790LUAG_FUNC( linda_limit) 792LUAG_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