diff options
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | docs/index.html | 81 | ||||
-rw-r--r-- | src/lanes.c | 111 | ||||
-rw-r--r-- | src/lanes.lua | 14 | ||||
-rw-r--r-- | tests/basic.lua | 36 |
5 files changed, 169 insertions, 78 deletions
@@ -1,5 +1,10 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 94: BGe 22-Jan-14 | ||
4 | * version 3.8.2 | ||
5 | * new lane launcher option gc_cb to set a callback that is invoked when a lane is garbage collected | ||
6 | * Fix more invalid memory accesses when fetching the name of a joined lane with lanes:threads() (because its lua_State is closed) | ||
7 | |||
3 | CHANGE 93: BGe 20-Jan-14 | 8 | CHANGE 93: BGe 20-Jan-14 |
4 | * slightly improve linda performance when the producer/consumer scenario leaves leave the key empty | 9 | * slightly improve linda performance when the producer/consumer scenario leaves leave the key empty |
5 | 10 | ||
diff --git a/docs/index.html b/docs/index.html index 7078b13..c662a14 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -63,14 +63,14 @@ | |||
63 | 63 | ||
64 | <font size="-1"> | 64 | <font size="-1"> |
65 | <p> | 65 | <p> |
66 | <br> | 66 | <br/> |
67 | <i>Copyright © 2007-14 Asko Kauppi, Benoit Germain. All rights reserved.</i> | 67 | <i>Copyright © 2007-14 Asko Kauppi, Benoit Germain. All rights reserved.</i> |
68 | <br> | 68 | <br/> |
69 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1 and 5.2. | 69 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1 and 5.2. |
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 20-Jan-14, and applies to version <tt>3.8.1</tt>. | 73 | This document was revised on 22-Jan-14, and applies to version <tt>3.8.2</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -386,7 +386,7 @@ | |||
386 | 386 | ||
387 | <p> | 387 | <p> |
388 | (Since v3.5.0) Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through <a href="#lindas">lindas</a>. | 388 | (Since v3.5.0) Once Lanes is configured, one should register with Lanes the modules exporting functions that will be transferred either during lane generation or through <a href="#lindas">lindas</a>. |
389 | <br> | 389 | <br/> |
390 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. | 390 | Use <tt>lanes.require()</tt> for this purpose. This will call the original <tt>require()</tt>, then add the result to the lookup databases. |
391 | </p> | 391 | </p> |
392 | 392 | ||
@@ -418,7 +418,7 @@ | |||
418 | </td> | 418 | </td> |
419 | </tr> | 419 | </tr> |
420 | </table> | 420 | </table> |
421 | <br> | 421 | <br/> |
422 | <table border=1 bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | 422 | <table border=1 bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> |
423 | <tr> | 423 | <tr> |
424 | <td> | 424 | <td> |
@@ -596,10 +596,19 @@ | |||
596 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. | 596 | These tables are built from the modules listed here. <tt>required</tt> must be a list of strings, each one being the name of a module to be required. Each module is required with <tt>require()</tt> before the lanes function is invoked. |
597 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. | 597 | So, from the required module's point of view, requiring it manually from inside the lane body or having it required this way doesn't change anything. From the lane body's point of view, the only difference is that a module not creating a global won't be accessible. |
598 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). | 598 | Therefore, a lane body will also have to require a module manually, but this won't do anything more (see Lua's <tt>require</tt> documentation). |
599 | <br> | 599 | <br/> |
600 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. | 600 | ATTEMPTING TO TRANSFER A FUNCTION REGISTERED BY A MODULE NOT LISTED HERE WILL RAISE AN ERROR. |
601 | </td> | 601 | </td> |
602 | </tr> | 602 | </tr> |
603 | <tr id="Tr1" valign=top> | ||
604 | <td> | ||
605 | <code>.gc_cb</code> | ||
606 | </td> | ||
607 | <td>function</td> | ||
608 | <td> | ||
609 | (Since version 3.8.2) Callback that gets invoked when the lane is garbage collected. The function receives two arguments (the lane name and a string, either <tt>"closed"</tt> or <tt>"selfdestruct"</tt>). | ||
610 | </td> | ||
611 | </tr> | ||
603 | <tr valign=top> | 612 | <tr valign=top> |
604 | <td> | 613 | <td> |
605 | <code>.priority</code> | 614 | <code>.priority</code> |
@@ -608,9 +617,9 @@ | |||
608 | <td> | 617 | <td> |
609 | The priority of lanes generated in the range -3..+3 (default is 0). | 618 | The priority of lanes generated in the range -3..+3 (default is 0). |
610 | These values are a mapping over the actual priority range of the underlying implementation. | 619 | These values are a mapping over the actual priority range of the underlying implementation. |
611 | <br> | 620 | <br/> |
612 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. | 621 | Implementation and dependability of priorities varies by platform. Especially Linux kernel 2.6 is not supporting priorities in user mode. |
613 | <br> | 622 | <br/> |
614 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. | 623 | A lane can also change its own thread priority dynamically with <a href="#priority"><tt>lanes.set_thread_priority()</tt></a>. |
615 | </td> | 624 | </td> |
616 | </tr> | 625 | </tr> |
@@ -621,9 +630,9 @@ | |||
621 | <td> table</td> | 630 | <td> table</td> |
622 | <td> | 631 | <td> |
623 | Introduced at version 3.0. | 632 | Introduced at version 3.0. |
624 | <br> | 633 | <br/> |
625 | Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. | 634 | Specifying it when <code>libs_str</code> doesn't cause the <code>package</code> library to be loaded will generate an error. |
626 | <br> | 635 | <br/> |
627 | If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> (Lua 5.1)/<tt>searchers</tt> (Lua 5.2) are transfered. | 636 | If not specified, the created lane will receive the current values of <tt>package</tt>. Only <tt>path</tt>, <tt>cpath</tt>, <tt>preload</tt> and <tt>loaders</tt> (Lua 5.1)/<tt>searchers</tt> (Lua 5.2) are transfered. |
628 | </td> | 637 | </td> |
629 | </tr> | 638 | </tr> |
@@ -678,7 +687,7 @@ | |||
678 | </table> | 687 | </table> |
679 | <p> | 688 | <p> |
680 | Besides setting a default priority in the generator <a href="#generator_settings">settings</a>, each thread can change its own priority at will. This is also true for the main Lua state. | 689 | Besides setting a default priority in the generator <a href="#generator_settings">settings</a>, each thread can change its own priority at will. This is also true for the main Lua state. |
681 | <br> | 690 | <br/> |
682 | The priority must be in the range <tt>[-3,+3]</tt>. | 691 | The priority must be in the range <tt>[-3,+3]</tt>. |
683 | </p> | 692 | </p> |
684 | 693 | ||
@@ -793,7 +802,7 @@ | |||
793 | 802 | ||
794 | <p> | 803 | <p> |
795 | Only available if lane tracking feature is compiled (see <tt>HAVE_LANE_TRACKING</tt> in <tt>lanes.c</tt>) and <a href="#track_lanes"><tt>track_lanes</tt></a> is set. | 804 | Only available if lane tracking feature is compiled (see <tt>HAVE_LANE_TRACKING</tt> in <tt>lanes.c</tt>) and <a href="#track_lanes"><tt>track_lanes</tt></a> is set. |
796 | <br> | 805 | <br/> |
797 | Returns a table where keys are a lane's name and values are the lane's status. Returns <tt>nil</tt> if no lane is running. | 806 | Returns a table where keys are a lane's name and values are the lane's status. Returns <tt>nil</tt> if no lane is running. |
798 | </p> | 807 | </p> |
799 | 808 | ||
@@ -820,7 +829,7 @@ | |||
820 | 829 | ||
821 | <p> | 830 | <p> |
822 | Makes sure lane has finished, and gives its first (maybe only) return value. Other return values will be available in other <tt>lane_h</tt> indices. | 831 | Makes sure lane has finished, and gives its first (maybe only) return value. Other return values will be available in other <tt>lane_h</tt> indices. |
823 | <br> | 832 | <br/> |
824 | If the lane ended in an error, it is propagated to master state at this place. | 833 | If the lane ended in an error, it is propagated to master state at this place. |
825 | </p> | 834 | </p> |
826 | 835 | ||
@@ -835,9 +844,9 @@ | |||
835 | 844 | ||
836 | <p> | 845 | <p> |
837 | <tt>stack_tbl</tt> is a table describing where the error was thrown. | 846 | <tt>stack_tbl</tt> is a table describing where the error was thrown. |
838 | <br> | 847 | <br/> |
839 | In <tt>"extended"</tt> mode, <tt>stack_tbl</tt> is an array of tables containing info gathered with <tt>lua_getinfo()</tt> (<tt>"source"</tt>,<tt>"currentline"</tt>,<tt>"name"</tt>,<tt>"namewhat"</tt>,<tt>"what"</tt>). | 848 | In <tt>"extended"</tt> mode, <tt>stack_tbl</tt> is an array of tables containing info gathered with <tt>lua_getinfo()</tt> (<tt>"source"</tt>,<tt>"currentline"</tt>,<tt>"name"</tt>,<tt>"namewhat"</tt>,<tt>"what"</tt>). |
840 | <br> | 849 | <br/> |
841 | In <tt>"basic mode"</tt>, <tt>stack_tbl</tt> is an array of "<filename>:<line>" strings. Use <tt>table.concat()</tt> to format it to your liking (or just ignore it). | 850 | In <tt>"basic mode"</tt>, <tt>stack_tbl</tt> is an array of "<filename>:<line>" strings. Use <tt>table.concat()</tt> to format it to your liking (or just ignore it). |
842 | </p> | 851 | </p> |
843 | 852 | ||
@@ -908,11 +917,11 @@ | |||
908 | 917 | ||
909 | <p> | 918 | <p> |
910 | Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>or <tt>send()</tt> call is awakened. | 919 | Cancellation is tested <u>before</u> going to sleep in <tt>receive()</tt> or <tt>send()</tt> calls and after executing <tt>cancelstep</tt> Lua statements. Starting with version 3.0-beta, a pending <tt>receive()</tt>or <tt>send()</tt> call is awakened. |
911 | <br> | 920 | <br/> |
912 | This means the execution of the lane will resume although the operation has not completed, to give the lane a chance to detect cancellation (even in the case the code waits on a <a href="#lindas">linda</a> with infinite timeout). | 921 | This means the execution of the lane will resume although the operation has not completed, to give the lane a chance to detect cancellation (even in the case the code waits on a <a href="#lindas">linda</a> with infinite timeout). |
913 | <br> | 922 | <br/> |
914 | The code should be able to handle this situation appropriately if required (in other words, it should gracefully handle the fact that it didn't receive the expected values). | 923 | The code should be able to handle this situation appropriately if required (in other words, it should gracefully handle the fact that it didn't receive the expected values). |
915 | <br> | 924 | <br/> |
916 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. | 925 | It is also possible to manually test for cancel requests with <tt>cancel_test()</tt>. |
917 | </p> | 926 | </p> |
918 | 927 | ||
@@ -1103,13 +1112,13 @@ | |||
1103 | 1112 | ||
1104 | <p> | 1113 | <p> |
1105 | Returns some information about the contents of the linda. | 1114 | Returns some information about the contents of the linda. |
1106 | <br> | 1115 | <br/> |
1107 | If no key is specified, and the linda is empty, returns nothing. | 1116 | If no key is specified, and the linda is empty, returns nothing. |
1108 | <br> | 1117 | <br/> |
1109 | If no key is specified, and the linda is not empty, returns a table of key/count pairs that counts the number of items in each of the exiting keys of the linda. This count can be 0 if the key has been used but is empty. | 1118 | If no key is specified, and the linda is not empty, returns a table of key/count pairs that counts the number of items in each of the exiting keys of the linda. This count can be 0 if the key has been used but is empty. |
1110 | <br> | 1119 | <br/> |
1111 | If a single key is specified, returns the number of pending items, or nothing if the key is unknown. | 1120 | If a single key is specified, returns the number of pending items, or nothing if the key is unknown. |
1112 | <br> | 1121 | <br/> |
1113 | If more than one key is specified, return a table of key/count pairs for the known keys. | 1122 | If more than one key is specified, return a table of key/count pairs for the known keys. |
1114 | </p> | 1123 | </p> |
1115 | 1124 | ||
@@ -1125,17 +1134,17 @@ | |||
1125 | 1134 | ||
1126 | <p> | 1135 | <p> |
1127 | A linda is a gateway to read and write data inside some hidden Lua states, called keeper states. Lindas are hashed to a fixed number of keeper states, which are a locking entity. | 1136 | A linda is a gateway to read and write data inside some hidden Lua states, called keeper states. Lindas are hashed to a fixed number of keeper states, which are a locking entity. |
1128 | <br> | 1137 | <br/> |
1129 | The data sent through a linda is stored inside the associated keeper state in a Lua table where each linda slot is the key to another table containing a FIFO for that slot. | 1138 | The data sent through a linda is stored inside the associated keeper state in a Lua table where each linda slot is the key to another table containing a FIFO for that slot. |
1130 | <br> | 1139 | <br/> |
1131 | Each keeper state is associated with an OS mutex, to prevent concurrent access to the keeper state. The linda itself uses two signals to be made aware of operations occuring on it. | 1140 | Each keeper state is associated with an OS mutex, to prevent concurrent access to the keeper state. The linda itself uses two signals to be made aware of operations occuring on it. |
1132 | <br> | 1141 | <br/> |
1133 | Whenever Lua code reads from or writes to a linda, the mutex is acquired. If linda limits don't block the operation, it is fulfilled, then the mutex is released. | 1142 | Whenever Lua code reads from or writes to a linda, the mutex is acquired. If linda limits don't block the operation, it is fulfilled, then the mutex is released. |
1134 | <br> | 1143 | <br/> |
1135 | If the linda has to block, the mutex is released and the OS thread sleeps, waiting for a linda operation to be signalled. When an operation occurs on the same linda, possibly fufilling the condition, or a timeout expires, the thread wakes up. | 1144 | If the linda has to block, the mutex is released and the OS thread sleeps, waiting for a linda operation to be signalled. When an operation occurs on the same linda, possibly fufilling the condition, or a timeout expires, the thread wakes up. |
1136 | <br> | 1145 | <br/> |
1137 | If the thread is woken but the condition is not yet fulfilled, it goes back to sleep, until the timeout expires. | 1146 | If the thread is woken but the condition is not yet fulfilled, it goes back to sleep, until the timeout expires. |
1138 | <br> | 1147 | <br/> |
1139 | When a lane is cancelled, the signal it is waiting on (if any) is signalled. In that case, the linda operation will return no data. | 1148 | When a lane is cancelled, the signal it is waiting on (if any) is signalled. In that case, the linda operation will return no data. |
1140 | </p> | 1149 | </p> |
1141 | 1150 | ||
@@ -1264,9 +1273,9 @@ events to a common Linda, but... :).</font> | |||
1264 | 1273 | ||
1265 | <p> | 1274 | <p> |
1266 | The generated function acquires M tokens from the N available, or releases them if the value is negative. The acquiring call will suspend the lane, if necessary. Use <tt>M=N=1</tt> for a critical section lock (only one lane allowed to enter). | 1275 | The generated function acquires M tokens from the N available, or releases them if the value is negative. The acquiring call will suspend the lane, if necessary. Use <tt>M=N=1</tt> for a critical section lock (only one lane allowed to enter). |
1267 | <br> | 1276 | <br/> |
1268 | When passsing <tt>"try"</tt> as second argument when acquiring, then <tt>lock_func</tt> operates on the linda with a timeout of 0 to emulate a TryLock() operation. If locking fails, <tt>lock_func</tt> returns <tt>false</tt>. <tt>"try"</tt> is ignored when releasing (as it it not expected to ever have to wait unless the acquisition/release pairs are not properly matched). | 1277 | When passsing <tt>"try"</tt> as second argument when acquiring, then <tt>lock_func</tt> operates on the linda with a timeout of 0 to emulate a TryLock() operation. If locking fails, <tt>lock_func</tt> returns <tt>false</tt>. <tt>"try"</tt> is ignored when releasing (as it it not expected to ever have to wait unless the acquisition/release pairs are not properly matched). |
1269 | <br> | 1278 | <br/> |
1270 | Upon successful lock/unlock, <tt>lock_func</tt> returns <tt>true</tt> (always the case when block-waiting for completion). | 1279 | Upon successful lock/unlock, <tt>lock_func</tt> returns <tt>true</tt> (always the case when block-waiting for completion). |
1271 | </p> | 1280 | </p> |
1272 | 1281 | ||
@@ -1364,7 +1373,7 @@ events to a common Linda, but... :).</font> | |||
1364 | 1373 | ||
1365 | <p> | 1374 | <p> |
1366 | This has the main drawback of not being LuaJIT-compatible, because some functions registered by LuaJIT are not regular C functions, but specially optimized implementations. As a result, <tt>lua_tocfunction()</tt> returns <tt>NULL</tt> for them. | 1375 | This has the main drawback of not being LuaJIT-compatible, because some functions registered by LuaJIT are not regular C functions, but specially optimized implementations. As a result, <tt>lua_tocfunction()</tt> returns <tt>NULL</tt> for them. |
1367 | <br> | 1376 | <br/> |
1368 | Therefore, Lanes no longer transfers functions that way. Instead, functions are transfered as follows (more or less): | 1377 | Therefore, Lanes no longer transfers functions that way. Instead, functions are transfered as follows (more or less): |
1369 | </p> | 1378 | </p> |
1370 | 1379 | ||
@@ -1385,15 +1394,15 @@ events to a common Linda, but... :).</font> | |||
1385 | 1394 | ||
1386 | <p> | 1395 | <p> |
1387 | Since functions are first class values, they don't have a name. All we know for sure is that when a C module registers some functions, they are accessible to the script that required the module through some exposed variables. | 1396 | Since functions are first class values, they don't have a name. All we know for sure is that when a C module registers some functions, they are accessible to the script that required the module through some exposed variables. |
1388 | <br> | 1397 | <br/> |
1389 | For example, loading the <tt>string</tt> base library creates a table accessible when indexing the global environment with key <tt>"string"</tt>. Indexing this table with <tt>"match"</tt>, <tt>"gsub"</tt>, etc. will give us a function. | 1398 | For example, loading the <tt>string</tt> base library creates a table accessible when indexing the global environment with key <tt>"string"</tt>. Indexing this table with <tt>"match"</tt>, <tt>"gsub"</tt>, etc. will give us a function. |
1390 | <br> | 1399 | <br/> |
1391 | When a lane generator creates a lane and performs initializations described by the list of base libraries and the list of required modules, it recursively scans the table created by the initialisation of the module, looking for all values that are C functions. | 1400 | When a lane generator creates a lane and performs initializations described by the list of base libraries and the list of required modules, it recursively scans the table created by the initialisation of the module, looking for all values that are C functions. |
1392 | <br> | 1401 | <br/> |
1393 | Each time a function is encountered, the sequence of keys that reached that function is contatenated in a (hopefully) unique name. The [name, function] and [function, name] pairs are both stored in a lookup table in all involved Lua states (main Lua state and lanes states). | 1402 | Each time a function is encountered, the sequence of keys that reached that function is contatenated in a (hopefully) unique name. The [name, function] and [function, name] pairs are both stored in a lookup table in all involved Lua states (main Lua state and lanes states). |
1394 | <br> | 1403 | <br/> |
1395 | Then when a function is transfered from one state to another, all we have to do is retrieve the name associated to a function in the source Lua state, then with that name retrieve the equivalent function that already exists in the destination state. | 1404 | Then when a function is transfered from one state to another, all we have to do is retrieve the name associated to a function in the source Lua state, then with that name retrieve the equivalent function that already exists in the destination state. |
1396 | <br> | 1405 | <br/> |
1397 | Note that there is no need to transfer upvalues, as they are already bound to the function registered in the destination state. (And in any event, it is not possible to create a closure from a C function pushed on the stack, it can only be created with a <tt>lua_CFunction</tt> pointer). | 1406 | Note that there is no need to transfer upvalues, as they are already bound to the function registered in the destination state. (And in any event, it is not possible to create a closure from a C function pushed on the stack, it can only be created with a <tt>lua_CFunction</tt> pointer). |
1398 | </p> | 1407 | </p> |
1399 | 1408 | ||
diff --git a/src/lanes.c b/src/lanes.c index 604e43d..a806c16 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.8.1"; | 55 | char const* VERSION = "3.8.2"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -209,6 +209,19 @@ static inline struct s_lane* get_lane_from_registry( lua_State* L) | |||
209 | return s; | 209 | return s; |
210 | } | 210 | } |
211 | 211 | ||
212 | // intern the debug name in the specified lua state so that the pointer remains valid when the lane's state is closed | ||
213 | static void securize_debug_threadname( lua_State* L, struct s_lane* s) | ||
214 | { | ||
215 | STACK_CHECK( L); | ||
216 | STACK_GROW( L, 3); | ||
217 | lua_getuservalue( L, 1); | ||
218 | lua_newtable( L); | ||
219 | s->debug_name = lua_pushstring( L, s->debug_name); | ||
220 | lua_rawset( L, -3); | ||
221 | lua_pop( L, 1); | ||
222 | STACK_END( L, 0); | ||
223 | } | ||
224 | |||
212 | /* | 225 | /* |
213 | * Check if the thread in question ('L') has been signalled for cancel. | 226 | * Check if the thread in question ('L') has been signalled for cancel. |
214 | * | 227 | * |
@@ -1766,7 +1779,7 @@ LUAG_FUNC( get_debug_threadname) | |||
1766 | { | 1779 | { |
1767 | struct s_lane* const s = lua_toLane( L, 1); | 1780 | struct s_lane* const s = lua_toLane( L, 1); |
1768 | luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments"); | 1781 | luaL_argcheck( L, lua_gettop( L) == 1, 2, "too many arguments"); |
1769 | lua_pushstring( L, s->debug_name ? s->debug_name : "<unnamed>"); | 1782 | lua_pushstring( L, s->debug_name); |
1770 | return 1; | 1783 | return 1; |
1771 | } | 1784 | } |
1772 | 1785 | ||
@@ -1998,6 +2011,8 @@ LUAG_FUNC( require) | |||
1998 | return 1; | 2011 | return 1; |
1999 | } | 2012 | } |
2000 | 2013 | ||
2014 | LUAG_FUNC( thread_gc); | ||
2015 | #define GCCB_KEY (void*)LG_thread_gc | ||
2001 | //--- | 2016 | //--- |
2002 | // lane_ud= thread_new( function, [libs_str], | 2017 | // lane_ud= thread_new( function, [libs_str], |
2003 | // [cancelstep_uint=0], | 2018 | // [cancelstep_uint=0], |
@@ -2005,6 +2020,7 @@ LUAG_FUNC( require) | |||
2005 | // [globals_tbl], | 2020 | // [globals_tbl], |
2006 | // [package_tbl], | 2021 | // [package_tbl], |
2007 | // [required], | 2022 | // [required], |
2023 | // [gc_cb], | ||
2008 | // [... args ...] ) | 2024 | // [... args ...] ) |
2009 | // | 2025 | // |
2010 | // Upvalues: metatable to use for 'lane_ud' | 2026 | // Upvalues: metatable to use for 'lane_ud' |
@@ -2022,8 +2038,9 @@ LUAG_FUNC( thread_new) | |||
2022 | uint_t glob = lua_isnoneornil( L, 5) ? 0 : 5; | 2038 | uint_t glob = lua_isnoneornil( L, 5) ? 0 : 5; |
2023 | uint_t package = lua_isnoneornil( L, 6) ? 0 : 6; | 2039 | uint_t package = lua_isnoneornil( L, 6) ? 0 : 6; |
2024 | uint_t required = lua_isnoneornil( L, 7) ? 0 : 7; | 2040 | uint_t required = lua_isnoneornil( L, 7) ? 0 : 7; |
2041 | uint_t gc_cb = lua_isnoneornil( L, 8) ? 0 : 8; | ||
2025 | 2042 | ||
2026 | #define FIXED_ARGS 7 | 2043 | #define FIXED_ARGS 8 |
2027 | uint_t args = lua_gettop(L) - FIXED_ARGS; | 2044 | uint_t args = lua_gettop(L) - FIXED_ARGS; |
2028 | 2045 | ||
2029 | // public Lanes API accepts a generic range -3/+3 | 2046 | // public Lanes API accepts a generic range -3/+3 |
@@ -2203,7 +2220,7 @@ LUAG_FUNC( thread_new) | |||
2203 | } | 2220 | } |
2204 | STACK_MID( L, 0); | 2221 | STACK_MID( L, 0); |
2205 | 2222 | ||
2206 | ASSERT_L( (uint_t)lua_gettop( L2) == 1+args); | 2223 | ASSERT_L( (uint_t)lua_gettop( L2) == 1 + args); |
2207 | ASSERT_L( lua_isfunction( L2, 1)); | 2224 | ASSERT_L( lua_isfunction( L2, 1)); |
2208 | 2225 | ||
2209 | // 's' is allocated from heap, not Lua, since its life span may surpass | 2226 | // 's' is allocated from heap, not Lua, since its life span may surpass |
@@ -2217,9 +2234,9 @@ LUAG_FUNC( thread_new) | |||
2217 | 2234 | ||
2218 | //memset( s, 0, sizeof(struct s_lane) ); | 2235 | //memset( s, 0, sizeof(struct s_lane) ); |
2219 | s->L = L2; | 2236 | s->L = L2; |
2220 | s->status= PENDING; | 2237 | s->status = PENDING; |
2221 | s->waiting_on = NULL; | 2238 | s->waiting_on = NULL; |
2222 | s->debug_name = NULL; | 2239 | s->debug_name = "<unnamed>"; |
2223 | s->cancel_request = CANCEL_NONE; | 2240 | s->cancel_request = CANCEL_NONE; |
2224 | 2241 | ||
2225 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR | 2242 | #if THREADWAIT_METHOD == THREADWAIT_CONDVAR |
@@ -2238,25 +2255,32 @@ LUAG_FUNC( thread_new) | |||
2238 | lua_setmetatable( L, -2); | 2255 | lua_setmetatable( L, -2); |
2239 | STACK_MID( L, 1); | 2256 | STACK_MID( L, 1); |
2240 | 2257 | ||
2241 | // Clear environment for the userdata | 2258 | // Create uservalue for the userdata |
2242 | // | 2259 | // (this is where lane body return values will be stored when the handle is indexed by a numeric key) |
2243 | lua_newtable( L); | 2260 | lua_newtable( L); |
2261 | |||
2262 | // Store the gc_cb callback in the uservalue | ||
2263 | if( gc_cb > 0) | ||
2264 | { | ||
2265 | lua_pushlightuserdata( L, GCCB_KEY); | ||
2266 | lua_pushvalue( L, gc_cb); | ||
2267 | lua_rawset( L, -3); | ||
2268 | } | ||
2269 | |||
2244 | lua_setuservalue( L, -2); | 2270 | lua_setuservalue( L, -2); |
2245 | 2271 | ||
2246 | // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still | 2272 | // Store 's' in the lane's registry, for 'cancel_test()' (even if 'cs'==0 we still do cancel tests at pending send/receive). |
2247 | // do cancel tests at pending send/receive). | ||
2248 | // | ||
2249 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY); | 2273 | lua_pushlightuserdata( L2, CANCEL_TEST_KEY); |
2250 | lua_pushlightuserdata( L2, s); | 2274 | lua_pushlightuserdata( L2, s); |
2251 | lua_rawset( L2, LUA_REGISTRYINDEX); | 2275 | lua_rawset( L2, LUA_REGISTRYINDEX); |
2252 | 2276 | ||
2253 | if( cs) | 2277 | if( cs) |
2254 | { | 2278 | { |
2255 | lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs ); | 2279 | lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs); |
2256 | } | 2280 | } |
2257 | 2281 | ||
2258 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: launching thread\n" INDENT_END)); | 2282 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "thread_new: launching thread\n" INDENT_END)); |
2259 | THREAD_CREATE( &s->thread, lane_main, s, prio ); | 2283 | THREAD_CREATE( &s->thread, lane_main, s, prio); |
2260 | STACK_END( L, 1); | 2284 | STACK_END( L, 1); |
2261 | 2285 | ||
2262 | DEBUGSPEW_CODE( -- debugspew_indent_depth); | 2286 | DEBUGSPEW_CODE( -- debugspew_indent_depth); |
@@ -2279,7 +2303,23 @@ LUAG_FUNC( thread_new) | |||
2279 | // | 2303 | // |
2280 | LUAG_FUNC( thread_gc) | 2304 | LUAG_FUNC( thread_gc) |
2281 | { | 2305 | { |
2282 | struct s_lane* s = lua_toLane( L, 1); | 2306 | bool_t have_gc_cb = FALSE; |
2307 | struct s_lane* s = lua_toLane( L, 1); // ud | ||
2308 | |||
2309 | // if there a gc callback? | ||
2310 | lua_getuservalue( L, 1); // ud uservalue | ||
2311 | lua_pushlightuserdata( L, GCCB_KEY); // ud uservalue __gc | ||
2312 | lua_rawget( L, -2); // ud uservalue gc_cb|nil | ||
2313 | if( !lua_isnil( L, -1)) | ||
2314 | { | ||
2315 | lua_remove( L, -2); // ud gc_cb|nil | ||
2316 | lua_pushstring( L, s->debug_name); // ud gc_cb name | ||
2317 | have_gc_cb = TRUE; | ||
2318 | } | ||
2319 | else | ||
2320 | { | ||
2321 | lua_pop( L, 2); // ud | ||
2322 | } | ||
2283 | 2323 | ||
2284 | // We can read 's->status' without locks, but not wait for it | 2324 | // We can read 's->status' without locks, but not wait for it |
2285 | // test KILLED state first, as it doesn't need to enter the selfdestruct chain | 2325 | // test KILLED state first, as it doesn't need to enter the selfdestruct chain |
@@ -2291,13 +2331,17 @@ LUAG_FUNC( thread_gc) | |||
2291 | DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **")); | 2331 | DEBUGSPEW_CODE( fprintf( stderr, "** Joining with a killed thread (needs testing) **")); |
2292 | // make sure the thread is no longer running, just like thread_join() | 2332 | // make sure the thread is no longer running, just like thread_join() |
2293 | if(! THREAD_ISNULL( s->thread)) | 2333 | if(! THREAD_ISNULL( s->thread)) |
2334 | { | ||
2294 | THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); | 2335 | THREAD_WAIT( &s->thread, -1, &s->done_signal, &s->done_lock, &s->status); |
2295 | // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing | 2336 | } |
2296 | // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen | ||
2297 | if( s->status >= DONE && s->L) | 2337 | if( s->status >= DONE && s->L) |
2298 | { | 2338 | { |
2339 | // we know the thread was killed while the Lua VM was not doing anything: we should be able to close it without crashing | ||
2340 | // now, thread_cancel() will not forcefully kill a lane with s->status >= DONE, so I am not sure it can ever happen | ||
2299 | lua_close( s->L); | 2341 | lua_close( s->L); |
2300 | s->L = 0; | 2342 | s->L = 0; |
2343 | // just in case, but s will be freed soon so... | ||
2344 | s->debug_name = "<gc>"; | ||
2301 | } | 2345 | } |
2302 | DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **")); | 2346 | DEBUGSPEW_CODE( fprintf( stderr, "** Joined ok **")); |
2303 | } | 2347 | } |
@@ -2306,18 +2350,31 @@ LUAG_FUNC( thread_gc) | |||
2306 | // still running: will have to be cleaned up later | 2350 | // still running: will have to be cleaned up later |
2307 | selfdestruct_add( s); | 2351 | selfdestruct_add( s); |
2308 | assert( s->selfdestruct_next); | 2352 | assert( s->selfdestruct_next); |
2353 | if( have_gc_cb) | ||
2354 | { | ||
2355 | lua_pushliteral( L, "selfdestruct"); // ud gc_cb name status | ||
2356 | lua_call( L, 2, 0); // ud | ||
2357 | } | ||
2309 | return 0; | 2358 | return 0; |
2310 | |||
2311 | } | 2359 | } |
2312 | else if( s->L) | 2360 | else if( s->L) |
2313 | { | 2361 | { |
2314 | // no longer accessing the Lua VM: we can close right now | 2362 | // no longer accessing the Lua VM: we can close right now |
2315 | lua_close( s->L); | 2363 | lua_close( s->L); |
2316 | s->L = 0; | 2364 | s->L = 0; |
2365 | // just in case, but s will be freed soon so... | ||
2366 | s->debug_name = "<gc>"; | ||
2317 | } | 2367 | } |
2318 | 2368 | ||
2319 | // Clean up after a (finished) thread | 2369 | // Clean up after a (finished) thread |
2320 | lane_cleanup( s); | 2370 | lane_cleanup( s); |
2371 | |||
2372 | // do this after lane cleanup in case the callback triggers an error | ||
2373 | if( have_gc_cb) | ||
2374 | { | ||
2375 | lua_pushliteral( L, "closed"); // ud gc_cb name status | ||
2376 | lua_call( L, 2, 0); // ud | ||
2377 | } | ||
2321 | return 0; | 2378 | return 0; |
2322 | } | 2379 | } |
2323 | 2380 | ||
@@ -2422,23 +2479,26 @@ LUAG_FUNC( thread_join) | |||
2422 | bool_t done; | 2479 | bool_t done; |
2423 | 2480 | ||
2424 | done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); | 2481 | done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal, &s->done_lock, &s->status); |
2425 | if (!done || !L2) | 2482 | if( !done || !L2) |
2483 | { | ||
2426 | return 0; // timeout: pushes none, leaves 'L2' alive | 2484 | return 0; // timeout: pushes none, leaves 'L2' alive |
2485 | } | ||
2427 | 2486 | ||
2428 | // Thread is DONE/ERROR_ST/CANCELLED; all ours now | 2487 | // Thread is DONE/ERROR_ST/CANCELLED; all ours now |
2429 | 2488 | ||
2430 | STACK_GROW( L, 1); | ||
2431 | |||
2432 | if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced | 2489 | if( s->mstatus == KILLED) // OS thread was killed if thread_cancel was forced |
2433 | { | 2490 | { |
2434 | // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values | 2491 | // in that case, even if the thread was killed while DONE/ERROR_ST/CANCELLED, ignore regular return values |
2435 | 2492 | STACK_GROW( L, 1); | |
2436 | lua_pushnil( L); | 2493 | lua_pushnil( L); |
2437 | lua_pushliteral( L, "killed"); | 2494 | lua_pushliteral( L, "killed"); |
2438 | ret = 2; | 2495 | ret = 2; |
2439 | } | 2496 | } |
2440 | else | 2497 | else |
2441 | { | 2498 | { |
2499 | // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed | ||
2500 | // so store it in the userdata uservalue at a key that can't possibly collide | ||
2501 | securize_debug_threadname( L, s); | ||
2442 | switch( s->status) | 2502 | switch( s->status) |
2443 | { | 2503 | { |
2444 | case DONE: | 2504 | case DONE: |
@@ -2467,11 +2527,9 @@ LUAG_FUNC( thread_join) | |||
2467 | 2527 | ||
2468 | default: | 2528 | default: |
2469 | DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status)); | 2529 | DEBUGSPEW_CODE( fprintf( stderr, "Status: %d\n", s->status)); |
2470 | ASSERT_L( FALSE ); ret= 0; | 2530 | ASSERT_L( FALSE); ret = 0; |
2471 | } | 2531 | } |
2472 | lua_close( L2); | 2532 | lua_close( L2); |
2473 | // debug_name is a pointer to an interned string, that no longer exists when the state is closed | ||
2474 | s->debug_name = "<closed>"; | ||
2475 | } | 2533 | } |
2476 | s->L = 0; | 2534 | s->L = 0; |
2477 | 2535 | ||
@@ -2652,10 +2710,7 @@ LUAG_FUNC( threads) | |||
2652 | lua_newtable( L); // {} | 2710 | lua_newtable( L); // {} |
2653 | while( s != TRACKING_END) | 2711 | while( s != TRACKING_END) |
2654 | { | 2712 | { |
2655 | if( s->debug_name) | 2713 | lua_pushstring( L, s->debug_name); // {} "name" |
2656 | lua_pushstring( L, s->debug_name); // {} "name" | ||
2657 | else | ||
2658 | lua_pushfstring( L, "Lane %p", s); // {} "name" | ||
2659 | push_thread_status( L, s); // {} "name" "status" | 2714 | push_thread_status( L, s); // {} "name" "status" |
2660 | lua_rawset( L, -3); // {} | 2715 | lua_rawset( L, -3); // {} |
2661 | s = s->tracking_next; | 2716 | s = s->tracking_next; |
diff --git a/src/lanes.lua b/src/lanes.lua index 9a0287d..1286099 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -196,7 +196,10 @@ end | |||
196 | -- | 196 | -- |
197 | -- .globals: table of globals to set for a new thread (passed by value) | 197 | -- .globals: table of globals to set for a new thread (passed by value) |
198 | -- | 198 | -- |
199 | -- .required: table of packages to require | 199 | -- .required: table of packages to require |
200 | -- | ||
201 | -- .gc_cb: function called when the lane handle is collected | ||
202 | -- | ||
200 | -- ... (more options may be introduced later) ... | 203 | -- ... (more options may be introduced later) ... |
201 | -- | 204 | -- |
202 | -- Calling with a function parameter ('lane_func') ends the string/table | 205 | -- Calling with a function parameter ('lane_func') ends the string/table |
@@ -272,10 +275,11 @@ local function gen( ... ) | |||
272 | end | 275 | end |
273 | end | 276 | end |
274 | 277 | ||
275 | local prio, cs, g_tbl, package_tbl, required | 278 | local prio, cs, g_tbl, package_tbl, required, gc_cb |
276 | 279 | ||
277 | for k,v in pairs(opt) do | 280 | for k,v in pairs(opt) do |
278 | if k=="priority" then prio= v | 281 | if k == "priority" then |
282 | prio = (type( v) == "number") and v or error( "Bad 'prio' option: expecting number, got " .. type( v), lev) | ||
279 | elseif k=="cancelstep" then | 283 | elseif k=="cancelstep" then |
280 | cs = (v==true) and 100 or | 284 | cs = (v==true) and 100 or |
281 | (v==false) and 0 or | 285 | (v==false) and 0 or |
@@ -286,6 +290,8 @@ local function gen( ... ) | |||
286 | package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev) | 290 | package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev) |
287 | elseif k=="required" then | 291 | elseif k=="required" then |
288 | required= (type( v) == "table") and v or error( "Bad 'required' option: expecting table, got " .. type( v), lev) | 292 | required= (type( v) == "table") and v or error( "Bad 'required' option: expecting table, got " .. type( v), lev) |
293 | elseif k == "gc_cb" then | ||
294 | gc_cb = (type( v) == "function") and v or error( "Bad 'gc_cb' option: expecting function, got " .. type( v), lev) | ||
289 | --.. | 295 | --.. |
290 | elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) | 296 | elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) |
291 | else error( "Bad option: ".. tostring(k), lev ) | 297 | else error( "Bad option: ".. tostring(k), lev ) |
@@ -296,7 +302,7 @@ local function gen( ... ) | |||
296 | -- Lane generator | 302 | -- Lane generator |
297 | -- | 303 | -- |
298 | return function(...) | 304 | return function(...) |
299 | return thread_new( func, libs, cs, prio, g_tbl, package_tbl, required, ...) -- args | 305 | return thread_new( func, libs, cs, prio, g_tbl, package_tbl, required, gc_cb, ...) -- args |
300 | end | 306 | end |
301 | end | 307 | end |
302 | 308 | ||
diff --git a/tests/basic.lua b/tests/basic.lua index 5b0d8a7..16919a3 100644 --- a/tests/basic.lua +++ b/tests/basic.lua | |||
@@ -25,6 +25,11 @@ local function PRINT(...) | |||
25 | end | 25 | end |
26 | end | 26 | end |
27 | 27 | ||
28 | local gc_cb = function( name_, status_) | ||
29 | PRINT( " ---> lane '" .. name_ .. "' collected with status " .. status_) | ||
30 | end | ||
31 | --gc_cb = nil | ||
32 | |||
28 | 33 | ||
29 | ---=== Local helpers ===--- | 34 | ---=== Local helpers ===--- |
30 | 35 | ||
@@ -71,7 +76,7 @@ local function task( a, b, c ) | |||
71 | return v, hey | 76 | return v, hey |
72 | end | 77 | end |
73 | 78 | ||
74 | local task_launch= lanes_gen( "", { globals={hey=true} }, task ) | 79 | local task_launch= lanes_gen( "", { globals={hey=true}, gc_cb = gc_cb}, task ) |
75 | -- base stdlibs, normal priority | 80 | -- base stdlibs, normal priority |
76 | 81 | ||
77 | -- 'task_launch' is a factory of multithreaded tasks, we can launch several: | 82 | -- 'task_launch' is a factory of multithreaded tasks, we can launch several: |
@@ -100,6 +105,8 @@ assert( v2_hey == true ) | |||
100 | 105 | ||
101 | assert( lane1.status == "done" ) | 106 | assert( lane1.status == "done" ) |
102 | assert( lane1.status == "done" ) | 107 | assert( lane1.status == "done" ) |
108 | lane1, lane2 = nil | ||
109 | collectgarbage() | ||
103 | 110 | ||
104 | --############################################################## | 111 | --############################################################## |
105 | --############################################################## | 112 | --############################################################## |
@@ -107,7 +114,7 @@ assert( lane1.status == "done" ) | |||
107 | 114 | ||
108 | PRINT( "\n\n", "---=== Tasking (cancelling) ===---", "\n\n") | 115 | PRINT( "\n\n", "---=== Tasking (cancelling) ===---", "\n\n") |
109 | 116 | ||
110 | local task_launch2= lanes_gen( "", { cancelstep=100, globals={hey=true} }, task ) | 117 | local task_launch2= lanes_gen( "", { cancelstep=100, globals={hey=true}, gc_cb = gc_cb}, task ) |
111 | 118 | ||
112 | local N=999999999 | 119 | local N=999999999 |
113 | local lane9= task_launch2(1,N,1) -- huuuuuuge... | 120 | local lane9= task_launch2(1,N,1) -- huuuuuuge... |
@@ -196,7 +203,7 @@ PRINT( "\n\n", "---=== Communications ===---", "\n\n") | |||
196 | local function WR(...) io.stderr:write(...) end | 203 | local function WR(...) io.stderr:write(...) end |
197 | 204 | ||
198 | local chunk= function( linda ) | 205 | local chunk= function( linda ) |
199 | 206 | set_debug_threadname "chunk" | |
200 | local function receive() return linda:receive( "->" ) end | 207 | local function receive() return linda:receive( "->" ) end |
201 | local function send(...) linda:send( "<-", ... ) end | 208 | local function send(...) linda:send( "<-", ... ) end |
202 | 209 | ||
@@ -226,7 +233,7 @@ local function PEEK() return linda:get("<-") end | |||
226 | local function SEND(...) linda:send( "->", ... ) end | 233 | local function SEND(...) linda:send( "->", ... ) end |
227 | local function RECEIVE() local k,v = linda:receive( 1, "<-" ) return v end | 234 | local function RECEIVE() local k,v = linda:receive( 1, "<-" ) return v end |
228 | 235 | ||
229 | local t= lanes_gen("io",chunk)(linda) -- prepare & launch | 236 | local t= lanes_gen("io", {gc_cb = gc_cb}, chunk)(linda) -- prepare & launch |
230 | 237 | ||
231 | SEND(1); WR( "1 sent\n" ) | 238 | SEND(1); WR( "1 sent\n" ) |
232 | SEND(2); WR( "2 sent\n" ) | 239 | SEND(2); WR( "2 sent\n" ) |
@@ -256,6 +263,8 @@ assert( tables_match( a, {'a','b','c',d=10} ) ) | |||
256 | assert( PEEK() == nil ) | 263 | assert( PEEK() == nil ) |
257 | SEND(4) | 264 | SEND(4) |
258 | 265 | ||
266 | t = nil | ||
267 | collectgarbage() | ||
259 | -- wait | 268 | -- wait |
260 | linda: receive( 1, "wait") | 269 | linda: receive( 1, "wait") |
261 | 270 | ||
@@ -266,6 +275,7 @@ linda: receive( 1, "wait") | |||
266 | PRINT( "\n\n", "---=== Stdlib naming ===---", "\n\n") | 275 | PRINT( "\n\n", "---=== Stdlib naming ===---", "\n\n") |
267 | 276 | ||
268 | local function dump_g( _x) | 277 | local function dump_g( _x) |
278 | set_debug_threadname "dump_g" | ||
269 | assert(print) | 279 | assert(print) |
270 | print( "### dumping _G for '" .. _x .. "'") | 280 | print( "### dumping _G for '" .. _x .. "'") |
271 | for k, v in pairs( _G) do | 281 | for k, v in pairs( _G) do |
@@ -275,6 +285,7 @@ local function dump_g( _x) | |||
275 | end | 285 | end |
276 | 286 | ||
277 | local function io_os_f( _x) | 287 | local function io_os_f( _x) |
288 | set_debug_threadname "io_os_f" | ||
278 | assert(print) | 289 | assert(print) |
279 | print( "### checking io and os libs existence for '" .. _x .. "'") | 290 | print( "### checking io and os libs existence for '" .. _x .. "'") |
280 | assert(io) | 291 | assert(io) |
@@ -283,13 +294,14 @@ local function io_os_f( _x) | |||
283 | end | 294 | end |
284 | 295 | ||
285 | local function coro_f( _x) | 296 | local function coro_f( _x) |
297 | set_debug_threadname "coro_f" | ||
286 | assert(print) | 298 | assert(print) |
287 | print( "### checking coroutine lib existence for '" .. _x .. "'") | 299 | print( "### checking coroutine lib existence for '" .. _x .. "'") |
288 | assert(coroutine) | 300 | assert(coroutine) |
289 | return true | 301 | return true |
290 | end | 302 | end |
291 | 303 | ||
292 | assert.fails( function() lanes_gen( "xxx", io_os_f ) end ) | 304 | assert.fails( function() lanes_gen( "xxx", {gc_cb = gc_cb}, io_os_f ) end ) |
293 | 305 | ||
294 | local stdlib_naming_tests = | 306 | local stdlib_naming_tests = |
295 | { | 307 | { |
@@ -305,10 +317,12 @@ local stdlib_naming_tests = | |||
305 | } | 317 | } |
306 | 318 | ||
307 | for _, t in ipairs( stdlib_naming_tests) do | 319 | for _, t in ipairs( stdlib_naming_tests) do |
308 | local f= lanes_gen( t[1], t[2]) -- any delimiter will do | 320 | local f= lanes_gen( t[1], {gc_cb = gc_cb}, t[2]) -- any delimiter will do |
309 | assert( f(t[1])[1] ) | 321 | assert( f(t[1])[1] ) |
310 | end | 322 | end |
311 | 323 | ||
324 | collectgarbage() | ||
325 | |||
312 | --############################################################## | 326 | --############################################################## |
313 | --############################################################## | 327 | --############################################################## |
314 | --############################################################## | 328 | --############################################################## |
@@ -317,9 +331,9 @@ PRINT( "\n\n", "---=== Comms criss cross ===---", "\n\n") | |||
317 | 331 | ||
318 | -- We make two identical lanes, which are using the same Linda channel. | 332 | -- We make two identical lanes, which are using the same Linda channel. |
319 | -- | 333 | -- |
320 | local tc= lanes_gen( "io", | 334 | local tc= lanes_gen( "io", {gc_cb = gc_cb}, |
321 | function( linda, ch_in, ch_out ) | 335 | function( linda, ch_in, ch_out ) |
322 | 336 | set_debug_threadname( "criss cross " .. ch_in .. " -> " .. ch_out) | |
323 | local function STAGE(str) | 337 | local function STAGE(str) |
324 | io.stderr:write( ch_in..": "..str.."\n" ) | 338 | io.stderr:write( ch_in..": "..str.."\n" ) |
325 | linda:send( nil, ch_out, str ) | 339 | linda:send( nil, ch_out, str ) |
@@ -338,6 +352,8 @@ local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twis | |||
338 | 352 | ||
339 | local _= a[1],b[1] -- waits until they are both ready | 353 | local _= a[1],b[1] -- waits until they are both ready |
340 | 354 | ||
355 | a, b = nil | ||
356 | collectgarbage() | ||
341 | 357 | ||
342 | --############################################################## | 358 | --############################################################## |
343 | --############################################################## | 359 | --############################################################## |
@@ -378,7 +394,7 @@ local function chunk2( linda ) | |||
378 | end | 394 | end |
379 | 395 | ||
380 | local linda= lanes.linda() | 396 | local linda= lanes.linda() |
381 | local t2= lanes_gen( "debug,string,io", chunk2 )(linda) -- prepare & launch | 397 | local t2= lanes_gen( "debug,string,io", {gc_cb = gc_cb}, chunk2 )(linda) -- prepare & launch |
382 | linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, | 398 | linda:send( "down", function(linda) linda:send( "up", "ready!" ) end, |
383 | "ok" ) | 399 | "ok" ) |
384 | -- wait to see if the tiny function gets executed | 400 | -- wait to see if the tiny function gets executed |
@@ -411,7 +427,7 @@ PRINT( "\n\n", "---=== :join test ===---", "\n\n") | |||
411 | -- (unless [1..n] has been read earlier, in which case it would seemingly | 427 | -- (unless [1..n] has been read earlier, in which case it would seemingly |
412 | -- work). | 428 | -- work). |
413 | 429 | ||
414 | local S= lanes_gen( "table", | 430 | local S= lanes_gen( "table", {gc_cb = gc_cb}, |
415 | function(arg) | 431 | function(arg) |
416 | set_debug_threadname "join test lane" | 432 | set_debug_threadname "join test lane" |
417 | set_finalizer( function() end) | 433 | set_finalizer( function() end) |