aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lanes.c214
-rw-r--r--src/lanes.lua2
-rw-r--r--src/threading.c14
3 files changed, 125 insertions, 105 deletions
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}