diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | docs/index.html | 8 | ||||
-rw-r--r-- | src/keeper.c | 8 | ||||
-rw-r--r-- | src/keeper.h | 2 | ||||
-rw-r--r-- | src/lanes.c | 32 |
5 files changed, 42 insertions, 14 deletions
@@ -1,5 +1,11 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 105: BGe 27-Feb-14 | ||
4 | * Bumped version to 3.9.3 | ||
5 | * new exposed variable linda.null that exposes the internal NIL_SENTINEL marker | ||
6 | * linda:send() interprets send key linda.null as authorization to silently send a single nil when not provided with anything to send | ||
7 | (useful when sending results of a function that can return nothing) | ||
8 | |||
3 | CHANGE 104: BGe 25-Feb-14 | 9 | CHANGE 104: BGe 25-Feb-14 |
4 | * Bumped version to 3.9.2 | 10 | * Bumped version to 3.9.2 |
5 | * Internal rework: the whole Lanes engine now works "per universe" to allow concurrent Lanes execution in more than one embedded master state | 11 | * Internal rework: the whole Lanes engine now works "per universe" to allow concurrent Lanes execution in more than one embedded master state |
diff --git a/docs/index.html b/docs/index.html index 6337647..c4a6441 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 26-Feb-14, and applies to version <tt>3.9.2</tt>. | 73 | This document was revised on 27-Feb-14, and applies to version <tt>3.9.3</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -1038,7 +1038,7 @@ | |||
1038 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1038 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1039 | h = lanes.linda( [opt_name, [opt_group]]) | 1039 | h = lanes.linda( [opt_name, [opt_group]]) |
1040 | 1040 | ||
1041 | [true|lanes.cancel_error] = h:send( [timeout_secs,] key, ...) | 1041 | [true|lanes.cancel_error] = h:send( [timeout_secs,] [h.null,] key, ...) |
1042 | 1042 | ||
1043 | [key, val]|[lanes.cancel_error] = h:receive( [timeout_secs,] key [, ...]) | 1043 | [key, val]|[lanes.cancel_error] = h:receive( [timeout_secs,] key [, ...]) |
1044 | 1044 | ||
@@ -1071,6 +1071,10 @@ | |||
1071 | <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. | 1071 | <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. |
1072 | <br/> | 1072 | <br/> |
1073 | (Since version 3.7.8) <tt>send()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request. | 1073 | (Since version 3.7.8) <tt>send()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request. |
1074 | <br/> | ||
1075 | If no data is provided after the key, <tt>send()</tt> raises an error. Since version 3.9.3, if provided with <tt>linda.null</tt> before the actual key and there is no data to send, <tt>send()</tt> sends a single <tt>nil</tt>. | ||
1076 | <br/> | ||
1077 | Also, if <tt>linda.null</tt> is sent as data in a linda, it will be read as a <tt>nil</tt>. | ||
1074 | </p> | 1078 | </p> |
1075 | 1079 | ||
1076 | <p> | 1080 | <p> |
diff --git a/src/keeper.c b/src/keeper.c index 9e5317b..7362ca6 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -756,23 +756,19 @@ void keeper_release( struct s_Keeper* K) | |||
756 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mode_) | 756 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mode_) |
757 | { | 757 | { |
758 | int i, n = lua_gettop( L); | 758 | int i, n = lua_gettop( L); |
759 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe | ||
760 | * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global symbol of ours) | ||
761 | */ | ||
762 | void* nil_sentinel = (void*) keeper_toggle_nil_sentinels; | ||
763 | for( i = val_i_; i <= n; ++ i) | 759 | for( i = val_i_; i <= n; ++ i) |
764 | { | 760 | { |
765 | if( mode_ == eLM_ToKeeper) | 761 | if( mode_ == eLM_ToKeeper) |
766 | { | 762 | { |
767 | if( lua_isnil( L, i)) | 763 | if( lua_isnil( L, i)) |
768 | { | 764 | { |
769 | lua_pushlightuserdata( L, nil_sentinel); | 765 | lua_pushlightuserdata( L, NIL_SENTINEL); |
770 | lua_replace( L, i); | 766 | lua_replace( L, i); |
771 | } | 767 | } |
772 | } | 768 | } |
773 | else | 769 | else |
774 | { | 770 | { |
775 | if( lua_touserdata( L, i) == nil_sentinel) | 771 | if( lua_touserdata( L, i) == NIL_SENTINEL) |
776 | { | 772 | { |
777 | lua_pushnil( L); | 773 | lua_pushnil( L); |
778 | lua_replace( L, i); | 774 | lua_replace( L, i); |
diff --git a/src/keeper.h b/src/keeper.h index 450f64d..6ca4ad4 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -23,6 +23,8 @@ void keeper_release( struct s_Keeper* K); | |||
23 | void keeper_toggle_nil_sentinels( lua_State* L, int _val_i, enum eLookupMode const mode_); | 23 | void keeper_toggle_nil_sentinels( lua_State* L, int _val_i, enum eLookupMode const mode_); |
24 | int keeper_push_linda_storage( struct s_Universe* U, lua_State* L, void* ptr, unsigned long magic_); | 24 | int keeper_push_linda_storage( struct s_Universe* U, lua_State* L, void* ptr, unsigned long magic_); |
25 | 25 | ||
26 | #define NIL_SENTINEL ((void*)keeper_toggle_nil_sentinels) | ||
27 | |||
26 | typedef lua_CFunction keeper_api_t; | 28 | typedef lua_CFunction keeper_api_t; |
27 | #define KEEPER_API( _op) keepercall_ ## _op | 29 | #define KEEPER_API( _op) keepercall_ ## _op |
28 | #define PUSH_KEEPER_FUNC lua_pushcfunction | 30 | #define PUSH_KEEPER_FUNC lua_pushcfunction |
diff --git a/src/lanes.c b/src/lanes.c index bca8b74..4130b24 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.9.2"; | 55 | char const* VERSION = "3.9.3"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -442,7 +442,7 @@ static void check_key_types( lua_State* L, int start_, int end_) | |||
442 | } | 442 | } |
443 | 443 | ||
444 | /* | 444 | /* |
445 | * bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) | 445 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) |
446 | * | 446 | * |
447 | * Send one or more values to a Linda. If there is a limit, all values must fit. | 447 | * Send one or more values to a Linda. If there is a limit, all values must fit. |
448 | * | 448 | * |
@@ -458,10 +458,11 @@ LUAG_FUNC( linda_send) | |||
458 | int pushed; | 458 | int pushed; |
459 | time_d timeout = -1.0; | 459 | time_d timeout = -1.0; |
460 | uint_t key_i = 2; // index of first key, if timeout not there | 460 | uint_t key_i = 2; // index of first key, if timeout not there |
461 | void* as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided | ||
461 | 462 | ||
462 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 463 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion |
463 | { | 464 | { |
464 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L,2)); | 465 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); |
465 | ++ key_i; | 466 | ++ key_i; |
466 | } | 467 | } |
467 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | 468 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key |
@@ -469,19 +470,35 @@ LUAG_FUNC( linda_send) | |||
469 | ++ key_i; | 470 | ++ key_i; |
470 | } | 471 | } |
471 | 472 | ||
472 | // make sure the keys are of a valid type | 473 | as_nil_sentinel = lua_touserdata( L, key_i); |
474 | if( as_nil_sentinel == NIL_SENTINEL) | ||
475 | { | ||
476 | // the real key to send data to is after the NIL_SENTINEL marker | ||
477 | ++ key_i; | ||
478 | } | ||
479 | |||
480 | // make sure the key is of a valid type | ||
473 | check_key_types( L, key_i, key_i); | 481 | check_key_types( L, key_i, key_i); |
474 | 482 | ||
483 | STACK_GROW( L, 1); | ||
484 | |||
475 | // make sure there is something to send | 485 | // make sure there is something to send |
476 | if( (uint_t)lua_gettop( L) == key_i) | 486 | if( (uint_t)lua_gettop( L) == key_i) |
477 | { | 487 | { |
478 | return luaL_error( L, "no data to send"); | 488 | if( as_nil_sentinel == NIL_SENTINEL) |
489 | { | ||
490 | // send a single nil if nothing is provided | ||
491 | lua_pushlightuserdata( L, NIL_SENTINEL); | ||
492 | } | ||
493 | else | ||
494 | { | ||
495 | return luaL_error( L, "no data to send"); | ||
496 | } | ||
479 | } | 497 | } |
480 | 498 | ||
481 | // convert nils to some special non-nil sentinel in sent values | 499 | // convert nils to some special non-nil sentinel in sent values |
482 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); | 500 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); |
483 | 501 | ||
484 | STACK_GROW( L, 1); | ||
485 | { | 502 | { |
486 | bool_t try_again = TRUE; | 503 | bool_t try_again = TRUE; |
487 | struct s_lane* const s = get_lane_from_registry( L); | 504 | struct s_lane* const s = get_lane_from_registry( L); |
@@ -1210,6 +1227,9 @@ static void* linda_id( lua_State* L, enum eDeepOp op_) | |||
1210 | lua_pushliteral( L, BATCH_SENTINEL); | 1227 | lua_pushliteral( L, BATCH_SENTINEL); |
1211 | lua_setfield(L, -2, "batched"); | 1228 | lua_setfield(L, -2, "batched"); |
1212 | 1229 | ||
1230 | lua_pushlightuserdata( L, NIL_SENTINEL); | ||
1231 | lua_setfield(L, -2, "null"); | ||
1232 | |||
1213 | STACK_END( L, 1); | 1233 | STACK_END( L, 1); |
1214 | return NULL; | 1234 | return NULL; |
1215 | } | 1235 | } |