aboutsummaryrefslogtreecommitdiff
path: root/src/lanes.c
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2012-09-10 20:41:03 +0200
committerBenoit Germain <bnt.germain@gmail.com>2012-09-10 20:41:03 +0200
commit242feeb342f68999b02c2b8dc4614abefdab8431 (patch)
tree904a3898035f89e5656036a0e7d454f560eb0455 /src/lanes.c
parente37191ab11102ee784dc88578165074d2d0a2fff (diff)
downloadlanes-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.c417
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
54char const* VERSION = "3.2.0"; 54char const* VERSION = "3.3.0";
55 55
56/* 56/*
57=============================================================================== 57===============================================================================
58 58
59Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com> 59Copyright (C) 2007-10 Asko Kauppi <akauppi@gmail.com>
60 2011-12 Benoit Germain <bnt.germain@gmail.com>
60 61
61Permission is hereby granted, free of charge, to any person obtaining a copy 62Permission is hereby granted, free of charge, to any person obtaining a copy
62of this software and associated documentation files (the "Software"), to deal 63of 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//
1291LUAG_FUNC( _single ) { 1292LUAG_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: 1907LUAG_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//
1898LUAG_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//
1964static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force) 1974
1975typedef enum
1976{
1977 CR_Timeout,
1978 CR_Cancelled,
1979 CR_Killed
1980} cancel_result;
1981
1982static 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
1999LUAG_FUNC( thread_cancel) 2029LUAG_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)
2039static char const * thread_status_string( struct s_lane *s) 2087static 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
2056static void push_thread_status( lua_State *L, struct s_lane *s) 2101static 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//
2073LUAG_FUNC( thread_join ) 2119LUAG_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*/
2348static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create) 2414static 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;
2446LUAG_FUNC( configure ) 2513LUAG_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");