aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES11
-rw-r--r--docs/index.html25
-rw-r--r--src/lanes.c214
-rw-r--r--src/lanes.lua2
-rw-r--r--src/threading.c14
-rw-r--r--tests/cancel.lua93
-rw-r--r--tests/linda_perf.lua8
-rw-r--r--tests/perftest.lua17
8 files changed, 258 insertions, 126 deletions
diff --git a/CHANGES b/CHANGES
index 0a6889d..f450700 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,16 @@
1CHANGES: 1CHANGES:
2 2
3CHANGE 90: BGe 16-Jan-14
4 * version 3.7.8
5 * lane:cancel() now accepts a boolean second argument when soft cancelling (negative timeout) to wake the thread if necessary
6 * if a blocked linda send() or receive() call is interrupted by a cancellation request,
7 it returns CANCEL_ERROR so that this case can be differentiated from a simple timeout
8 * fixed WIN32 THREAD_CREATE() wrong _beginthreadex() error detection
9 * fatal WIN32 threading errors retrieve and output the error description string with FormatMessage()
10 * fixed missing lanes.set_singlethreaded
11 * fixed perftest.lua
12 * added test/cancel.lua
13
3CHANGE 89: BGe 09-Jan-14 14CHANGE 89: BGe 09-Jan-14
4 * version 3.7.7 15 * version 3.7.7
5 * fix crash when calling linda:count() on unknown keys 16 * fix crash when calling linda:count() on unknown keys
diff --git a/docs/index.html b/docs/index.html
index c62f64f..3b64cb4 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 09-Jan-14, and applies to version <tt>3.7.7</tt>. 73 This document was revised on 16-Jan-14, and applies to version <tt>3.7.8</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -880,13 +880,18 @@
880<h2 id="cancelling">Cancelling</h2> 880<h2 id="cancelling">Cancelling</h2>
881 881
882<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 882<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
883 bool[,reason] = lane_h:cancel( [timeout_secs=0.0,] [force_kill_bool = false] [, forcekill_timeout=0.0]) 883 bool[,reason] = lane_h:cancel( [positive_timeout_secs=0.0] [, force_kill_bool = false] [, forcekill_timeout=0.0])
884 bool[,reason] = lane_h:cancel( negative_timeout_secs [, wake_bool = false])
884</pre></td></tr></table> 885</pre></td></tr></table>
885 886
886<p> 887<p>
887 <tt>cancel()</tt> sends a cancellation request to the lane.<br/> 888 <tt>cancel()</tt> sends a cancellation request to the lane.<br/>
888 If <tt>timeout_secs</tt> is negative (aka "soft cancel"), starting with version 3.6.3, will only cause <tt>cancel_test()</tt> to return <tt>true</tt>, so that the lane can cleanup manually (the actual value is irrelevant). You can't provide the additional arguments in that case.<br/> 889 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.
889 If <tt>timeout_secs</tt> is positive (aka "hard cancel"), waits for the request to be processed, or a timeout to occur.<br/> 890 <br/>
891 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).
892 If <tt>wake_bool</tt> is <tt>true</tt>, the lane is also signalled so that execution returns from any pending linda operation.
893 <br/>
894 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).
890 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. 895 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.
891 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. 896 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.
892</p> 897</p>
@@ -1010,11 +1015,11 @@
1010<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 1015<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
1011 h = lanes.linda( [opt_name]) 1016 h = lanes.linda( [opt_name])
1012 1017
1013 bool = h:send( [timeout_secs,] key, ...) 1018 bool|cancel_error = h:send( [timeout_secs,] key, ...)
1014 1019
1015 [key, val] = h:receive( [timeout_secs,] key [, ...]) 1020 [key, val]|[cancel_error] = h:receive( [timeout_secs,] key [, ...])
1016 1021
1017 [key, val [, ...]] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max]) 1022 [key, val [, ...]]|[cancel_error] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max])
1018 1023
1019 [true] = h:limit( key, n_uint) 1024 [true] = h:limit( key, n_uint)
1020</pre></td></tr></table> 1025</pre></td></tr></table>
@@ -1041,12 +1046,16 @@
1041 1046
1042<p> 1047<p>
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. 1048 <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.
1049 <br/>
1050 (Since version 3.7.8) <tt>send()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request.
1044</p> 1051</p>
1045 1052
1046<p> 1053<p>
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. 1054 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.
1048 <br> 1055 <br/>
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. 1056 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.
1057 <br/>
1058 (Since version 3.7.8) <tt>receive()</tt> returns <tt>lanes.cancel_error</tt> if interrupted by a soft cancel request.
1050</p> 1059</p>
1051 1060
1052<p> 1061<p>
diff --git a/src/lanes.c b/src/lanes.c
index ef44d90..cad8fb1 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -52,7 +52,7 @@
52 * ... 52 * ...
53 */ 53 */
54 54
55char const* VERSION = "3.7.7"; 55char const* VERSION = "3.7.8";
56 56
57/* 57/*
58=============================================================================== 58===============================================================================
@@ -189,11 +189,59 @@ struct s_lane
189 // For tracking only 189 // For tracking only
190}; 190};
191 191
192static enum e_cancel_request cancel_test( lua_State* L); 192// To allow free-running threads (longer lifespan than the handle's)
193static void cancel_error( lua_State*L ); 193// 'struct s_lane' are malloc/free'd and the handle only carries a pointer.
194// This is not deep userdata since the handle's not portable among lanes.
195//
196#define lua_toLane( L, i) (*((struct s_lane**) lua_touserdata( L, i)))
197
198#define CANCEL_TEST_KEY ((void*)get_lane) // used as registry key
199static inline struct s_lane* get_lane( lua_State* L)
200{
201 struct s_lane* s;
202 STACK_GROW( L, 1);
203 STACK_CHECK( L);
204 lua_pushlightuserdata( L, CANCEL_TEST_KEY);
205 lua_rawget( L, LUA_REGISTRYINDEX);
206 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil
207 lua_pop( L, 1);
208 STACK_END( L, 0);
209 return s;
210}
211
212/*
213* Check if the thread in question ('L') has been signalled for cancel.
214*
215* Called by cancellation hooks and/or pending Linda operations (because then
216* the check won't affect performance).
217*
218* Returns TRUE if any locks are to be exited, and 'cancel_error()' called,
219* to make execution of the lane end.
220*/
221static inline enum e_cancel_request cancel_test( lua_State* L)
222{
223 struct s_lane* const s = get_lane( L);
224 // 's' is NULL for the original main state (and no-one can cancel that)
225 return s ? s->cancel_request : CANCEL_NONE;
226}
194 227
195#define CANCEL_TEST_KEY ((void*)cancel_test) // used as registry key
196#define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel 228#define CANCEL_ERROR ((void*)cancel_error) // 'cancel_error' sentinel
229static int cancel_error( lua_State* L)
230{
231 STACK_GROW( L, 1);
232 lua_pushlightuserdata( L, CANCEL_ERROR); // special error value
233 return lua_error( L); // doesn't return
234}
235
236static void cancel_hook( lua_State* L, lua_Debug* ar)
237{
238 (void)ar;
239 if( cancel_test( L) != CANCEL_NONE)
240 {
241 cancel_error( L);
242 }
243}
244
197 245
198#if ERROR_FULL_STACK 246#if ERROR_FULL_STACK
199static int lane_error( lua_State* L); 247static int lane_error( lua_State* L);
@@ -383,6 +431,7 @@ static void check_key_types( lua_State*L, int _start, int _end)
383* 431*
384* Returns: 'true' if the value was queued 432* Returns: 'true' if the value was queued
385* 'false' for timeout (only happens when the queue size is limited) 433* 'false' for timeout (only happens when the queue size is limited)
434* nil, CANCEL_ERROR if cancelled
386*/ 435*/
387LUAG_FUNC( linda_send) 436LUAG_FUNC( linda_send)
388{ 437{
@@ -417,7 +466,7 @@ LUAG_FUNC( linda_send)
417 // convert nils to some special non-nil sentinel in sent values 466 // convert nils to some special non-nil sentinel in sent values
418 keeper_toggle_nil_sentinels( L, key_i + 1, 1); 467 keeper_toggle_nil_sentinels( L, key_i + 1, 1);
419 468
420 STACK_GROW(L, 1); 469 STACK_GROW( L, 1);
421 { 470 {
422 struct s_Keeper* K = keeper_acquire( linda); 471 struct s_Keeper* K = keeper_acquire( linda);
423 lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' 472 lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK'
@@ -451,26 +500,17 @@ LUAG_FUNC( linda_send)
451 } 500 }
452 /* limit faced; push until timeout */ 501 /* limit faced; push until timeout */
453 502
454 cancel = cancel_test( L); // testing here causes no delays
455 if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything
456 { 503 {
457 break;
458 }
459
460 // change status of lane to "waiting"
461 {
462 struct s_lane* s;
463 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 504 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
464 STACK_GROW( L, 1); 505 struct s_lane* const s = get_lane( L);
465
466 STACK_CHECK( L);
467 lua_pushlightuserdata( L, CANCEL_TEST_KEY);
468 lua_rawget( L, LUA_REGISTRYINDEX);
469 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) or nil if in the main Lua state
470 lua_pop( L, 1);
471 STACK_END( L, 0);
472 if( s != NULL) 506 if( s != NULL)
473 { 507 {
508 cancel = s->cancel_request; // testing here causes no delays
509 if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything
510 {
511 break;
512 }
513 // change status of lane to "waiting"
474 prev_status = s->status; // RUNNING, most likely 514 prev_status = s->status; // RUNNING, most likely
475 ASSERT_L( prev_status == RUNNING); // but check, just in case 515 ASSERT_L( prev_status == RUNNING); // but check, just in case
476 s->status = WAITING; 516 s->status = WAITING;
@@ -484,6 +524,8 @@ LUAG_FUNC( linda_send)
484 { 524 {
485 s->waiting_on = NULL; 525 s->waiting_on = NULL;
486 s->status = prev_status; 526 s->status = prev_status;
527 // if woken by a cancel request, be sure to handle it properly
528 cancel = s->cancel_request;
487 } 529 }
488 break; 530 break;
489 } 531 }
@@ -504,12 +546,21 @@ LUAG_FUNC( linda_send)
504 return luaL_error( L, "tried to copy unsupported types"); 546 return luaL_error( L, "tried to copy unsupported types");
505 } 547 }
506 548
507 // raise an error interrupting execution only in case of hard cancel 549 switch( cancel)
508 if( cancel == CANCEL_HARD) 550 {
509 cancel_error( L); 551 case CANCEL_SOFT:
552 // if user wants to soft-cancel, the call returns CANCEL_ERROR
553 lua_pushlightuserdata( L, CANCEL_ERROR);
554 return 1;
510 555
511 lua_pushboolean( L, ret); 556 case CANCEL_HARD:
512 return 1; 557 // raise an error interrupting execution only in case of hard cancel
558 return cancel_error( L); // raises an error and doesn't return
559
560 default:
561 lua_pushboolean( L, ret); // true (success) or false (timeout)
562 return 1;
563 }
513} 564}
514 565
515 566
@@ -611,26 +662,17 @@ LUAG_FUNC( linda_receive)
611 } 662 }
612 /* nothing received; wait until timeout */ 663 /* nothing received; wait until timeout */
613 664
614 cancel = cancel_test( L); // testing here causes no delays
615 if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything
616 { 665 {
617 break;
618 }
619
620 // change status of lane to "waiting"
621 {
622 struct s_lane* s;
623 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 666 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
624 STACK_GROW( L, 1); 667 struct s_lane* const s = get_lane( L);
625
626 STACK_CHECK( L);
627 lua_pushlightuserdata( L, CANCEL_TEST_KEY);
628 lua_rawget( L, LUA_REGISTRYINDEX);
629 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) or nil if in the main Lua state
630 lua_pop( L, 1);
631 STACK_END( L, 0);
632 if( s != NULL) 668 if( s != NULL)
633 { 669 {
670 cancel = s->cancel_request; // testing here causes no delays
671 if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything
672 {
673 break;
674 }
675 // change status of lane to "waiting"
634 prev_status = s->status; // RUNNING, most likely 676 prev_status = s->status; // RUNNING, most likely
635 ASSERT_L( prev_status == RUNNING); // but check, just in case 677 ASSERT_L( prev_status == RUNNING); // but check, just in case
636 s->status = WAITING; 678 s->status = WAITING;
@@ -644,6 +686,8 @@ LUAG_FUNC( linda_receive)
644 { 686 {
645 s->waiting_on = NULL; 687 s->waiting_on = NULL;
646 s->status = prev_status; 688 s->status = prev_status;
689 // if woken by a cancel request, be sure to handle it properly
690 cancel = s->cancel_request;
647 } 691 }
648 break; 692 break;
649 } 693 }
@@ -663,11 +707,20 @@ LUAG_FUNC( linda_receive)
663 return luaL_error( L, "tried to copy unsupported types"); 707 return luaL_error( L, "tried to copy unsupported types");
664 } 708 }
665 709
666 // raise an error interrupting execution only in case of hard cancel 710 switch( cancel)
667 if( cancel == CANCEL_HARD) 711 {
668 cancel_error( L); 712 case CANCEL_SOFT:
713 // if user wants to soft-cancel, the call returns CANCEL_ERROR
714 lua_pushlightuserdata( L, CANCEL_ERROR);
715 return 1;
669 716
670 return pushed; 717 case CANCEL_HARD:
718 // raise an error interrupting execution only in case of hard cancel
719 return cancel_error( L); // raises an error and doesn't return
720
721 default:
722 return pushed;
723 }
671} 724}
672 725
673 726
@@ -1225,6 +1278,14 @@ static cancel_result thread_cancel( lua_State* L, struct s_lane* s, double secs,
1225 { 1278 {
1226 s->cancel_request = CANCEL_SOFT; // it's now signaled to stop 1279 s->cancel_request = CANCEL_SOFT; // it's now signaled to stop
1227 // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own 1280 // negative timeout: we don't want to truly abort the lane, we just want it to react to cancel_test() on its own
1281 if( force) // wake the thread so that execution returns from any pending linda operation if desired
1282 {
1283 SIGNAL_T *waiting_on = s->waiting_on;
1284 if( s->status == WAITING && waiting_on != NULL)
1285 {
1286 SIGNAL_ALL( waiting_on);
1287 }
1288 }
1228 // say we succeeded though 1289 // say we succeeded though
1229 result = CR_Cancelled; 1290 result = CR_Cancelled;
1230 } 1291 }
@@ -1509,54 +1570,6 @@ static int selfdestruct_gc( lua_State* L)
1509} 1570}
1510 1571
1511 1572
1512// To allow free-running threads (longer lifespan than the handle's)
1513// 'struct s_lane' are malloc/free'd and the handle only carries a pointer.
1514// This is not deep userdata since the handle's not portable among lanes.
1515//
1516#define lua_toLane(L,i) (* ((struct s_lane**) lua_touserdata(L,i)))
1517
1518
1519/*
1520* Check if the thread in question ('L') has been signalled for cancel.
1521*
1522* Called by cancellation hooks and/or pending Linda operations (because then
1523* the check won't affect performance).
1524*
1525* Returns TRUE if any locks are to be exited, and 'cancel_error()' called,
1526* to make execution of the lane end.
1527*/
1528static enum e_cancel_request cancel_test( lua_State* L)
1529{
1530 struct s_lane* s;
1531
1532 STACK_GROW( L, 1);
1533
1534 STACK_CHECK( L);
1535 lua_pushlightuserdata( L, CANCEL_TEST_KEY);
1536 lua_rawget( L, LUA_REGISTRYINDEX);
1537 s = lua_touserdata( L, -1); // lightuserdata (true 's_lane' pointer) / nil
1538 lua_pop( L, 1);
1539 STACK_END( L, 0);
1540
1541 // 's' is NULL for the original main state (no-one can cancel that)
1542 //
1543 return s ? s->cancel_request : CANCEL_NONE;
1544}
1545
1546static void cancel_error( lua_State*L ) {
1547 STACK_GROW(L,1);
1548 lua_pushlightuserdata( L, CANCEL_ERROR ); // special error value
1549 lua_error(L); // no return
1550}
1551
1552static void cancel_hook( lua_State*L, lua_Debug *ar )
1553{
1554 (void)ar;
1555 if( cancel_test( L) != CANCEL_NONE)
1556 cancel_error( L);
1557}
1558
1559
1560//--- 1573//---
1561// bool = cancel_test() 1574// bool = cancel_test()
1562// 1575//
@@ -2294,7 +2307,7 @@ LUAG_FUNC( thread_gc)
2294 return 0; 2307 return 0;
2295} 2308}
2296 2309
2297// lane_h:cancel( [timeout,] force[, forcekill_timeout]) 2310// lane_h:cancel( [timeout] [, force [, forcekill_timeout]])
2298LUAG_FUNC( thread_cancel) 2311LUAG_FUNC( thread_cancel)
2299{ 2312{
2300 if( lua_gettop( L) < 1 || lua_type( L, 1) != LUA_TUSERDATA) 2313 if( lua_gettop( L) < 1 || lua_type( L, 1) != LUA_TUSERDATA)
@@ -2311,10 +2324,11 @@ LUAG_FUNC( thread_cancel)
2311 if( lua_isnumber( L, 2)) 2324 if( lua_isnumber( L, 2))
2312 { 2325 {
2313 secs = lua_tonumber( L, 2); 2326 secs = lua_tonumber( L, 2);
2314 if( secs < 0.0 && lua_gettop( L) > 2) 2327 if( secs < 0.0 && lua_gettop( L) > 3)
2315 { 2328 {
2316 return luaL_error( L, "can't force a soft cancel"); 2329 return luaL_error( L, "can't force_kill a soft cancel");
2317 } 2330 }
2331 // negative timeout and force flag means we want to wake linda-waiting threads
2318 ++ force_i; 2332 ++ force_i;
2319 ++ forcekill_timeout_i; 2333 ++ forcekill_timeout_i;
2320 } 2334 }
@@ -2330,16 +2344,16 @@ LUAG_FUNC( thread_cancel)
2330 2344
2331 switch( thread_cancel( L, s, secs, force, forcekill_timeout)) 2345 switch( thread_cancel( L, s, secs, force, forcekill_timeout))
2332 { 2346 {
2333 case CR_Timeout: 2347 case CR_Timeout:
2334 lua_pushboolean( L, 0); 2348 lua_pushboolean( L, 0);
2335 lua_pushstring( L, "timeout"); 2349 lua_pushstring( L, "timeout");
2336 return 2; 2350 return 2;
2337 2351
2338 case CR_Cancelled: 2352 case CR_Cancelled:
2339 lua_pushboolean( L, 1); 2353 lua_pushboolean( L, 1);
2340 return 1; 2354 return 1;
2341 2355
2342 case CR_Killed: 2356 case CR_Killed:
2343 lua_pushboolean( L, 0); 2357 lua_pushboolean( L, 0);
2344 lua_pushstring( L, "killed"); 2358 lua_pushstring( L, "killed");
2345 return 2; 2359 return 2;
diff --git a/src/lanes.lua b/src/lanes.lua
index 0c544bb..e7b9715 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -128,7 +128,6 @@ lanes.configure = function( settings_)
128 end 128 end
129 local settings = core.configure and core.configure( params_checker( settings_)) or core.settings 129 local settings = core.configure and core.configure( params_checker( settings_)) or core.settings
130 local thread_new = assert( core.thread_new) 130 local thread_new = assert( core.thread_new)
131 local set_singlethreaded = assert( core.set_singlethreaded)
132 local max_prio = assert( core.max_prio) 131 local max_prio = assert( core.max_prio)
133 132
134lanes.ABOUT = 133lanes.ABOUT =
@@ -664,6 +663,7 @@ end
664 lanes.linda = core.linda 663 lanes.linda = core.linda
665 lanes.cancel_error = core.cancel_error 664 lanes.cancel_error = core.cancel_error
666 lanes.nameof = core.nameof 665 lanes.nameof = core.nameof
666 lanes.set_singlethreaded = core.set_singlethreaded
667 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false 667 lanes.threads = core.threads or function() error "lane tracking is not available" end -- core.threads isn't registered if settings.track_lanes is false
668 lanes.set_thread_priority = core.set_thread_priority 668 lanes.set_thread_priority = core.set_thread_priority
669 lanes.timer = timer 669 lanes.timer = timer
diff --git a/src/threading.c b/src/threading.c
index 3014136..5a3e64b 100644
--- a/src/threading.c
+++ b/src/threading.c
@@ -87,11 +87,13 @@ THE SOFTWARE.
87#if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) 87#if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)
88static void FAIL( char const* funcname, int rc) 88static void FAIL( char const* funcname, int rc)
89{ 89{
90 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); 90 char buf[256];
91 FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM, NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 256, NULL);
92 fprintf( stderr, "%s() failed! [GetLastError() -> %d] '%s'", funcname, rc, buf);
91#ifdef _MSC_VER 93#ifdef _MSC_VER
92 __debugbreak(); // give a chance to the debugger! 94 __debugbreak(); // give a chance to the debugger!
93#endif // _MSC_VER 95#endif // _MSC_VER
94 abort(); 96 abort();
95} 97}
96#endif // win32 build 98#endif // win32 build
97 99
@@ -296,11 +298,15 @@ void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (__stdcall *func)( void*), vo
296 NULL // thread id (not used) 298 NULL // thread id (not used)
297 ); 299 );
298 300
299 if( h == INVALID_HANDLE_VALUE) 301 if( h == NULL) // _beginthreadex returns 0L on failure instead of -1L (like _beginthread)
302 {
300 FAIL( "CreateThread", GetLastError()); 303 FAIL( "CreateThread", GetLastError());
304 }
301 305
302 if (!SetThreadPriority( h, gs_prio_remap[prio + 3])) 306 if (!SetThreadPriority( h, gs_prio_remap[prio + 3]))
307 {
303 FAIL( "SetThreadPriority", GetLastError()); 308 FAIL( "SetThreadPriority", GetLastError());
309 }
304 310
305 *ref = h; 311 *ref = h;
306} 312}
diff --git a/tests/cancel.lua b/tests/cancel.lua
new file mode 100644
index 0000000..0aab341
--- /dev/null
+++ b/tests/cancel.lua
@@ -0,0 +1,93 @@
1local lanes = require "lanes" .configure{ with_timers = false}
2
3local linda = lanes.linda()
4
5local laneBody = function( timeout_)
6 set_finalizer( function( err, stk)
7 if err == lanes.cancel_error then
8 -- note that we don't get the cancel_error when running wrapped inside a protected call if it doesn't rethrow it
9 print( " laneBody after cancel" )
10 elseif err then
11 print( " laneBody error: "..tostring(err))
12 else
13 print(" laneBody finalized")
14 end
15 end)
16
17 print( " entering lane with " .. tostring( timeout_) .. " timeout")
18 repeat
19 -- block-wait to be hard-cancelled
20 print " lane calling receive()"
21 local key, val = linda:receive( timeout_, "boob")
22 print( " receive() -> ", lanes.cancel_error == key and "cancel_error" or tostring( key), tostring( val))
23 until cancel_test() -- soft cancel self test
24 print " shutting down after breaking out of loop"
25end
26
27local protectedBody = function( ...)
28 local ce = lanes.cancel_error
29 local errorHandler = function( _msg)
30 -- forward the message to the main thread that will display it with a popup
31 print( " error handler got ", ce == _msg and "cancel_error"or tostring( _msg))
32 return _msg
33 end
34 -- Lua 5.1 doesn't pass additional xpcall arguments to the called function
35 -- therefore we need to create a closure that has no arguments but pulls everything from its upvalue
36 local params = {...}
37 local paramLessClosure = function() laneBody(table.unpack( params)) end
38 local status, message = xpcall( paramLessClosure, errorHandler)
39 if status == false then
40 print( " error handler rethrowing '" .. (ce == message and "cancel_error"or tostring( message)) .. "'")
41 -- if the error isn't rethrown, the lane's finalizer won't get it
42 error( message)
43 end
44end
45
46--####################################################################
47
48print "####################################################################\nbegin soft cancel test\n"
49h = lanes.gen("*", protectedBody)( 0.666)
50print "wait 3s"
51linda:receive( 3, "yeah")
52
53-- soft cancel
54print "soft cancel with awakening"
55h:cancel( -1, true)
56
57-- wait 10s: the lane will interrupt its loop and print the exit message
58print "wait 2s"
59linda:receive( 2, "yeah")
60
61--####################################################################
62
63print "\n\n####################################################################\nbegin hard cancel test\n"
64h = lanes.gen("*", protectedBody)()
65
66-- wait 3s before cancelling the lane
67print "wait 3s"
68linda:receive( 3, "yeah")
69
70-- hard cancel and wait 10s: the lane will be interrupted from inside its current linda:receive() and won't return from it
71print "hard cancel (always awakens)"
72h:cancel()
73
74print "wait 5s"
75linda:receive( 5, "yeah")
76
77--####################################################################
78
79print "\n\n####################################################################\nbegin hard cancel test with unprotected lane body\n"
80h = lanes.gen("*", laneBody)()
81
82-- wait 3s before cancelling the lane
83print "wait 3s"
84linda:receive( 3, "yeah")
85
86-- hard cancel and wait 10s: the lane will be interrupted from inside its current linda:receive() and won't return from it
87print "hard cancel (always awakens)"
88h:cancel()
89
90print "wait 5s"
91linda:receive( 5, "yeah")
92
93print "\ndone" \ No newline at end of file
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua
index ebe9eac..9348f71 100644
--- a/tests/linda_perf.lua
+++ b/tests/linda_perf.lua
@@ -34,7 +34,7 @@ local function ziva( preloop, loop, batch)
34 -- prefill the linda a bit to increase fifo stress 34 -- prefill the linda a bit to increase fifo stress
35 local top = math.max( preloop, loop) 35 local top = math.max( preloop, loop)
36 local l, lane = lanes.linda() 36 local l, lane = lanes.linda()
37 local t1 = os.time() 37 local t1 = lanes.now_secs()
38 for i = 1, preloop do 38 for i = 1, preloop do
39 l:send( "key", i) 39 l:send( "key", i)
40 end 40 end
@@ -59,7 +59,7 @@ local function ziva( preloop, loop, batch)
59 end 59 end
60 l:send( "done" ,"are you happy?") 60 l:send( "done" ,"are you happy?")
61 lane:join() 61 lane:join()
62 return os.difftime(os.time(), t1) 62 return lanes.now_secs() - t1
63end 63end
64 64
65local tests = 65local tests =
@@ -149,7 +149,7 @@ local function ziva2( preloop, loop, batch)
149 l:receive( "key") 149 l:receive( "key")
150 end 150 end
151 end 151 end
152 local t1 = os.time() 152 local t1 = lanes.now_secs()
153 -- first, prime the linda with some data 153 -- first, prime the linda with some data
154 for i = 1, preloop, step do 154 for i = 1, preloop, step do
155 batch_send() 155 batch_send()
@@ -165,7 +165,7 @@ local function ziva2( preloop, loop, batch)
165 for i = 1, preloop, step do 165 for i = 1, preloop, step do
166 batch_read() 166 batch_read()
167 end 167 end
168 return os.difftime(os.time(), t1) 168 return lanes.now_secs() - t1
169end 169end
170 170
171local tests2 = 171local tests2 =
diff --git a/tests/perftest.lua b/tests/perftest.lua
index 4df2ad8..6ffc064 100644
--- a/tests/perftest.lua
+++ b/tests/perftest.lua
@@ -27,8 +27,7 @@
27local MSYS= os.getenv("OSTYPE")=="msys" 27local MSYS= os.getenv("OSTYPE")=="msys"
28 28
29 29
30local lanes = require "lanes" 30local lanes = require "lanes".configure{ with_timers = false}
31lanes.configure()
32 31
33local m= require "argtable" 32local m= require "argtable"
34local argtable= assert( m.argtable ) 33local argtable= assert( m.argtable )
@@ -36,7 +35,7 @@ local argtable= assert( m.argtable )
36local N= 1000 -- threads/loops to use 35local N= 1000 -- threads/loops to use
37local M= 1000 -- sieves from 1..M 36local M= 1000 -- sieves from 1..M
38local PLAIN= false -- single threaded (true) or using Lanes (false) 37local PLAIN= false -- single threaded (true) or using Lanes (false)
39local SINGLE= false -- cores to use (false / 1..n) 38local SINGLE= 0 -- cores to use (0 / 1..n)
40local TIME= false -- use Lua for the timing 39local TIME= false -- use Lua for the timing
41local PRIO_ODD, PRIO_EVEN -- -3..+3 40local PRIO_ODD, PRIO_EVEN -- -3..+3
42 41
@@ -63,7 +62,7 @@ end
63for k,v in pairs( argtable(...) ) do 62for k,v in pairs( argtable(...) ) do
64 if k==1 then N= tonumber(v) or HELP() 63 if k==1 then N= tonumber(v) or HELP()
65 elseif k=="plain" then PLAIN= true 64 elseif k=="plain" then PLAIN= true
66 elseif k=="single" then SINGLE= v -- true/number 65 elseif k=="single" then SINGLE= v -- number
67 elseif k=="time" then TIME= true 66 elseif k=="time" then TIME= true
68 elseif k=="prio" then PRIO_ODD, PRIO_EVEN= prio_param(v) 67 elseif k=="prio" then PRIO_ODD, PRIO_EVEN= prio_param(v)
69 else HELP() 68 else HELP()
@@ -104,7 +103,7 @@ local function sieve_lane(N,id)
104 while 1 do 103 while 1 do
105 local n = g() 104 local n = g()
106 if n == nil then return end 105 if n == nil then return end
107 if math.mod(n, p) ~= 0 then coroutine.yield(n) end 106 if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
108 end 107 end
109 end) 108 end)
110 end 109 end
@@ -138,7 +137,7 @@ local f_odd= lanes.gen( "base,coroutine,math,table,io", -- "*" = all
138 137
139io.stderr:write( "*** Counting primes 1.."..M.." "..N.." times ***\n\n" ) 138io.stderr:write( "*** Counting primes 1.."..M.." "..N.." times ***\n\n" )
140 139
141local t0= TIME and os.time() 140local t0= TIME and lanes.now_secs()
142 141
143if PLAIN then 142if PLAIN then
144 io.stderr:write( "Plain (no multithreading):\n" ) 143 io.stderr:write( "Plain (no multithreading):\n" )
@@ -148,9 +147,9 @@ if PLAIN then
148 assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 ) 147 assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 )
149 end 148 end
150else 149else
151 if SINGLE then 150 if SINGLE > 0 then
152 io.stderr:write( (tonumber(SINGLE) and SINGLE or 1) .. " core(s):\n" ) 151 io.stderr:write( (tonumber(SINGLE) and SINGLE or 1) .. " core(s):\n" )
153 lanes.single(SINGLE) -- limit to N cores (just OS X) 152 lanes.set_singlethreaded(SINGLE) -- limit to N cores (just OS X)
154 else 153 else
155 io.stderr:write( "Multi core:\n" ) 154 io.stderr:write( "Multi core:\n" )
156 end 155 end
@@ -177,7 +176,7 @@ end
177io.stderr:write "\n" 176io.stderr:write "\n"
178 177
179if TIME then 178if TIME then
180 local t= os.time() - t0 179 local t= lanes.now_secs() - t0
181 io.stderr:write( "*** TIMING: "..t.." seconds ***\n" ) 180 io.stderr:write( "*** TIMING: "..t.." seconds ***\n" )
182end 181end
183 182