diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rw-r--r-- | docs/index.html | 48 | ||||
-rw-r--r-- | src/lanes.c | 405 | ||||
-rw-r--r-- | tests/cancel.lua | 19 |
4 files changed, 302 insertions, 176 deletions
@@ -1,5 +1,11 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 97: BGe 10-Feb-14 | ||
4 | * version 3.8.4 | ||
5 | * new API linda:cancel("read"|"write"|"both"|"none") | ||
6 | * all linda operations return lanes.cancel_error on a cancelled linda | ||
7 | * raised an internal string length so that longer linda names are fully output before truncation applies when doing tostring( linda) | ||
8 | |||
3 | CHANGE 96: BGe 24-Jan-14 | 9 | CHANGE 96: BGe 24-Jan-14 |
4 | * another Lua stack overflow fix when sending complex function through lindas or as lane body | 10 | * another Lua stack overflow fix when sending complex function through lindas or as lane body |
5 | 11 | ||
diff --git a/docs/index.html b/docs/index.html index f639f18..1ad25a6 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 22-Jan-14, and applies to version <tt>3.8.3</tt>. | 73 | This document was revised on 10-Feb-14, and applies to version <tt>3.8.4</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -902,10 +902,12 @@ | |||
902 | <p> | 902 | <p> |
903 | <tt>cancel()</tt> sends a cancellation request to the lane.<br/> | 903 | <tt>cancel()</tt> sends a cancellation request to the lane.<br/> |
904 | First argument is a timeout, that defaults to 0 if not specified. (Starting with version 3.6.3) signification of the following arguments differ depending on whether the timeout is negative or not. | 904 | First argument is a timeout, that defaults to 0 if not specified. (Starting with version 3.6.3) signification of the following arguments differ depending on whether the timeout is negative or not. |
905 | <br/> | 905 | </p> |
906 | <p> | ||
906 | If <tt>timeout_secs</tt> is negative (aka "soft cancel"), cancellation will only cause <tt>cancel_test()</tt> to return <tt>true</tt>, so that the lane can cleanup manually (the actual value is irrelevant). | 907 | If <tt>timeout_secs</tt> is negative (aka "soft cancel"), cancellation will only cause <tt>cancel_test()</tt> to return <tt>true</tt>, so that the lane can cleanup manually (the actual value is irrelevant). |
907 | If <tt>wake_bool</tt> is <tt>true</tt>, the lane is also signalled so that execution returns from any pending linda operation. | 908 | If <tt>wake_bool</tt> is <tt>true</tt>, the lane is also signalled so that execution returns from any pending linda operation. Linda operations detecting the cancellation request return <tt>lanes.cancel_error</tt>. |
908 | <br/> | 909 | </p> |
910 | <p> | ||
909 | If <tt>timeout_secs</tt> is positive (aka "hard cancel"), waits for the request to be processed, or a timeout to occur. Linda operations detecting the cancellation request will raise a special cancellation error (meaning they won't return in that case). | 911 | If <tt>timeout_secs</tt> is positive (aka "hard cancel"), waits for the request to be processed, or a timeout to occur. Linda operations detecting the cancellation request will raise a special cancellation error (meaning they won't return in that case). |
910 | If <tt>force_kill_bool</tt> is <tt>true</tt>, <tt>forcekill_timeout</tt> can be set to tell how long lanes will wait for the OS thread to terminate before raising an error. Windows threads always terminate immediately, but it might not always be the case with some pthread implementations. | 912 | If <tt>force_kill_bool</tt> is <tt>true</tt>, <tt>forcekill_timeout</tt> can be set to tell how long lanes will wait for the OS thread to terminate before raising an error. Windows threads always terminate immediately, but it might not always be the case with some pthread implementations. |
911 | Returns <tt>true</tt> if soft cancelling, or the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status), or the cancellation was fruitful within <tt>timeout_secs</tt> timeout period. | 913 | Returns <tt>true</tt> if soft cancelling, or the lane was already done (in <tt>"done"</tt>, <tt>"error"</tt> or <tt>"cancelled"</tt> status), or the cancellation was fruitful within <tt>timeout_secs</tt> timeout period. |
@@ -1007,7 +1009,7 @@ | |||
1007 | print( "timed out") | 1009 | print( "timed out") |
1008 | break | 1010 | break |
1009 | end | 1011 | end |
1010 | print( "received: " .. val) | 1012 | print( tostring( linda) .. " received: " .. val) |
1011 | end | 1013 | end |
1012 | </pre></td></tr></table> | 1014 | </pre></td></tr></table> |
1013 | 1015 | ||
@@ -1024,27 +1026,28 @@ | |||
1024 | <li><tt>receive</tt> can wait for multiple keys at once.</li> | 1026 | <li><tt>receive</tt> can wait for multiple keys at once.</li> |
1025 | <li><tt>receive</tt> has a batched mode to consume more than one value from a single key, as in <tt>linda:receive( 1.0, linda.batched, "key", 3, 6).</tt></li> | 1027 | <li><tt>receive</tt> has a batched mode to consume more than one value from a single key, as in <tt>linda:receive( 1.0, linda.batched, "key", 3, 6).</tt></li> |
1026 | <li>individual keys' queue length can be limited, balancing speed differences in a producer/consumer scenario (making <tt>:send</tt> wait).</li> | 1028 | <li>individual keys' queue length can be limited, balancing speed differences in a producer/consumer scenario (making <tt>:send</tt> wait).</li> |
1029 | <li><tt>tostring( linda)</tt> returns a string of the form <tt>"Linda: <opt_name>"</tt></li> | ||
1027 | </ul> | 1030 | </ul> |
1028 | </p> | 1031 | </p> |
1029 | 1032 | ||
1030 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1033 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1031 | h = lanes.linda( [opt_name]) | 1034 | h = lanes.linda( [opt_name]) |
1032 | 1035 | ||
1033 | bool|cancel_error = h:send( [timeout_secs,] key, ...) | 1036 | [true|lanes.cancel_error] = h:send( [timeout_secs,] key, ...) |
1034 | 1037 | ||
1035 | [key, val]|[cancel_error] = h:receive( [timeout_secs,] key [, ...]) | 1038 | [key, val]|[lanes.cancel_error] = h:receive( [timeout_secs,] key [, ...]) |
1036 | 1039 | ||
1037 | [key, val [, ...]]|[cancel_error] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max]) | 1040 | [key, val [, ...]]|[lanes.cancel_error] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max]) |
1038 | 1041 | ||
1039 | [true] = h:limit( key, n_uint) | 1042 | [true] = h:limit( key, n_uint) |
1040 | </pre></td></tr></table> | 1043 | </pre></td></tr></table> |
1041 | 1044 | ||
1042 | <p> | 1045 | <p> |
1043 | The <tt>send</tt> and <tt>receive</tt> methods use Linda keys as FIFO stacks (first in, first out). Timeouts are given in seconds (millisecond accuracy). If using numbers as the first Linda key, one must explicitly give <tt>nil</tt> as the timeout parameter to avoid ambiguities. | 1046 | The <tt>send()</tt> and <tt>receive()</tt> methods use Linda keys as FIFO stacks (first in, first out). Timeouts are given in seconds (millisecond accuracy). If using numbers as the first Linda key, one must explicitly give <tt>nil</tt> as the timeout parameter to avoid ambiguities. |
1044 | </p> | 1047 | </p> |
1045 | 1048 | ||
1046 | <p> | 1049 | <p> |
1047 | 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. | 1050 | 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. |
1048 | <br/> | 1051 | <br/> |
1049 | A limit of 0 is allowed to block everything. | 1052 | A limit of 0 is allowed to block everything. |
1050 | <br/> | 1053 | <br/> |
@@ -1083,9 +1086,9 @@ | |||
1083 | </p> | 1086 | </p> |
1084 | 1087 | ||
1085 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1088 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1086 | [bool] = linda_h:set( key [, val [, ...]]) | 1089 | bool|lanes.cancel_error = linda_h:set( key [, val [, ...]]) |
1087 | 1090 | ||
1088 | [val [, ...]] = linda_h:get( key [, count = 1]) | 1091 | [[val [, ...]]|lanes.cancel_error] = linda_h:get( key [, count = 1]) |
1089 | </pre></td></tr></table> | 1092 | </pre></td></tr></table> |
1090 | 1093 | ||
1091 | <p> | 1094 | <p> |
@@ -1099,9 +1102,9 @@ | |||
1099 | </p> | 1102 | </p> |
1100 | 1103 | ||
1101 | <p> | 1104 | <p> |
1102 | <tt>set()</tt> signals the linda for write if a value is stored. | 1105 | <tt>set()</tt> signals the linda for write if a value is stored. If nothing special happens, <tt>set() </tt>returns nothing. |
1103 | <br/> | 1106 | <br/> |
1104 | (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. | 1107 | 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 <tt>send()</tt>-blocked threads are awakened. |
1105 | </p> | 1108 | </p> |
1106 | 1109 | ||
1107 | <p> | 1110 | <p> |
@@ -1110,6 +1113,10 @@ | |||
1110 | 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. | 1113 | 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. |
1111 | </p> | 1114 | </p> |
1112 | 1115 | ||
1116 | <p> | ||
1117 | Since version 3.8.4, trying to send or receive data through a cancelled linda does nothing and returns <tt>lanes.cancel_error</tt>. | ||
1118 | </p> | ||
1119 | |||
1113 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1120 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1114 | [val] = linda_h:count( [key[,...]]) | 1121 | [val] = linda_h:count( [key[,...]]) |
1115 | </pre></td></tr></table> | 1122 | </pre></td></tr></table> |
@@ -1134,6 +1141,19 @@ | |||
1134 | Returns a table describing the full contents of a linda, or <tt>nil</tt> if the linda wasn't used yet. | 1141 | Returns a table describing the full contents of a linda, or <tt>nil</tt> if the linda wasn't used yet. |
1135 | </p> | 1142 | </p> |
1136 | 1143 | ||
1144 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | ||
1145 | void = linda_h:cancel("read"|"write"|"both"|"none") | ||
1146 | </pre></td></tr></table> | ||
1147 | |||
1148 | <p> | ||
1149 | (Starting with version 3.8.4) Signals the linda so that lanes waiting for read, write, or both, wake up. | ||
1150 | All linda operations (including <tt>get()</tt> and <tt>set()</tt>) will return <tt>lanes.cancel_error</tt> as when the calling lane is <a href="#cancelling">soft-cancelled</a> as long as the linda is marked as cancelled. | ||
1151 | <br/> | ||
1152 | <tt>"none"</tt> reset the linda's cancel status, but doesn't signal it. | ||
1153 | <br/> | ||
1154 | If not void, the lane's cancel status overrides the linda's cancel status. | ||
1155 | </p> | ||
1156 | |||
1137 | <h3>Granularity of using Lindas</h3> | 1157 | <h3>Granularity of using Lindas</h3> |
1138 | 1158 | ||
1139 | <p> | 1159 | <p> |
diff --git a/src/lanes.c b/src/lanes.c index 64c144c..cf88171 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.8.3"; | 55 | char const* VERSION = "3.8.4"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -147,7 +147,7 @@ struct s_lane | |||
147 | // M: sets to PENDING (before launching) | 147 | // M: sets to PENDING (before launching) |
148 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED | 148 | // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED |
149 | 149 | ||
150 | SIGNAL_T * volatile waiting_on; | 150 | SIGNAL_T* volatile waiting_on; |
151 | // | 151 | // |
152 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL | 152 | // When status is WAITING, points on the linda's signal the thread waits on, else NULL |
153 | 153 | ||
@@ -414,10 +414,12 @@ static void lane_cleanup( struct s_lane* s) | |||
414 | * Actual data is kept within a keeper state, which is hashed by the 's_Linda' | 414 | * Actual data is kept within a keeper state, which is hashed by the 's_Linda' |
415 | * pointer (which is same to all userdatas pointing to it). | 415 | * pointer (which is same to all userdatas pointing to it). |
416 | */ | 416 | */ |
417 | struct s_Linda { | 417 | struct s_Linda |
418 | SIGNAL_T read_happened; | 418 | { |
419 | SIGNAL_T write_happened; | 419 | SIGNAL_T read_happened; |
420 | char name[1]; | 420 | SIGNAL_T write_happened; |
421 | enum e_cancel_request simulate_cancel; | ||
422 | char name[1]; | ||
421 | }; | 423 | }; |
422 | 424 | ||
423 | static void linda_id( lua_State*, char const * const which); | 425 | static void linda_id( lua_State*, char const * const which); |
@@ -450,11 +452,11 @@ static void check_key_types( lua_State*L, int _start, int _end) | |||
450 | */ | 452 | */ |
451 | LUAG_FUNC( linda_send) | 453 | LUAG_FUNC( linda_send) |
452 | { | 454 | { |
453 | struct s_Linda *linda = lua_toLinda( L, 1); | 455 | struct s_Linda* linda = lua_toLinda( L, 1); |
454 | bool_t ret; | 456 | bool_t ret; |
455 | enum e_cancel_request cancel = CANCEL_NONE; | 457 | enum e_cancel_request cancel = CANCEL_NONE; |
456 | int pushed; | 458 | int pushed; |
457 | time_d timeout= -1.0; | 459 | time_d timeout = -1.0; |
458 | 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 |
459 | 461 | ||
460 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 462 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
@@ -507,7 +509,6 @@ LUAG_FUNC( linda_send) | |||
507 | // | 509 | // |
508 | SIGNAL_ALL( &linda->write_happened); | 510 | SIGNAL_ALL( &linda->write_happened); |
509 | break; | 511 | break; |
510 | |||
511 | } | 512 | } |
512 | if( timeout == 0.0) | 513 | if( timeout == 0.0) |
513 | { | 514 | { |
@@ -520,11 +521,15 @@ LUAG_FUNC( linda_send) | |||
520 | struct s_lane* const s = get_lane_from_registry( L); | 521 | struct s_lane* const s = get_lane_from_registry( L); |
521 | if( s != NULL) | 522 | if( s != NULL) |
522 | { | 523 | { |
523 | cancel = s->cancel_request; // testing here causes no delays | 524 | cancel = s->cancel_request; |
524 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything | 525 | } |
525 | { | 526 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; |
526 | break; | 527 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything |
527 | } | 528 | { |
529 | break; | ||
530 | } | ||
531 | if( s != NULL) | ||
532 | { | ||
528 | // change status of lane to "waiting" | 533 | // change status of lane to "waiting" |
529 | prev_status = s->status; // RUNNING, most likely | 534 | prev_status = s->status; // RUNNING, most likely |
530 | ASSERT_L( prev_status == RUNNING); // but check, just in case | 535 | ASSERT_L( prev_status == RUNNING); // but check, just in case |
@@ -532,22 +537,22 @@ LUAG_FUNC( linda_send) | |||
532 | ASSERT_L( s->waiting_on == NULL); | 537 | ASSERT_L( s->waiting_on == NULL); |
533 | s->waiting_on = &linda->read_happened; | 538 | s->waiting_on = &linda->read_happened; |
534 | } | 539 | } |
535 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
536 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) | ||
537 | { | 540 | { |
541 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
542 | bool_t const signalled = SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout); | ||
538 | if( s != NULL) | 543 | if( s != NULL) |
539 | { | 544 | { |
540 | s->waiting_on = NULL; | 545 | s->waiting_on = NULL; |
541 | s->status = prev_status; | 546 | s->status = prev_status; |
542 | // if woken by a cancel request, be sure to handle it properly | 547 | // if a cancel request is pending, be sure to handle it as soon as possible |
543 | cancel = s->cancel_request; | 548 | cancel = s->cancel_request; |
544 | } | 549 | } |
545 | break; | 550 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; |
546 | } | 551 | if( !signalled || cancel != CANCEL_NONE) |
547 | if( s != NULL) | 552 | { |
548 | { | 553 | // waiting returned after a timeout, or pending cancel: we are done |
549 | s->waiting_on = NULL; | 554 | break; |
550 | s->status = prev_status; | 555 | } |
551 | } | 556 | } |
552 | } | 557 | } |
553 | } | 558 | } |
@@ -593,7 +598,7 @@ LUAG_FUNC( linda_send) | |||
593 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | 598 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" |
594 | LUAG_FUNC( linda_receive) | 599 | LUAG_FUNC( linda_receive) |
595 | { | 600 | { |
596 | struct s_Linda *linda = lua_toLinda( L, 1); | 601 | struct s_Linda* linda = lua_toLinda( L, 1); |
597 | int pushed, expected_pushed_min, expected_pushed_max; | 602 | int pushed, expected_pushed_min, expected_pushed_max; |
598 | enum e_cancel_request cancel = CANCEL_NONE; | 603 | enum e_cancel_request cancel = CANCEL_NONE; |
599 | keeper_api_t keeper_receive; | 604 | keeper_api_t keeper_receive; |
@@ -650,7 +655,7 @@ LUAG_FUNC( linda_receive) | |||
650 | } | 655 | } |
651 | 656 | ||
652 | { | 657 | { |
653 | struct s_Keeper *K = keeper_acquire( linda); | 658 | struct s_Keeper* K = keeper_acquire( linda); |
654 | if( K == NULL) return 0; | 659 | if( K == NULL) return 0; |
655 | for( ;;) | 660 | for( ;;) |
656 | { | 661 | { |
@@ -682,11 +687,15 @@ LUAG_FUNC( linda_receive) | |||
682 | struct s_lane* const s = get_lane_from_registry( L); | 687 | struct s_lane* const s = get_lane_from_registry( L); |
683 | if( s != NULL) | 688 | if( s != NULL) |
684 | { | 689 | { |
685 | cancel = s->cancel_request; // testing here causes no delays | 690 | cancel = s->cancel_request; |
686 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything | 691 | } |
687 | { | 692 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; |
688 | break; | 693 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything |
689 | } | 694 | { |
695 | break; | ||
696 | } | ||
697 | if( s != NULL) | ||
698 | { | ||
690 | // change status of lane to "waiting" | 699 | // change status of lane to "waiting" |
691 | prev_status = s->status; // RUNNING, most likely | 700 | prev_status = s->status; // RUNNING, most likely |
692 | ASSERT_L( prev_status == RUNNING); // but check, just in case | 701 | ASSERT_L( prev_status == RUNNING); // but check, just in case |
@@ -694,22 +703,22 @@ LUAG_FUNC( linda_receive) | |||
694 | ASSERT_L( s->waiting_on == NULL); | 703 | ASSERT_L( s->waiting_on == NULL); |
695 | s->waiting_on = &linda->write_happened; | 704 | s->waiting_on = &linda->write_happened; |
696 | } | 705 | } |
697 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
698 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) | ||
699 | { | 706 | { |
707 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
708 | bool_t const signalled = SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout); | ||
700 | if( s != NULL) | 709 | if( s != NULL) |
701 | { | 710 | { |
702 | s->waiting_on = NULL; | 711 | s->waiting_on = NULL; |
703 | s->status = prev_status; | 712 | s->status = prev_status; |
704 | // if woken by a cancel request, be sure to handle it properly | 713 | // if a cancel request is pending, be sure to handle it as soon as possible |
705 | cancel = s->cancel_request; | 714 | cancel = s->cancel_request; |
706 | } | 715 | } |
707 | break; | 716 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; |
708 | } | 717 | if( !signalled || cancel != CANCEL_NONE) |
709 | if( s != NULL) | 718 | { |
710 | { | 719 | // waiting returned after a timeout, or pending cancel: we are done |
711 | s->waiting_on = NULL; | 720 | break; |
712 | s->status = prev_status; | 721 | } |
713 | } | 722 | } |
714 | } | 723 | } |
715 | } | 724 | } |
@@ -740,7 +749,7 @@ LUAG_FUNC( linda_receive) | |||
740 | 749 | ||
741 | 750 | ||
742 | /* | 751 | /* |
743 | * [true] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | 752 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) |
744 | * | 753 | * |
745 | * Set one or more value to Linda. | 754 | * Set one or more value to Linda. |
746 | * TODO: what do we do if we set to non-nil and limit is 0? | 755 | * TODO: what do we do if we set to non-nil and limit is 0? |
@@ -760,28 +769,38 @@ LUAG_FUNC( linda_set) | |||
760 | { | 769 | { |
761 | struct s_Keeper* K = keeper_acquire( linda); | 770 | struct s_Keeper* K = keeper_acquire( linda); |
762 | if( K == NULL) return 0; | 771 | if( K == NULL) return 0; |
763 | if( has_value) | ||
764 | { | ||
765 | // convert nils to some special non-nil sentinel in sent values | ||
766 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | ||
767 | } | ||
768 | pushed = keeper_call( K->L, KEEPER_API( set), L, linda, 2); | ||
769 | if( pushed >= 0) // no error? | ||
770 | { | ||
771 | ASSERT_L( pushed == 0 || pushed == 1); | ||
772 | 772 | ||
773 | if( linda->simulate_cancel == CANCEL_NONE) | ||
774 | { | ||
773 | if( has_value) | 775 | if( has_value) |
774 | { | 776 | { |
775 | // we put some data in the slot, tell readers that they should wake | 777 | // convert nils to some special non-nil sentinel in sent values |
776 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | 778 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); |
777 | } | 779 | } |
778 | if( pushed == 1) | 780 | pushed = keeper_call( K->L, KEEPER_API( set), L, linda, 2); |
781 | if( pushed >= 0) // no error? | ||
779 | { | 782 | { |
780 | // the key was full, but it is no longer the case, tell writers they should wake | 783 | ASSERT_L( pushed == 0 || pushed == 1); |
781 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | 784 | |
782 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | 785 | if( has_value) |
786 | { | ||
787 | // we put some data in the slot, tell readers that they should wake | ||
788 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
789 | } | ||
790 | if( pushed == 1) | ||
791 | { | ||
792 | // the key was full, but it is no longer the case, tell writers they should wake | ||
793 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
794 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
795 | } | ||
783 | } | 796 | } |
784 | } | 797 | } |
798 | else // linda is cancelled | ||
799 | { | ||
800 | // do nothing and return lanes.cancel_error | ||
801 | lua_pushlightuserdata( L, CANCEL_ERROR); | ||
802 | pushed = 1; | ||
803 | } | ||
785 | keeper_release( K); | 804 | keeper_release( K); |
786 | } | 805 | } |
787 | 806 | ||
@@ -837,10 +856,20 @@ LUAG_FUNC( linda_get) | |||
837 | { | 856 | { |
838 | struct s_Keeper* K = keeper_acquire( linda); | 857 | struct s_Keeper* K = keeper_acquire( linda); |
839 | if( K == NULL) return 0; | 858 | if( K == NULL) return 0; |
840 | pushed = keeper_call( K->L, KEEPER_API( get), L, linda, 2); | 859 | |
841 | if( pushed > 0) | 860 | if( linda->simulate_cancel == CANCEL_NONE) |
861 | { | ||
862 | pushed = keeper_call( K->L, KEEPER_API( get), L, linda, 2); | ||
863 | if( pushed > 0) | ||
864 | { | ||
865 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
866 | } | ||
867 | } | ||
868 | else // linda is cancelled | ||
842 | { | 869 | { |
843 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | 870 | // do nothing and return lanes.cancel_error |
871 | lua_pushlightuserdata( L, CANCEL_ERROR); | ||
872 | pushed = 1; | ||
844 | } | 873 | } |
845 | keeper_release( K); | 874 | keeper_release( K); |
846 | // must trigger error after keeper state has been released | 875 | // must trigger error after keeper state has been released |
@@ -893,6 +922,59 @@ LUAG_FUNC( linda_limit) | |||
893 | 922 | ||
894 | 923 | ||
895 | /* | 924 | /* |
925 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | ||
926 | * | ||
927 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | ||
928 | */ | ||
929 | LUAG_FUNC( linda_cancel) | ||
930 | { | ||
931 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
932 | char const* who = luaL_checkstring( L, 2); | ||
933 | struct s_Keeper* K; | ||
934 | |||
935 | // make sure we got 3 arguments: the linda, a key and a limit | ||
936 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
937 | luaL_argcheck( L, lua_gettop( L) == 2, 2, "wrong number of arguments"); | ||
938 | |||
939 | // signalling must be done from inside the K locking area | ||
940 | K = keeper_acquire( linda); | ||
941 | if( K == NULL) return 0; | ||
942 | |||
943 | linda->simulate_cancel = CANCEL_SOFT; | ||
944 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | ||
945 | { | ||
946 | SIGNAL_ALL( &linda->write_happened); | ||
947 | SIGNAL_ALL( &linda->read_happened); | ||
948 | } | ||
949 | else if( strcmp( who, "none") == 0) // reset flag | ||
950 | { | ||
951 | linda->simulate_cancel = CANCEL_NONE; | ||
952 | } | ||
953 | else if( strcmp( who, "read") == 0) // tell blocked readers to wake up | ||
954 | { | ||
955 | SIGNAL_ALL( &linda->write_happened); | ||
956 | } | ||
957 | else if( strcmp( who, "write") == 0) // tell blocked writers to wake up | ||
958 | { | ||
959 | SIGNAL_ALL( &linda->read_happened); | ||
960 | } | ||
961 | else | ||
962 | { | ||
963 | // error ... | ||
964 | linda = NULL; | ||
965 | } | ||
966 | keeper_release( K); | ||
967 | |||
968 | // ... but we must raise it outside the lock | ||
969 | if( !linda) | ||
970 | { | ||
971 | return luaL_error( L, "unknown wake hint '%s'", who); | ||
972 | } | ||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | |||
977 | /* | ||
896 | * lightuserdata= linda_deep( linda_ud ) | 978 | * lightuserdata= linda_deep( linda_ud ) |
897 | * | 979 | * |
898 | * Return the 'deep' userdata pointer, identifying the Linda. | 980 | * Return the 'deep' userdata pointer, identifying the Linda. |
@@ -918,16 +1000,16 @@ LUAG_FUNC( linda_deep ) { | |||
918 | * Useful for concatenation or debugging purposes | 1000 | * Useful for concatenation or debugging purposes |
919 | */ | 1001 | */ |
920 | 1002 | ||
921 | static int linda_tostring( lua_State* L, int _idx, bool_t _opt) | 1003 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) |
922 | { | 1004 | { |
923 | struct s_Linda* linda = lua_toLinda( L, _idx); | 1005 | struct s_Linda* linda = lua_toLinda( L, idx_); |
924 | if( !_opt) | 1006 | if( !opt_) |
925 | { | 1007 | { |
926 | luaL_argcheck( L, linda, _idx, "expected a linda object!"); | 1008 | luaL_argcheck( L, linda, idx_, "expected a linda object!"); |
927 | } | 1009 | } |
928 | if( linda != NULL) | 1010 | if( linda != NULL) |
929 | { | 1011 | { |
930 | char text[32]; | 1012 | char text[128]; |
931 | int len; | 1013 | int len; |
932 | if( linda->name[0]) | 1014 | if( linda->name[0]) |
933 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); | 1015 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); |
@@ -1008,120 +1090,123 @@ LUAG_FUNC( linda_dump) | |||
1008 | * For any other strings, the ID function must not react at all. This allows | 1090 | * For any other strings, the ID function must not react at all. This allows |
1009 | * future extensions of the system. | 1091 | * future extensions of the system. |
1010 | */ | 1092 | */ |
1011 | static void linda_id( lua_State*L, char const * const which) | 1093 | static void linda_id( lua_State* L, char const* const which) |
1012 | { | 1094 | { |
1013 | if (strcmp( which, "new" )==0) | 1095 | if( strcmp( which, "new" ) == 0) |
1014 | { | 1096 | { |
1015 | struct s_Linda *s; | 1097 | struct s_Linda* s; |
1016 | size_t name_len = 0; | 1098 | size_t name_len = 0; |
1017 | char const* linda_name = NULL; | 1099 | char const* linda_name = NULL; |
1018 | int const top = lua_gettop( L); | 1100 | int const top = lua_gettop( L); |
1019 | 1101 | ||
1020 | if( top > 0 && lua_type( L, top) == LUA_TSTRING) | 1102 | if( top > 0 && lua_type( L, top) == LUA_TSTRING) |
1021 | { | 1103 | { |
1022 | linda_name = lua_tostring( L, top); | 1104 | linda_name = lua_tostring( L, top); |
1023 | name_len = strlen( linda_name); | 1105 | name_len = strlen( linda_name); |
1024 | } | 1106 | } |
1025 | 1107 | ||
1026 | /* The deep data is allocated separately of Lua stack; we might no | 1108 | /* The deep data is allocated separately of Lua stack; we might no |
1027 | * longer be around when last reference to it is being released. | 1109 | * longer be around when last reference to it is being released. |
1028 | * One can use any memory allocation scheme. | 1110 | * One can use any memory allocation scheme. |
1029 | */ | 1111 | */ |
1030 | s= (struct s_Linda *) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included | 1112 | s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included |
1031 | ASSERT_L(s); | 1113 | ASSERT_L( s); |
1032 | 1114 | ||
1033 | SIGNAL_INIT( &s->read_happened ); | 1115 | SIGNAL_INIT( &s->read_happened); |
1034 | SIGNAL_INIT( &s->write_happened ); | 1116 | SIGNAL_INIT( &s->write_happened); |
1035 | s->name[0] = 0; | 1117 | s->simulate_cancel = CANCEL_NONE; |
1036 | memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); | 1118 | s->name[0] = 0; |
1119 | memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); | ||
1037 | 1120 | ||
1038 | lua_pushlightuserdata( L, s ); | 1121 | lua_pushlightuserdata( L, s); |
1039 | } | 1122 | } |
1040 | else if( strcmp( which, "delete" ) == 0) | 1123 | else if( strcmp( which, "delete") == 0) |
1041 | { | 1124 | { |
1042 | struct s_Keeper* K; | 1125 | struct s_Keeper* K; |
1043 | struct s_Linda* l= lua_touserdata( L, 1); | 1126 | struct s_Linda* l = lua_touserdata( L, 1); |
1044 | ASSERT_L( l); | 1127 | ASSERT_L( l); |
1045 | |||
1046 | /* Clean associated structures in the keeper state. | ||
1047 | */ | ||
1048 | K = keeper_acquire( l); | ||
1049 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
1050 | { | ||
1051 | keeper_call( K->L, KEEPER_API( clear), L, l, 0); | ||
1052 | } | ||
1053 | keeper_release( K); | ||
1054 | |||
1055 | /* There aren't any lanes waiting on these lindas, since all proxies | ||
1056 | * have been gc'ed. Right? | ||
1057 | */ | ||
1058 | SIGNAL_FREE( &l->read_happened); | ||
1059 | SIGNAL_FREE( &l->write_happened); | ||
1060 | free( l); | ||
1061 | } | ||
1062 | else if (strcmp( which, "metatable" )==0) | ||
1063 | { | ||
1064 | 1128 | ||
1065 | STACK_CHECK( L); | 1129 | /* Clean associated structures in the keeper state. |
1066 | lua_newtable(L); | 1130 | */ |
1067 | // metatable is its own index | 1131 | K = keeper_acquire( l); |
1068 | lua_pushvalue( L, -1); | 1132 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) |
1069 | lua_setfield( L, -2, "__index"); | 1133 | { |
1134 | keeper_call( K->L, KEEPER_API( clear), L, l, 0); | ||
1135 | } | ||
1136 | keeper_release( K); | ||
1070 | 1137 | ||
1071 | // protect metatable from external access | 1138 | /* There aren't any lanes waiting on these lindas, since all proxies |
1072 | lua_pushliteral( L, "Linda"); | 1139 | * have been gc'ed. Right? |
1073 | lua_setfield( L, -2, "__metatable"); | 1140 | */ |
1141 | SIGNAL_FREE( &l->read_happened); | ||
1142 | SIGNAL_FREE( &l->write_happened); | ||
1143 | free( l); | ||
1144 | } | ||
1145 | else if( strcmp( which, "metatable" ) == 0) | ||
1146 | { | ||
1074 | 1147 | ||
1075 | lua_pushcfunction( L, LG_linda_tostring); | 1148 | STACK_CHECK( L); |
1076 | lua_setfield( L, -2, "__tostring"); | 1149 | lua_newtable( L); |
1150 | // metatable is its own index | ||
1151 | lua_pushvalue( L, -1); | ||
1152 | lua_setfield( L, -2, "__index"); | ||
1077 | 1153 | ||
1078 | // Decoda __towatch support | 1154 | // protect metatable from external access |
1079 | lua_pushcfunction( L, LG_linda_dump); | 1155 | lua_pushliteral( L, "Linda"); |
1080 | lua_setfield( L, -2, "__towatch"); | 1156 | lua_setfield( L, -2, "__metatable"); |
1081 | 1157 | ||
1082 | lua_pushcfunction( L, LG_linda_concat); | 1158 | lua_pushcfunction( L, LG_linda_tostring); |
1083 | lua_setfield( L, -2, "__concat"); | 1159 | lua_setfield( L, -2, "__tostring"); |
1084 | 1160 | ||
1085 | // | 1161 | // Decoda __towatch support |
1086 | // [-1]: linda metatable | 1162 | lua_pushcfunction( L, LG_linda_dump); |
1087 | lua_pushcfunction( L, LG_linda_send ); | 1163 | lua_setfield( L, -2, "__towatch"); |
1088 | lua_setfield( L, -2, "send" ); | ||
1089 | 1164 | ||
1090 | lua_pushcfunction( L, LG_linda_receive ); | 1165 | lua_pushcfunction( L, LG_linda_concat); |
1091 | lua_setfield( L, -2, "receive" ); | 1166 | lua_setfield( L, -2, "__concat"); |
1092 | 1167 | ||
1093 | lua_pushcfunction( L, LG_linda_limit ); | 1168 | // [-1]: linda metatable |
1094 | lua_setfield( L, -2, "limit" ); | 1169 | lua_pushcfunction( L, LG_linda_send); |
1170 | lua_setfield( L, -2, "send"); | ||
1095 | 1171 | ||
1096 | lua_pushcfunction( L, LG_linda_set ); | 1172 | lua_pushcfunction( L, LG_linda_receive); |
1097 | lua_setfield( L, -2, "set" ); | 1173 | lua_setfield( L, -2, "receive"); |
1098 | |||
1099 | lua_pushcfunction( L, LG_linda_count ); | ||
1100 | lua_setfield( L, -2, "count" ); | ||
1101 | |||
1102 | lua_pushcfunction( L, LG_linda_get ); | ||
1103 | lua_setfield( L, -2, "get" ); | ||
1104 | 1174 | ||
1105 | lua_pushcfunction( L, LG_linda_deep ); | 1175 | lua_pushcfunction( L, LG_linda_limit); |
1106 | lua_setfield( L, -2, "deep" ); | 1176 | lua_setfield( L, -2, "limit"); |
1107 | 1177 | ||
1108 | lua_pushcfunction( L, LG_linda_dump); | 1178 | lua_pushcfunction( L, LG_linda_set); |
1109 | lua_setfield( L, -2, "dump" ); | 1179 | lua_setfield( L, -2, "set"); |
1110 | |||
1111 | lua_pushliteral( L, BATCH_SENTINEL); | ||
1112 | lua_setfield(L, -2, "batched"); | ||
1113 | 1180 | ||
1114 | STACK_END( L, 1); | 1181 | lua_pushcfunction( L, LG_linda_count); |
1115 | } | 1182 | lua_setfield( L, -2, "count"); |
1116 | else if( strcmp( which, "module") == 0) | 1183 | |
1117 | { | 1184 | lua_pushcfunction( L, LG_linda_get); |
1118 | // linda is a special case because we know lanes must be loaded from the main lua state | 1185 | lua_setfield( L, -2, "get"); |
1119 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | 1186 | |
1120 | // in other words, forever. | 1187 | lua_pushcfunction( L, LG_linda_cancel); |
1121 | lua_pushnil( L); | 1188 | lua_setfield( L, -2, "cancel"); |
1122 | // other idfuncs must push a string naming the module they come from | 1189 | |
1123 | //lua_pushliteral( L, "lanes.core"); | 1190 | lua_pushcfunction( L, LG_linda_deep); |
1124 | } | 1191 | lua_setfield( L, -2, "deep"); |
1192 | |||
1193 | lua_pushcfunction( L, LG_linda_dump); | ||
1194 | lua_setfield( L, -2, "dump"); | ||
1195 | |||
1196 | lua_pushliteral( L, BATCH_SENTINEL); | ||
1197 | lua_setfield(L, -2, "batched"); | ||
1198 | |||
1199 | STACK_END( L, 1); | ||
1200 | } | ||
1201 | else if( strcmp( which, "module") == 0) | ||
1202 | { | ||
1203 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
1204 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
1205 | // in other words, forever. | ||
1206 | lua_pushnil( L); | ||
1207 | // other idfuncs must push a string naming the module they come from | ||
1208 | //lua_pushliteral( L, "lanes.core"); | ||
1209 | } | ||
1125 | } | 1210 | } |
1126 | 1211 | ||
1127 | /* | 1212 | /* |
diff --git a/tests/cancel.lua b/tests/cancel.lua index 0aab341..a5f1dab 100644 --- a/tests/cancel.lua +++ b/tests/cancel.lua | |||
@@ -83,11 +83,26 @@ h = lanes.gen("*", laneBody)() | |||
83 | print "wait 3s" | 83 | print "wait 3s" |
84 | linda:receive( 3, "yeah") | 84 | linda:receive( 3, "yeah") |
85 | 85 | ||
86 | -- hard cancel and wait 10s: the lane will be interrupted from inside its current linda:receive() and won't return from it | 86 | -- hard cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it |
87 | print "hard cancel (always awakens)" | 87 | print "hard cancel (always awakens)" |
88 | h:cancel() | 88 | h:cancel() |
89 | 89 | ||
90 | print "wait 5s" | 90 | print "wait 5s" |
91 | linda:receive( 5, "yeah") | 91 | linda:receive( 5, "yeah") |
92 | 92 | ||
93 | print "\ndone" \ No newline at end of file | 93 | --#################################################################### |
94 | print "\n\n####################################################################\nbegin linda cancel test\n" | ||
95 | h = lanes.gen("*", laneBody)() | ||
96 | |||
97 | -- wait 3s before cancelling the lane | ||
98 | print "wait 3s" | ||
99 | linda:receive( 3, "yeah") | ||
100 | |||
101 | -- linda cancel: the lane will be interrupted from inside its current linda:receive() and won't return from it | ||
102 | print "linda cancel (always awakens the lane)" | ||
103 | linda:cancel( "both") | ||
104 | |||
105 | print "wait 5s" | ||
106 | linda:receive( 5, "yeah") | ||
107 | |||
108 | print "\ndone" | ||