diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2012-09-10 20:41:03 +0200 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2012-09-10 20:41:03 +0200 |
commit | 242feeb342f68999b02c2b8dc4614abefdab8431 (patch) | |
tree | 904a3898035f89e5656036a0e7d454f560eb0455 /src/lanes.c | |
parent | e37191ab11102ee784dc88578165074d2d0a2fff (diff) | |
download | lanes-242feeb342f68999b02c2b8dc4614abefdab8431.tar.gz lanes-242feeb342f68999b02c2b8dc4614abefdab8431.tar.bz2 lanes-242feeb342f68999b02c2b8dc4614abefdab8431.zip |
version 3.3.0
* lane.status can return "killed" if lane was forcefully killed with lanes:cancel()
* lane:join(): return nil, "killed" if called on a killed lane.
* lane[<n>]: produces [1] = nil, [2] = "killed" if the lane was killed
* lane:join(): fixed an assertion in debug builds when joining a lane forcefully cancelled with lane:cancel( <x>, true).
* indexing a lane with a string other than "join", "cancel" or "status" raises an error.
* fixed configure() to correctly apply defaults when they are missing from the provided settings
* added a shutdown_timeout to control the duration Lanes will wait for graceful termination of running lanes at application shutdown. Default is 0.25.
Among other things, fixes issue #31.
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 417 |
1 files changed, 242 insertions, 175 deletions
diff --git a/src/lanes.c b/src/lanes.c index 9f455b2..462999f 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -47,16 +47,17 @@ | |||
47 | * | 47 | * |
48 | * To-do: | 48 | * To-do: |
49 | * | 49 | * |
50 | * Make waiting threads cancelable. | 50 | * Make waiting threads cancellable. |
51 | * ... | 51 | * ... |
52 | */ | 52 | */ |
53 | 53 | ||
54 | char const* VERSION = "3.2.0"; | 54 | char const* VERSION = "3.3.0"; |
55 | 55 | ||
56 | /* | 56 | /* |
57 | =============================================================================== | 57 | =============================================================================== |
58 | 58 | ||
59 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> | 59 | Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> |
60 | 2011-12 Benoit Germain <bnt.germain@gmail.com> | ||
60 | 61 | ||
61 | Permission is hereby granted, free of charge, to any person obtaining a copy | 62 | Permission is hereby granted, free of charge, to any person obtaining a copy |
62 | of this software and associated documentation files (the "Software"), to deal | 63 | of this software and associated documentation files (the "Software"), to deal |
@@ -136,12 +137,12 @@ struct s_lane { | |||
136 | // S: reads to see if cancel is requested | 137 | // S: reads to see if cancel is requested |
137 | 138 | ||
138 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 139 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
139 | SIGNAL_T done_signal_; | 140 | SIGNAL_T done_signal; |
140 | // | 141 | // |
141 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) | 142 | // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) |
142 | // S: sets the signal once cancellation is noticed (avoids a kill) | 143 | // S: sets the signal once cancellation is noticed (avoids a kill) |
143 | 144 | ||
144 | MUTEX_T done_lock_; | 145 | MUTEX_T done_lock; |
145 | // | 146 | // |
146 | // Lock required by 'done_signal' condition variable, protecting | 147 | // Lock required by 'done_signal' condition variable, protecting |
147 | // lane status changes to DONE/ERROR_ST/CANCELLED. | 148 | // lane status changes to DONE/ERROR_ST/CANCELLED. |
@@ -256,7 +257,7 @@ static void check_key_types( lua_State *L, int _start, int _end) | |||
256 | { | 257 | { |
257 | continue; | 258 | continue; |
258 | } | 259 | } |
259 | luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | 260 | (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); |
260 | } | 261 | } |
261 | } | 262 | } |
262 | 263 | ||
@@ -295,7 +296,7 @@ LUAG_FUNC( linda_send) | |||
295 | // make sure there is something to send | 296 | // make sure there is something to send |
296 | if( (uint_t)lua_gettop( L) == key_i) | 297 | if( (uint_t)lua_gettop( L) == key_i) |
297 | { | 298 | { |
298 | luaL_error( L, "no data to send"); | 299 | return luaL_error( L, "no data to send"); |
299 | } | 300 | } |
300 | 301 | ||
301 | // convert nils to some special non-nil sentinel in sent values | 302 | // convert nils to some special non-nil sentinel in sent values |
@@ -353,7 +354,8 @@ LUAG_FUNC( linda_send) | |||
353 | STACK_END(L,0) | 354 | STACK_END(L,0) |
354 | if( s) | 355 | if( s) |
355 | { | 356 | { |
356 | prev_status = s->status; | 357 | prev_status = s->status; // RUNNING, most likely |
358 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
357 | s->status = WAITING; | 359 | s->status = WAITING; |
358 | ASSERT_L( s->waiting_on == NULL); | 360 | ASSERT_L( s->waiting_on == NULL); |
359 | s->waiting_on = &linda->read_happened; | 361 | s->waiting_on = &linda->read_happened; |
@@ -382,7 +384,7 @@ LUAG_FUNC( linda_send) | |||
382 | // must trigger error after keeper state has been released | 384 | // must trigger error after keeper state has been released |
383 | if( pushed < 0) | 385 | if( pushed < 0) |
384 | { | 386 | { |
385 | luaL_error( L, "tried to copy unsupported types"); | 387 | return luaL_error( L, "tried to copy unsupported types"); |
386 | } | 388 | } |
387 | 389 | ||
388 | if( cancel) | 390 | if( cancel) |
@@ -446,7 +448,7 @@ LUAG_FUNC( linda_receive) | |||
446 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); | 448 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); |
447 | if( expected_pushed_min > expected_pushed_max) | 449 | if( expected_pushed_min > expected_pushed_max) |
448 | { | 450 | { |
449 | luaL_error( L, "batched min/max error"); | 451 | return luaL_error( L, "batched min/max error"); |
450 | } | 452 | } |
451 | } | 453 | } |
452 | else | 454 | else |
@@ -507,7 +509,8 @@ LUAG_FUNC( linda_receive) | |||
507 | STACK_END(L, 0) | 509 | STACK_END(L, 0) |
508 | if( s) | 510 | if( s) |
509 | { | 511 | { |
510 | prev_status = s->status; | 512 | prev_status = s->status; // RUNNING, most likely |
513 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
511 | s->status = WAITING; | 514 | s->status = WAITING; |
512 | ASSERT_L( s->waiting_on == NULL); | 515 | ASSERT_L( s->waiting_on == NULL); |
513 | s->waiting_on = &linda->write_happened; | 516 | s->waiting_on = &linda->write_happened; |
@@ -535,7 +538,7 @@ LUAG_FUNC( linda_receive) | |||
535 | // must trigger error after keeper state has been released | 538 | // must trigger error after keeper state has been released |
536 | if( pushed < 0) | 539 | if( pushed < 0) |
537 | { | 540 | { |
538 | luaL_error( L, "tried to copy unsupported types"); | 541 | return luaL_error( L, "tried to copy unsupported types"); |
539 | } | 542 | } |
540 | 543 | ||
541 | if( cancel) | 544 | if( cancel) |
@@ -583,7 +586,7 @@ LUAG_FUNC( linda_set) | |||
583 | // must trigger error after keeper state has been released | 586 | // must trigger error after keeper state has been released |
584 | if( pushed < 0) | 587 | if( pushed < 0) |
585 | { | 588 | { |
586 | luaL_error( L, "tried to copy unsupported types"); | 589 | return luaL_error( L, "tried to copy unsupported types"); |
587 | } | 590 | } |
588 | } | 591 | } |
589 | 592 | ||
@@ -611,7 +614,7 @@ LUAG_FUNC( linda_count) | |||
611 | keeper_release( K); | 614 | keeper_release( K); |
612 | if( pushed < 0) | 615 | if( pushed < 0) |
613 | { | 616 | { |
614 | luaL_error( L, "tried to count an invalid key"); | 617 | return luaL_error( L, "tried to count an invalid key"); |
615 | } | 618 | } |
616 | } | 619 | } |
617 | return pushed; | 620 | return pushed; |
@@ -645,7 +648,7 @@ LUAG_FUNC( linda_get) | |||
645 | // must trigger error after keeper state has been released | 648 | // must trigger error after keeper state has been released |
646 | if( pushed < 0) | 649 | if( pushed < 0) |
647 | { | 650 | { |
648 | luaL_error( L, "tried to copy unsupported types"); | 651 | return luaL_error( L, "tried to copy unsupported types"); |
649 | } | 652 | } |
650 | } | 653 | } |
651 | 654 | ||
@@ -678,7 +681,7 @@ LUAG_FUNC( linda_limit) | |||
678 | // must trigger error after keeper state has been released | 681 | // must trigger error after keeper state has been released |
679 | if( pushed < 0) | 682 | if( pushed < 0) |
680 | { | 683 | { |
681 | luaL_error( L, "tried to copy unsupported types"); | 684 | return luaL_error( L, "tried to copy unsupported types"); |
682 | } | 685 | } |
683 | } | 686 | } |
684 | 687 | ||
@@ -1125,11 +1128,9 @@ static int selfdestruct_gc( lua_State *L) | |||
1125 | // Tested on MacBook Core Duo 2GHz and 10.5.5: | 1128 | // Tested on MacBook Core Duo 2GHz and 10.5.5: |
1126 | // -- AKa 25-Oct-2008 | 1129 | // -- AKa 25-Oct-2008 |
1127 | // | 1130 | // |
1128 | #ifndef ATEXIT_WAIT_SECS | ||
1129 | # define ATEXIT_WAIT_SECS (0.25) | ||
1130 | #endif | ||
1131 | { | 1131 | { |
1132 | double t_until= now_secs() + ATEXIT_WAIT_SECS; | 1132 | lua_Number const shutdown_timeout = lua_tonumber( L, lua_upvalueindex( 1)); |
1133 | double const t_until = now_secs() + shutdown_timeout; | ||
1133 | 1134 | ||
1134 | while( selfdestruct_first != SELFDESTRUCT_END ) | 1135 | while( selfdestruct_first != SELFDESTRUCT_END ) |
1135 | { | 1136 | { |
@@ -1153,7 +1154,7 @@ static int selfdestruct_gc( lua_State *L) | |||
1153 | t_now = now_secs(); | 1154 | t_now = now_secs(); |
1154 | if( n == 0 || ( t_now >= t_until)) | 1155 | if( n == 0 || ( t_now >= t_until)) |
1155 | { | 1156 | { |
1156 | DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, ATEXIT_WAIT_SECS - (t_until - t_now))); | 1157 | DEBUGEXEC(fprintf( stderr, "%d uncancelled lane(s) remain after waiting %fs at process end.\n", n, shutdown_timeout - (t_until - t_now))); |
1157 | break; | 1158 | break; |
1158 | } | 1159 | } |
1159 | } | 1160 | } |
@@ -1201,13 +1202,13 @@ static int selfdestruct_gc( lua_State *L) | |||
1201 | THREAD_KILL( &s->thread); | 1202 | THREAD_KILL( &s->thread); |
1202 | #if THREADAPI == THREADAPI_PTHREAD | 1203 | #if THREADAPI == THREADAPI_PTHREAD |
1203 | // pthread: make sure the thread is really stopped! | 1204 | // pthread: make sure the thread is really stopped! |
1204 | THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status); | 1205 | THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); |
1205 | #endif // THREADAPI == THREADAPI_PTHREAD | 1206 | #endif // THREADAPI == THREADAPI_PTHREAD |
1206 | } | 1207 | } |
1207 | // NO lua_close() in this case because we don't know where execution of the state was interrupted | 1208 | // NO lua_close() in this case because we don't know where execution of the state was interrupted |
1208 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1209 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1209 | SIGNAL_FREE( &s->done_signal_); | 1210 | SIGNAL_FREE( &s->done_signal); |
1210 | MUTEX_FREE( &s->done_lock_); | 1211 | MUTEX_FREE( &s->done_lock); |
1211 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1212 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1212 | free( s); | 1213 | free( s); |
1213 | s = next_s; | 1214 | s = next_s; |
@@ -1288,22 +1289,26 @@ LUAG_FUNC( cancel_test) | |||
1288 | // Limits the process to use only 'cores' CPU cores. To be used for performance | 1289 | // Limits the process to use only 'cores' CPU cores. To be used for performance |
1289 | // testing on multicore devices. DEBUGGING ONLY! | 1290 | // testing on multicore devices. DEBUGGING ONLY! |
1290 | // | 1291 | // |
1291 | LUAG_FUNC( _single ) { | 1292 | LUAG_FUNC( set_singlethreaded) |
1292 | uint_t cores= luaG_optunsigned(L,1,1); | 1293 | { |
1294 | uint_t cores = luaG_optunsigned( L, 1, 1); | ||
1295 | (void) cores; // prevent "unused" warning | ||
1293 | 1296 | ||
1294 | #ifdef PLATFORM_OSX | 1297 | #ifdef PLATFORM_OSX |
1295 | #ifdef _UTILBINDTHREADTOCPU | 1298 | #ifdef _UTILBINDTHREADTOCPU |
1296 | if (cores > 1) luaL_error( L, "Limiting to N>1 cores not possible." ); | 1299 | if( cores > 1) |
1297 | // requires 'chudInitialize()' | 1300 | { |
1298 | utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) | 1301 | return luaL_error( L, "Limiting to N>1 cores not possible"); |
1299 | #else | 1302 | } |
1300 | luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU" ); | 1303 | // requires 'chudInitialize()' |
1301 | #endif | 1304 | utilBindThreadToCPU(0); // # of CPU to run on (we cannot limit to 2..N CPUs?) |
1302 | #else | 1305 | #else |
1303 | luaL_error( L, "not implemented!" ); | 1306 | return luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU"); |
1304 | #endif | 1307 | #endif |
1305 | (void)cores; | 1308 | #else |
1306 | 1309 | return luaL_error( L, "not implemented"); | |
1310 | #endif | ||
1311 | |||
1307 | return 0; | 1312 | return 0; |
1308 | } | 1313 | } |
1309 | 1314 | ||
@@ -1592,8 +1597,8 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1592 | s->L = L = 0; | 1597 | s->L = L = 0; |
1593 | 1598 | ||
1594 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1599 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1595 | SIGNAL_FREE( &s->done_signal_); | 1600 | SIGNAL_FREE( &s->done_signal); |
1596 | MUTEX_FREE( &s->done_lock_); | 1601 | MUTEX_FREE( &s->done_lock); |
1597 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1602 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1598 | free(s); | 1603 | free(s); |
1599 | 1604 | ||
@@ -1611,14 +1616,14 @@ static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs) | |||
1611 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change | 1616 | // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change |
1612 | // | 1617 | // |
1613 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1618 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1614 | MUTEX_LOCK( &s->done_lock_); | 1619 | MUTEX_LOCK( &s->done_lock); |
1615 | { | 1620 | { |
1616 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1621 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1617 | s->status = st; | 1622 | s->status = st; |
1618 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1623 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1619 | SIGNAL_ONE( &s->done_signal_); // wake up master (while 's->done_lock' is on) | 1624 | SIGNAL_ONE( &s->done_signal); // wake up master (while 's->done_lock' is on) |
1620 | } | 1625 | } |
1621 | MUTEX_UNLOCK( &s->done_lock_); | 1626 | MUTEX_UNLOCK( &s->done_lock); |
1622 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1627 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1623 | } | 1628 | } |
1624 | return 0; // ignored | 1629 | return 0; // ignored |
@@ -1680,8 +1685,7 @@ LUAG_FUNC( thread_new ) | |||
1680 | 1685 | ||
1681 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) | 1686 | if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) |
1682 | { | 1687 | { |
1683 | luaL_error( L, "Priority out of range: %d..+%d (%d)", | 1688 | return luaL_error( L, "Priority out of range: %d..+%d (%d)", THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio); |
1684 | THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio ); | ||
1685 | } | 1689 | } |
1686 | 1690 | ||
1687 | /* --- Create and prepare the sub state --- */ | 1691 | /* --- Create and prepare the sub state --- */ |
@@ -1689,7 +1693,10 @@ LUAG_FUNC( thread_new ) | |||
1689 | // populate with selected libraries at the same time | 1693 | // populate with selected libraries at the same time |
1690 | // | 1694 | // |
1691 | L2 = luaG_newstate( libs, on_state_create); | 1695 | L2 = luaG_newstate( libs, on_state_create); |
1692 | if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); | 1696 | if (!L2) |
1697 | { | ||
1698 | return luaL_error( L, "'luaL_newstate()' failed; out of memory"); | ||
1699 | } | ||
1693 | 1700 | ||
1694 | STACK_GROW( L, 2); | 1701 | STACK_GROW( L, 2); |
1695 | STACK_GROW( L2, 3); | 1702 | STACK_GROW( L2, 3); |
@@ -1701,8 +1708,10 @@ LUAG_FUNC( thread_new ) | |||
1701 | STACK_CHECK(L2) | 1708 | STACK_CHECK(L2) |
1702 | if( package) | 1709 | if( package) |
1703 | { | 1710 | { |
1704 | if (lua_type(L,package) != LUA_TTABLE) | 1711 | if( lua_type( L, package) != LUA_TTABLE) |
1705 | luaL_error( L, "expected package as table, got %s", luaL_typename(L,package)); | 1712 | { |
1713 | return luaL_error( L, "expected package as table, got %s", luaL_typename( L, package)); | ||
1714 | } | ||
1706 | lua_getglobal( L2, "package"); | 1715 | lua_getglobal( L2, "package"); |
1707 | if( !lua_isnil( L2, -1)) // package library not loaded: do nothing | 1716 | if( !lua_isnil( L2, -1)) // package library not loaded: do nothing |
1708 | { | 1717 | { |
@@ -1746,13 +1755,15 @@ LUAG_FUNC( thread_new ) | |||
1746 | int nbRequired = 1; | 1755 | int nbRequired = 1; |
1747 | // should not happen, was checked in lanes.lua before calling thread_new() | 1756 | // should not happen, was checked in lanes.lua before calling thread_new() |
1748 | if (lua_type(L, required) != LUA_TTABLE) | 1757 | if (lua_type(L, required) != LUA_TTABLE) |
1749 | luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); | 1758 | { |
1759 | return luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required)); | ||
1760 | } | ||
1750 | lua_pushnil( L); | 1761 | lua_pushnil( L); |
1751 | while( lua_next( L, required) != 0) | 1762 | while( lua_next( L, required) != 0) |
1752 | { | 1763 | { |
1753 | if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) | 1764 | if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired) |
1754 | { | 1765 | { |
1755 | luaL_error( L, "required module list should be a list of strings."); | 1766 | return luaL_error( L, "required module list should be a list of strings"); |
1756 | } | 1767 | } |
1757 | else | 1768 | else |
1758 | { | 1769 | { |
@@ -1772,8 +1783,10 @@ LUAG_FUNC( thread_new ) | |||
1772 | { | 1783 | { |
1773 | STACK_CHECK(L) | 1784 | STACK_CHECK(L) |
1774 | STACK_CHECK(L2) | 1785 | STACK_CHECK(L2) |
1775 | if (!lua_istable(L,glob)) | 1786 | if( !lua_istable( L, glob)) |
1776 | luaL_error( L, "Expected table, got %s", luaL_typename(L,glob)); | 1787 | { |
1788 | return luaL_error( L, "Expected table, got %s", luaL_typename(L,glob)); | ||
1789 | } | ||
1777 | 1790 | ||
1778 | lua_pushnil( L); | 1791 | lua_pushnil( L); |
1779 | lua_pushglobaltable( L2); // Lua 5.2 wants us to push the globals table on the stack | 1792 | lua_pushglobaltable( L2); // Lua 5.2 wants us to push the globals table on the stack |
@@ -1799,7 +1812,9 @@ LUAG_FUNC( thread_new ) | |||
1799 | { | 1812 | { |
1800 | lua_pushvalue( L, 1); | 1813 | lua_pushvalue( L, 1); |
1801 | if( luaG_inter_move( L, L2, 1) != 0) // L->L2 | 1814 | if( luaG_inter_move( L, L2, 1) != 0) // L->L2 |
1802 | luaL_error( L, "tried to copy unsupported types"); | 1815 | { |
1816 | return luaL_error( L, "tried to copy unsupported types"); | ||
1817 | } | ||
1803 | STACK_MID(L,0) | 1818 | STACK_MID(L,0) |
1804 | } | 1819 | } |
1805 | else if( lua_type(L, 1) == LUA_TSTRING) | 1820 | else if( lua_type(L, 1) == LUA_TSTRING) |
@@ -1807,7 +1822,7 @@ LUAG_FUNC( thread_new ) | |||
1807 | // compile the string | 1822 | // compile the string |
1808 | if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) | 1823 | if( luaL_loadstring( L2, lua_tostring( L, 1)) != 0) |
1809 | { | 1824 | { |
1810 | luaL_error( L, "error when parsing lane function code"); | 1825 | return luaL_error( L, "error when parsing lane function code"); |
1811 | } | 1826 | } |
1812 | } | 1827 | } |
1813 | 1828 | ||
@@ -1817,7 +1832,9 @@ LUAG_FUNC( thread_new ) | |||
1817 | // revive arguments | 1832 | // revive arguments |
1818 | // | 1833 | // |
1819 | if( (args > 0) && (luaG_inter_copy( L, L2, args) != 0)) // L->L2 | 1834 | if( (args > 0) && (luaG_inter_copy( L, L2, args) != 0)) // L->L2 |
1820 | luaL_error( L, "tried to copy unsupported types"); | 1835 | { |
1836 | return luaL_error( L, "tried to copy unsupported types"); | ||
1837 | } | ||
1821 | STACK_MID(L,0) | 1838 | STACK_MID(L,0) |
1822 | 1839 | ||
1823 | ASSERT_L( (uint_t)lua_gettop(L2) == 1+args ); | 1840 | ASSERT_L( (uint_t)lua_gettop(L2) == 1+args ); |
@@ -1839,8 +1856,8 @@ LUAG_FUNC( thread_new ) | |||
1839 | s->cancel_request= FALSE; | 1856 | s->cancel_request= FALSE; |
1840 | 1857 | ||
1841 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1858 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1842 | MUTEX_INIT( &s->done_lock_); | 1859 | MUTEX_INIT( &s->done_lock); |
1843 | SIGNAL_INIT( &s->done_signal_); | 1860 | SIGNAL_INIT( &s->done_signal); |
1844 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1861 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1845 | s->mstatus= NORMAL; | 1862 | s->mstatus= NORMAL; |
1846 | s->selfdestruct_next= NULL; | 1863 | s->selfdestruct_next= NULL; |
@@ -1884,50 +1901,44 @@ LUAG_FUNC( thread_new ) | |||
1884 | // * Why NOT cancel/kill a loose thread: | 1901 | // * Why NOT cancel/kill a loose thread: |
1885 | // | 1902 | // |
1886 | // At least timer system uses a free-running thread, they should be handy | 1903 | // At least timer system uses a free-running thread, they should be handy |
1887 | // and the issue of cancelling/killing threads at gc is not very nice, either | 1904 | // and the issue of canceling/killing threads at gc is not very nice, either |
1888 | // (would easily cause waits at gc cycle, which we don't want). | 1905 | // (would easily cause waits at gc cycle, which we don't want). |
1889 | // | 1906 | // |
1890 | // * Why YES kill a loose thread: | 1907 | LUAG_FUNC( thread_gc) |
1891 | // | ||
1892 | // Current way causes segfaults at program exit, if free-running threads are | ||
1893 | // in certain stages. Details are not clear, but this is the core reason. | ||
1894 | // If gc would kill threads then at process exit only one thread would remain. | ||
1895 | // | ||
1896 | // Todo: Maybe we should have a clear #define for selecting either behaviour. | ||
1897 | // | ||
1898 | LUAG_FUNC( thread_gc ) | ||
1899 | { | 1908 | { |
1900 | struct s_lane *s= lua_toLane(L,1); | 1909 | struct s_lane* s = lua_toLane( L, 1); |
1901 | 1910 | ||
1902 | // We can read 's->status' without locks, but not wait for it | 1911 | // We can read 's->status' without locks, but not wait for it |
1903 | // | 1912 | // test KILLED state first, as it doesn't need to enter the selfdestruct chain |
1904 | if (s->status < DONE) | 1913 | if( s->mstatus == KILLED) |
1905 | { | ||
1906 | // | ||
1907 | selfdestruct_add(s); | ||
1908 | assert( s->selfdestruct_next ); | ||
1909 | return 0; | ||
1910 | |||
1911 | } | ||
1912 | else if (s->mstatus==KILLED) | ||
1913 | { | 1914 | { |
1914 | // Make sure a kill has proceeded, before cleaning up the data structure. | 1915 | // Make sure a kill has proceeded, before cleaning up the data structure. |
1915 | // | 1916 | // |
1916 | // NO lua_close() in this case because we don't know where execution of the state was interrupted | 1917 | // NO lua_close() in this case because we don't know where execution of the state was interrupted |
1917 | // If not doing 'THREAD_WAIT()' we should close the Lua state here | ||
1918 | // (can it be out of order, since we killed the lane abruptly?) | ||
1919 | // | ||
1920 | #if 0 | ||
1921 | lua_close( s->L ); | ||
1922 | s->L = 0; | ||
1923 | #else // 0 | ||
1924 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); | 1918 | DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); |
1925 | THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status); | 1919 | // make sure the thread is no longer running, just like thread_join() |
1920 | if(! THREAD_ISNULL( s->thread)) | ||
1921 | THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); | ||
1922 | // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing | ||
1923 | // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen | ||
1924 | if( s->status >= DONE && s->L) | ||
1925 | { | ||
1926 | lua_close( s->L); | ||
1927 | s->L = 0; | ||
1928 | } | ||
1926 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); | 1929 | DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); |
1927 | #endif // 0 | 1930 | } |
1931 | else if( s->status < DONE) | ||
1932 | { | ||
1933 | // still running: will have to be cleaned up later | ||
1934 | selfdestruct_add( s); | ||
1935 | assert( s->selfdestruct_next); | ||
1936 | return 0; | ||
1937 | |||
1928 | } | 1938 | } |
1929 | else if( s->L) | 1939 | else if( s->L) |
1930 | { | 1940 | { |
1941 | // no longer accessing the Lua VM: we can close right now | ||
1931 | lua_close( s->L); | 1942 | lua_close( s->L); |
1932 | s->L = 0; | 1943 | s->L = 0; |
1933 | } | 1944 | } |
@@ -1935,12 +1946,11 @@ LUAG_FUNC( thread_gc ) | |||
1935 | // Clean up after a (finished) thread | 1946 | // Clean up after a (finished) thread |
1936 | // | 1947 | // |
1937 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1948 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1938 | SIGNAL_FREE( &s->done_signal_); | 1949 | SIGNAL_FREE( &s->done_signal); |
1939 | MUTEX_FREE( &s->done_lock_); | 1950 | MUTEX_FREE( &s->done_lock); |
1940 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR | 1951 | #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR |
1941 | 1952 | ||
1942 | free( s); | 1953 | free( s); |
1943 | |||
1944 | return 0; | 1954 | return 0; |
1945 | } | 1955 | } |
1946 | 1956 | ||
@@ -1961,17 +1971,29 @@ LUAG_FUNC( thread_gc ) | |||
1961 | // managed to cancel it. | 1971 | // managed to cancel it. |
1962 | // false if the cancellation timed out, or a kill was needed. | 1972 | // false if the cancellation timed out, or a kill was needed. |
1963 | // | 1973 | // |
1964 | static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | 1974 | |
1975 | typedef enum | ||
1976 | { | ||
1977 | CR_Timeout, | ||
1978 | CR_Cancelled, | ||
1979 | CR_Killed | ||
1980 | } cancel_result; | ||
1981 | |||
1982 | static cancel_result thread_cancel( struct s_lane *s, double secs, bool_t force) | ||
1965 | { | 1983 | { |
1966 | bool_t done= TRUE; | 1984 | cancel_result result; |
1985 | |||
1986 | // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here | ||
1967 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) | 1987 | // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) |
1968 | // | 1988 | if( s->mstatus == KILLED) |
1969 | if( s->status < DONE) | 1989 | { |
1990 | result = CR_Killed; | ||
1991 | } | ||
1992 | else if( s->status < DONE) | ||
1970 | { | 1993 | { |
1971 | s->cancel_request = TRUE; // it's now signaled to stop | 1994 | s->cancel_request = TRUE; // it's now signaled to stop |
1972 | // signal the linda the wake up the thread so that it can react to the cancel query | 1995 | // signal the linda the wake up the thread so that it can react to the cancel query |
1973 | // let us hope we never land here with a pointer on a linda that has been destroyed... | 1996 | // let us hope we never land here with a pointer on a linda that has been destroyed... |
1974 | //MUTEX_LOCK( &selfdestruct_cs ); | ||
1975 | { | 1997 | { |
1976 | SIGNAL_T *waiting_on = s->waiting_on; | 1998 | SIGNAL_T *waiting_on = s->waiting_on; |
1977 | if( s->status == WAITING && waiting_on != NULL) | 1999 | if( s->status == WAITING && waiting_on != NULL) |
@@ -1979,10 +2001,10 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1979 | SIGNAL_ALL( waiting_on); | 2001 | SIGNAL_ALL( waiting_on); |
1980 | } | 2002 | } |
1981 | } | 2003 | } |
1982 | //MUTEX_UNLOCK( &selfdestruct_cs ); | ||
1983 | done = THREAD_WAIT( &s->thread, secs, &s->done_signal_, &s->done_lock_, &s->status); | ||
1984 | 2004 | ||
1985 | if ((!done) && force) | 2005 | result = THREAD_WAIT( &s->thread, secs, &s->done_signal, &s->done_lock, &s->status) ? CR_Cancelled : CR_Timeout; |
2006 | |||
2007 | if( (result == CR_Timeout) && force) | ||
1986 | { | 2008 | { |
1987 | // Killing is asynchronous; we _will_ wait for it to be done at | 2009 | // Killing is asynchronous; we _will_ wait for it to be done at |
1988 | // GC, to make sure the data structure can be released (alternative | 2010 | // GC, to make sure the data structure can be released (alternative |
@@ -1990,10 +2012,18 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) | |||
1990 | // PThread seems to have). | 2012 | // PThread seems to have). |
1991 | // | 2013 | // |
1992 | THREAD_KILL( &s->thread); | 2014 | THREAD_KILL( &s->thread); |
1993 | s->mstatus= KILLED; // mark 'gc' to wait for it | 2015 | s->mstatus = KILLED; // mark 'gc' to wait for it |
2016 | // note that s->status value must remain to whatever it was at the time of the kill | ||
2017 | // because we need to know if we can lua_close() the Lua State or not. | ||
2018 | result = CR_Killed; | ||
1994 | } | 2019 | } |
1995 | } | 2020 | } |
1996 | return done; | 2021 | else |
2022 | { | ||
2023 | // say "ok" by default, including when lane is already done | ||
2024 | result = CR_Cancelled; | ||
2025 | } | ||
2026 | return result; | ||
1997 | } | 2027 | } |
1998 | 2028 | ||
1999 | LUAG_FUNC( thread_cancel) | 2029 | LUAG_FUNC( thread_cancel) |
@@ -2004,10 +2034,11 @@ LUAG_FUNC( thread_cancel) | |||
2004 | } | 2034 | } |
2005 | else | 2035 | else |
2006 | { | 2036 | { |
2007 | struct s_lane *s = lua_toLane( L, 1); | 2037 | struct s_lane* s = lua_toLane( L, 1); |
2008 | double secs = 0.0; | 2038 | double secs = 0.0; |
2009 | uint_t force_i = 2; | 2039 | uint_t force_i = 2; |
2010 | bool_t force, done= TRUE; | 2040 | cancel_result result; |
2041 | bool_t force; | ||
2011 | 2042 | ||
2012 | if( lua_isnumber( L, 2)) | 2043 | if( lua_isnumber( L, 2)) |
2013 | { | 2044 | { |
@@ -2015,15 +2046,32 @@ LUAG_FUNC( thread_cancel) | |||
2015 | ++ force_i; | 2046 | ++ force_i; |
2016 | } | 2047 | } |
2017 | else if( lua_isnil( L, 2)) | 2048 | else if( lua_isnil( L, 2)) |
2049 | { | ||
2018 | ++ force_i; | 2050 | ++ force_i; |
2051 | } | ||
2019 | 2052 | ||
2020 | force = lua_toboolean( L, force_i); // FALSE if nothing there | 2053 | force = lua_toboolean( L, force_i); // FALSE if nothing there |
2021 | 2054 | ||
2022 | done = thread_cancel( s, secs, force); | 2055 | result = thread_cancel( s, secs, force); |
2023 | 2056 | switch( result) | |
2024 | lua_pushboolean( L, done); | 2057 | { |
2025 | return 1; | 2058 | case CR_Timeout: |
2059 | lua_pushboolean( L, 0); | ||
2060 | lua_pushstring( L, "timeout"); | ||
2061 | return 2; | ||
2062 | |||
2063 | case CR_Cancelled: | ||
2064 | lua_pushboolean( L, 1); | ||
2065 | return 1; | ||
2066 | |||
2067 | case CR_Killed: | ||
2068 | lua_pushboolean( L, 0); | ||
2069 | lua_pushstring( L, "killed"); | ||
2070 | return 2; | ||
2071 | } | ||
2026 | } | 2072 | } |
2073 | // should never happen, only here to prevent the compiler from complaining of "not all control paths returning a value" | ||
2074 | return 0; | ||
2027 | } | 2075 | } |
2028 | 2076 | ||
2029 | //--- | 2077 | //--- |
@@ -2039,12 +2087,9 @@ LUAG_FUNC( thread_cancel) | |||
2039 | static char const * thread_status_string( struct s_lane *s) | 2087 | static char const * thread_status_string( struct s_lane *s) |
2040 | { | 2088 | { |
2041 | enum e_status st = s->status; // read just once (volatile) | 2089 | enum e_status st = s->status; // read just once (volatile) |
2042 | char const * str; | 2090 | char const * str = |
2043 | 2091 | (s->mstatus == KILLED) ? "killed" : // new to v3.3.0! | |
2044 | if (s->mstatus == KILLED) | 2092 | (st==PENDING) ? "pending" : |
2045 | st= CANCELLED; | ||
2046 | |||
2047 | str= (st==PENDING) ? "pending" : | ||
2048 | (st==RUNNING) ? "running" : // like in 'co.status()' | 2093 | (st==RUNNING) ? "running" : // like in 'co.status()' |
2049 | (st==WAITING) ? "waiting" : | 2094 | (st==WAITING) ? "waiting" : |
2050 | (st==DONE) ? "done" : | 2095 | (st==DONE) ? "done" : |
@@ -2053,12 +2098,13 @@ static char const * thread_status_string( struct s_lane *s) | |||
2053 | return str; | 2098 | return str; |
2054 | } | 2099 | } |
2055 | 2100 | ||
2056 | static void push_thread_status( lua_State *L, struct s_lane *s) | 2101 | static int push_thread_status( lua_State *L, struct s_lane *s) |
2057 | { | 2102 | { |
2058 | char const * const str = thread_status_string( s); | 2103 | char const * const str = thread_status_string( s); |
2059 | ASSERT_L( str); | 2104 | ASSERT_L( str); |
2060 | 2105 | ||
2061 | lua_pushstring( L, str ); | 2106 | lua_pushstring( L, str ); |
2107 | return 1; | ||
2062 | } | 2108 | } |
2063 | 2109 | ||
2064 | 2110 | ||
@@ -2070,15 +2116,15 @@ static void push_thread_status( lua_State *L, struct s_lane *s) | |||
2070 | // error: returns nil + error value + stack table | 2116 | // error: returns nil + error value + stack table |
2071 | // cancelled: returns nil | 2117 | // cancelled: returns nil |
2072 | // | 2118 | // |
2073 | LUAG_FUNC( thread_join ) | 2119 | LUAG_FUNC( thread_join) |
2074 | { | 2120 | { |
2075 | struct s_lane *s= lua_toLane(L,1); | 2121 | struct s_lane* const s = lua_toLane( L, 1); |
2076 | double wait_secs= luaL_optnumber(L,2,-1.0); | 2122 | double wait_secs= luaL_optnumber(L,2,-1.0); |
2077 | lua_State *L2= s->L; | 2123 | lua_State *L2= s->L; |
2078 | int ret; | 2124 | int ret; |
2079 | bool_t done; | 2125 | bool_t done; |
2080 | 2126 | ||
2081 | done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal_, &s->done_lock_, &s->status); | 2127 | done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); |
2082 | if (!done || !L2) | 2128 | if (!done || !L2) |
2083 | return 0; // timeout: pushes none, leaves 'L2' alive | 2129 | return 0; // timeout: pushes none, leaves 'L2' alive |
2084 | 2130 | ||
@@ -2086,34 +2132,49 @@ LUAG_FUNC( thread_join ) | |||
2086 | 2132 | ||
2087 | STACK_GROW( L, 1); | 2133 | STACK_GROW( L, 1); |
2088 | 2134 | ||
2089 | switch( s->status) | 2135 | if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced |
2090 | { | 2136 | { |
2091 | case DONE: | 2137 | // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values |
2138 | |||
2139 | lua_pushnil( L); | ||
2140 | lua_pushliteral( L, "killed"); | ||
2141 | ret = 2; | ||
2142 | } | ||
2143 | else | ||
2144 | { | ||
2145 | switch( s->status) | ||
2092 | { | 2146 | { |
2093 | uint_t n = lua_gettop( L2); // whole L2 stack | 2147 | case DONE: |
2094 | if( (n > 0) && (luaG_inter_move( L2, L, n) != 0)) | 2148 | { |
2095 | luaL_error( L, "tried to copy unsupported types"); | 2149 | uint_t n = lua_gettop( L2); // whole L2 stack |
2096 | ret = n; | 2150 | if( (n > 0) && (luaG_inter_move( L2, L, n) != 0)) |
2097 | } | 2151 | { |
2098 | break; | 2152 | return luaL_error( L, "tried to copy unsupported types"); |
2153 | } | ||
2154 | ret = n; | ||
2155 | } | ||
2156 | break; | ||
2099 | 2157 | ||
2100 | case ERROR_ST: | 2158 | case ERROR_ST: |
2101 | lua_pushnil( L); | 2159 | lua_pushnil( L); |
2102 | if( luaG_inter_move( L2, L, 2) != 0) // error message at [-2], stack trace at [-1] | 2160 | if( luaG_inter_move( L2, L, 2) != 0) // error message at [-2], stack trace at [-1] |
2103 | luaL_error( L, "tried to copy unsupported types"); | 2161 | { |
2104 | ret= 3; | 2162 | return luaL_error( L, "tried to copy unsupported types"); |
2105 | break; | 2163 | } |
2106 | 2164 | ret= 3; | |
2107 | case CANCELLED: | 2165 | break; |
2108 | ret= 0; | 2166 | |
2109 | break; | 2167 | case CANCELLED: |
2110 | 2168 | ret= 0; | |
2111 | default: | 2169 | break; |
2112 | DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status)); | 2170 | |
2113 | ASSERT_L( FALSE ); ret= 0; | 2171 | default: |
2172 | DEBUGEXEC(fprintf( stderr, "Status: %d\n", s->status)); | ||
2173 | ASSERT_L( FALSE ); ret= 0; | ||
2174 | } | ||
2175 | lua_close( L2); | ||
2114 | } | 2176 | } |
2115 | lua_close( L2); | 2177 | s->L = 0; |
2116 | s->L = L2 = 0; | ||
2117 | 2178 | ||
2118 | return ret; | 2179 | return ret; |
2119 | } | 2180 | } |
@@ -2131,7 +2192,7 @@ LUAG_FUNC( thread_index) | |||
2131 | { | 2192 | { |
2132 | int const UD = 1; | 2193 | int const UD = 1; |
2133 | int const KEY = 2; | 2194 | int const KEY = 2; |
2134 | int const ENV = 3; | 2195 | int const USR = 3; |
2135 | struct s_lane *s = lua_toLane( L, UD); | 2196 | struct s_lane *s = lua_toLane( L, UD); |
2136 | ASSERT_L( lua_gettop( L) == 2); | 2197 | ASSERT_L( lua_gettop( L) == 2); |
2137 | 2198 | ||
@@ -2142,10 +2203,10 @@ LUAG_FUNC( thread_index) | |||
2142 | { | 2203 | { |
2143 | // first, check that we don't already have an environment that holds the requested value | 2204 | // first, check that we don't already have an environment that holds the requested value |
2144 | { | 2205 | { |
2145 | // If key is found in the environment, return it | 2206 | // If key is found in the uservalue, return it |
2146 | lua_getuservalue( L, UD); | 2207 | lua_getuservalue( L, UD); |
2147 | lua_pushvalue( L, KEY); | 2208 | lua_pushvalue( L, KEY); |
2148 | lua_rawget( L, ENV); | 2209 | lua_rawget( L, USR); |
2149 | if( !lua_isnil( L, -1)) | 2210 | if( !lua_isnil( L, -1)) |
2150 | { | 2211 | { |
2151 | return 1; | 2212 | return 1; |
@@ -2157,27 +2218,40 @@ LUAG_FUNC( thread_index) | |||
2157 | bool_t fetched; | 2218 | bool_t fetched; |
2158 | lua_Integer key = lua_tointeger( L, KEY); | 2219 | lua_Integer key = lua_tointeger( L, KEY); |
2159 | lua_pushinteger( L, 0); | 2220 | lua_pushinteger( L, 0); |
2160 | lua_rawget( L, ENV); | 2221 | lua_rawget( L, USR); |
2161 | fetched = !lua_isnil( L, -1); | 2222 | fetched = !lua_isnil( L, -1); |
2162 | lua_pop( L, 1); // back to our 2 args + env on the stack | 2223 | lua_pop( L, 1); // back to our 2 args + uservalue on the stack |
2163 | if( !fetched) | 2224 | if( !fetched) |
2164 | { | 2225 | { |
2165 | lua_pushinteger( L, 0); | 2226 | lua_pushinteger( L, 0); |
2166 | lua_pushboolean( L, 1); | 2227 | lua_pushboolean( L, 1); |
2167 | lua_rawset( L, ENV); | 2228 | lua_rawset( L, USR); |
2168 | // wait until thread has completed | 2229 | // wait until thread has completed |
2169 | lua_pushcfunction( L, LG_thread_join); | 2230 | lua_pushcfunction( L, LG_thread_join); |
2170 | lua_pushvalue( L, UD); | 2231 | lua_pushvalue( L, UD); |
2171 | lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ | 2232 | lua_call( L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ |
2172 | switch( s->status) | 2233 | switch( s->status) |
2173 | { | 2234 | { |
2235 | default: | ||
2236 | if( s->mstatus != KILLED) | ||
2237 | { | ||
2238 | // this is an internal error, we probably never get here | ||
2239 | lua_settop( L, 0); | ||
2240 | lua_pushliteral( L, "Unexpected status: "); | ||
2241 | lua_pushstring( L, thread_status_string( s)); | ||
2242 | lua_concat( L, 2); | ||
2243 | lua_error( L); | ||
2244 | break; | ||
2245 | } | ||
2246 | // fall through if we are killed, as we got nil, "killed" on the stack | ||
2247 | |||
2174 | case DONE: // got regular return values | 2248 | case DONE: // got regular return values |
2175 | { | 2249 | { |
2176 | int i, nvalues = lua_gettop( L) - 3; | 2250 | int i, nvalues = lua_gettop( L) - 3; |
2177 | for( i = nvalues; i > 0; -- i) | 2251 | for( i = nvalues; i > 0; -- i) |
2178 | { | 2252 | { |
2179 | // pop the last element of the stack, to store it in the environment at its proper index | 2253 | // pop the last element of the stack, to store it in the uservalue at its proper index |
2180 | lua_rawseti( L, ENV, i); | 2254 | lua_rawseti( L, USR, i); |
2181 | } | 2255 | } |
2182 | } | 2256 | } |
2183 | break; | 2257 | break; |
@@ -2190,28 +2264,19 @@ LUAG_FUNC( thread_index) | |||
2190 | // store errstring at key -1 | 2264 | // store errstring at key -1 |
2191 | lua_pushnumber( L, -1); | 2265 | lua_pushnumber( L, -1); |
2192 | lua_pushvalue( L, 5); | 2266 | lua_pushvalue( L, 5); |
2193 | lua_rawset( L, ENV); | 2267 | lua_rawset( L, USR); |
2194 | break; | 2268 | break; |
2195 | 2269 | ||
2196 | case CANCELLED: | 2270 | case CANCELLED: |
2197 | // do nothing | 2271 | // do nothing |
2198 | break; | 2272 | break; |
2199 | |||
2200 | default: | ||
2201 | // this is an internal error, we probably never get here | ||
2202 | lua_settop( L, 0); | ||
2203 | lua_pushliteral( L, "Unexpected status: "); | ||
2204 | lua_pushstring( L, thread_status_string( s)); | ||
2205 | lua_concat( L, 2); | ||
2206 | lua_error( L); | ||
2207 | break; | ||
2208 | } | 2273 | } |
2209 | } | 2274 | } |
2210 | lua_settop( L, 3); // UD KEY ENV | 2275 | lua_settop( L, 3); // UD KEY ENV |
2211 | if( key != -1) | 2276 | if( key != -1) |
2212 | { | 2277 | { |
2213 | lua_pushnumber( L, -1); // UD KEY ENV -1 | 2278 | lua_pushnumber( L, -1); // UD KEY ENV -1 |
2214 | lua_rawget( L, ENV); // UD KEY ENV "error" | 2279 | lua_rawget( L, USR); // UD KEY ENV "error" |
2215 | if( !lua_isnil( L, -1)) // an error was stored | 2280 | if( !lua_isnil( L, -1)) // an error was stored |
2216 | { | 2281 | { |
2217 | // Note: Lua 5.1 interpreter is not prepared to show | 2282 | // Note: Lua 5.1 interpreter is not prepared to show |
@@ -2238,7 +2303,7 @@ LUAG_FUNC( thread_index) | |||
2238 | lua_pop( L, 1); // back to our 3 arguments on the stack | 2303 | lua_pop( L, 1); // back to our 3 arguments on the stack |
2239 | } | 2304 | } |
2240 | } | 2305 | } |
2241 | lua_rawgeti( L, ENV, (int)key); | 2306 | lua_rawgeti( L, USR, (int)key); |
2242 | } | 2307 | } |
2243 | return 1; | 2308 | return 1; |
2244 | } | 2309 | } |
@@ -2248,17 +2313,18 @@ LUAG_FUNC( thread_index) | |||
2248 | lua_settop( L, 2); // keep only our original arguments on the stack | 2313 | lua_settop( L, 2); // keep only our original arguments on the stack |
2249 | if( strcmp( keystr, "status") == 0) | 2314 | if( strcmp( keystr, "status") == 0) |
2250 | { | 2315 | { |
2251 | push_thread_status( L, s); // push the string representing the status | 2316 | return push_thread_status( L, s); // push the string representing the status |
2252 | } | 2317 | } |
2253 | else if( strcmp( keystr, "cancel") == 0 || strcmp( keystr, "join") == 0) | 2318 | // return UD.metatable[key] |
2319 | lua_getmetatable( L, UD); // UD KEY mt | ||
2320 | lua_replace( L, -3); // mt KEY | ||
2321 | lua_rawget( L, -2); // mt value | ||
2322 | // only "cancel" and "join" are registered as functions, any other string will raise an error | ||
2323 | if( lua_iscfunction( L, -1)) | ||
2254 | { | 2324 | { |
2255 | // return UD.metatable[key] (should be a function in both cases) | 2325 | return 1; |
2256 | lua_getmetatable( L, UD); // UD KEY mt | ||
2257 | lua_replace( L, -3); // mt KEY | ||
2258 | lua_rawget( L, -2); // mt value | ||
2259 | ASSERT_L( lua_iscfunction( L, -1)); | ||
2260 | } | 2326 | } |
2261 | return 1; | 2327 | return luaL_error( L, "can't index a lane with '%s'", keystr); |
2262 | } | 2328 | } |
2263 | // unknown key | 2329 | // unknown key |
2264 | lua_getmetatable( L, UD); | 2330 | lua_getmetatable( L, UD); |
@@ -2338,14 +2404,14 @@ static const struct luaL_Reg lanes_functions [] = { | |||
2338 | {"now_secs", LG_now_secs}, | 2404 | {"now_secs", LG_now_secs}, |
2339 | {"wakeup_conv", LG_wakeup_conv}, | 2405 | {"wakeup_conv", LG_wakeup_conv}, |
2340 | {"nameof", luaG_nameof}, | 2406 | {"nameof", luaG_nameof}, |
2341 | {"_single", LG__single}, | 2407 | {"set_singlethreaded", LG_set_singlethreaded}, |
2342 | {NULL, NULL} | 2408 | {NULL, NULL} |
2343 | }; | 2409 | }; |
2344 | 2410 | ||
2345 | /* | 2411 | /* |
2346 | * One-time initializations | 2412 | * One-time initializations |
2347 | */ | 2413 | */ |
2348 | static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create) | 2414 | static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create, lua_Number _shutdown_timeout) |
2349 | { | 2415 | { |
2350 | const char *err; | 2416 | const char *err; |
2351 | 2417 | ||
@@ -2398,7 +2464,7 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r | |||
2398 | err = init_keepers( nbKeepers, _on_state_create); | 2464 | err = init_keepers( nbKeepers, _on_state_create); |
2399 | if (err) | 2465 | if (err) |
2400 | { | 2466 | { |
2401 | luaL_error( L, "Unable to initialize: %s", err ); | 2467 | (void) luaL_error( L, "Unable to initialize: %s", err ); |
2402 | } | 2468 | } |
2403 | 2469 | ||
2404 | // Initialize 'timer_deep'; a common Linda object shared by all states | 2470 | // Initialize 'timer_deep'; a common Linda object shared by all states |
@@ -2428,7 +2494,8 @@ static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_r | |||
2428 | { | 2494 | { |
2429 | lua_newuserdata( L, 1); | 2495 | lua_newuserdata( L, 1); |
2430 | lua_newtable( L); | 2496 | lua_newtable( L); |
2431 | lua_pushcfunction( L, selfdestruct_gc); | 2497 | lua_pushnumber( L, _shutdown_timeout); |
2498 | lua_pushcclosure( L, selfdestruct_gc, 1); | ||
2432 | lua_setfield( L, -2, "__gc"); | 2499 | lua_setfield( L, -2, "__gc"); |
2433 | lua_pushliteral( L, "AtExit"); | 2500 | lua_pushliteral( L, "AtExit"); |
2434 | lua_setfield( L, -2, "__metatable"); | 2501 | lua_setfield( L, -2, "__metatable"); |
@@ -2446,10 +2513,10 @@ static volatile long s_initCount = 0; | |||
2446 | LUAG_FUNC( configure ) | 2513 | LUAG_FUNC( configure ) |
2447 | { | 2514 | { |
2448 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); | 2515 | char const* name = luaL_checkstring( L, lua_upvalueindex( 1)); |
2449 | int const nbKeepers = luaL_optint( L, 1, 1); | 2516 | // all parameter checks are done lua-side |
2517 | int const nbKeepers = (int)lua_tointeger( L, 1); | ||
2450 | lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; | 2518 | lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL; |
2451 | luaL_argcheck( L, nbKeepers > 0, 1, "Number of keeper states must be > 0"); | 2519 | lua_Number shutdown_timeout = lua_tonumber( L, 3); |
2452 | luaL_argcheck( L, lua_iscfunction( L, 2) || lua_isnil( L, 2), 2, "on_state_create should be a C function"); | ||
2453 | /* | 2520 | /* |
2454 | * Making one-time initializations. | 2521 | * Making one-time initializations. |
2455 | * | 2522 | * |
@@ -2462,7 +2529,7 @@ LUAG_FUNC( configure ) | |||
2462 | static volatile int /*bool*/ go_ahead; // = 0 | 2529 | static volatile int /*bool*/ go_ahead; // = 0 |
2463 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) | 2530 | if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0) |
2464 | { | 2531 | { |
2465 | init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create); | 2532 | init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout); |
2466 | go_ahead= 1; // let others pass | 2533 | go_ahead= 1; // let others pass |
2467 | } | 2534 | } |
2468 | else | 2535 | else |
@@ -2480,7 +2547,7 @@ LUAG_FUNC( configure ) | |||
2480 | // | 2547 | // |
2481 | if( s_initCount == 0) | 2548 | if( s_initCount == 0) |
2482 | { | 2549 | { |
2483 | init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create); | 2550 | init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create, shutdown_timeout); |
2484 | s_initCount = 1; | 2551 | s_initCount = 1; |
2485 | } | 2552 | } |
2486 | } | 2553 | } |
@@ -2525,7 +2592,7 @@ LUAG_FUNC( configure ) | |||
2525 | lua_setfield(L, -2, "timer_gateway"); | 2592 | lua_setfield(L, -2, "timer_gateway"); |
2526 | 2593 | ||
2527 | lua_pushstring(L, VERSION); | 2594 | lua_pushstring(L, VERSION); |
2528 | lua_setfield(L, -2, "_version"); | 2595 | lua_setfield(L, -2, "version"); |
2529 | 2596 | ||
2530 | lua_pushinteger(L, THREAD_PRIO_MAX); | 2597 | lua_pushinteger(L, THREAD_PRIO_MAX); |
2531 | lua_setfield(L, -2, "max_prio"); | 2598 | lua_setfield(L, -2, "max_prio"); |