diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2021-06-16 18:41:14 +0200 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2021-06-16 18:41:14 +0200 |
commit | 5875fe44ba7a240dd101f954c7dc83337a0c2cef (patch) | |
tree | 5c53b56b750c135163d462a39217fb88f8741ed6 | |
parent | 21b2ac762ff8a5926872963aa2fd8feeb1bf3b39 (diff) | |
download | lanes-5875fe44ba7a240dd101f954c7dc83337a0c2cef.tar.gz lanes-5875fe44ba7a240dd101f954c7dc83337a0c2cef.tar.bz2 lanes-5875fe44ba7a240dd101f954c7dc83337a0c2cef.zip |
changed lanes.threads() output so that several lanes with the same name don't clobber each other in the result table
In the original implementations, the debug name was used as key, which meant that several lanes using the same name would cause only the oldest non-collected one to be listed in the results. Now the result is an array of tuples.
-rw-r--r-- | CHANGES | 22 | ||||
-rw-r--r-- | docs/index.html | 38 | ||||
-rw-r--r-- | src/lanes.c | 45 | ||||
-rw-r--r-- | src/lanes.h | 2 | ||||
-rw-r--r-- | tests/track_lanes.lua | 72 |
5 files changed, 130 insertions, 49 deletions
@@ -1,5 +1,9 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 147: BGe 16-Jun-21 | ||
4 | * changed lanes.threads() output so that several lanes with the same name don't clobber each other in the result table | ||
5 | * bumped version to 3.15 because of the API change | ||
6 | |||
3 | CHANGE 146: BGe 26-Apr-19 | 7 | CHANGE 146: BGe 26-Apr-19 |
4 | * lane:cancel() rework (see doc). | 8 | * lane:cancel() rework (see doc). |
5 | * opt.cancelstep is gone, hook is installed by lane:cancel() if requested | 9 | * opt.cancelstep is gone, hook is installed by lane:cancel() if requested |
@@ -662,15 +666,15 @@ CHANGE 6 (bug fix) AKa 15-Oct-2008: | |||
662 | Added local caches of the following to src/lanes.lua (was otherwise getting | 666 | Added local caches of the following to src/lanes.lua (was otherwise getting |
663 | errors at least in 'tests/irayo_recursive.lua'). | 667 | errors at least in 'tests/irayo_recursive.lua'). |
664 | 668 | ||
665 | local assert= assert | 669 | local assert= assert |
666 | local string_gmatch= assert( string.gmatch ) | 670 | local string_gmatch= assert( string.gmatch ) |
667 | local select= assert( select ) | 671 | local select= assert( select ) |
668 | local type= assert( type ) | 672 | local type= assert( type ) |
669 | local pairs= assert( pairs ) | 673 | local pairs= assert( pairs ) |
670 | local tostring= assert( tostring ) | 674 | local tostring= assert( tostring ) |
671 | local error= assert( error ) | 675 | local error= assert( error ) |
672 | local setmetatable= assert( setmetatable ) | 676 | local setmetatable= assert( setmetatable ) |
673 | local rawget= assert( rawget ) | 677 | local rawget= assert( rawget ) |
674 | 678 | ||
675 | Thanks to Irayo for detecting and reporting this. | 679 | Thanks to Irayo for detecting and reporting this. |
676 | 680 | ||
diff --git a/docs/index.html b/docs/index.html index 0b19223..49b862f 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -1,6 +1,6 @@ | |||
1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | 1 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
2 | <!-- | 2 | <!-- |
3 | Documentation for Lua Lanes | 3 | Documentation for Lua Lanes |
4 | --> | 4 | --> |
5 | 5 | ||
6 | <html> | 6 | <html> |
@@ -85,13 +85,13 @@ | |||
85 | Lua Lanes is a Lua extension library providing the possibility to run multiple Lua states in parallel. It is intended to be used for optimizing performance on multicore CPU's and to study ways to make Lua programs naturally parallel to begin with. | 85 | Lua Lanes is a Lua extension library providing the possibility to run multiple Lua states in parallel. It is intended to be used for optimizing performance on multicore CPU's and to study ways to make Lua programs naturally parallel to begin with. |
86 | </p> | 86 | </p> |
87 | <p> | 87 | <p> |
88 | Lanes is included into your software by the regular <tt>require "lanes"</tt> method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should work seamlessly together with the multiple lanes. | 88 | Lanes is included into your software by the regular <tt>require "lanes"</tt> method. No C side programming is needed; all APIs are Lua side, and most existing extension modules should work seamlessly together with the multiple lanes. |
89 | </p> | 89 | </p> |
90 | <p> | 90 | <p> |
91 | Starting with version 3.1.6, Lanes should build and run identically with either Lua 5.1 or Lua 5.2. Version 3.10.0 supports Lua 5.3. | 91 | Starting with version 3.1.6, Lanes should build and run identically with either Lua 5.1 or Lua 5.2. Version 3.10.0 supports Lua 5.3. |
92 | </p> | 92 | </p> |
93 | <p> | 93 | <p> |
94 | See <A HREF="comparison.html">comparison</A> of Lua Lanes with other Lua multithreading solutions. | 94 | See <A HREF="comparison.html">comparison</A> of Lua Lanes with other Lua multithreading solutions. |
95 | </p> | 95 | </p> |
96 | <p> | 96 | <p> |
97 | <h3>Features:</h3> | 97 | <h3>Features:</h3> |
@@ -134,11 +134,11 @@ | |||
134 | <li>Openwrt (15.05 and later)</li> | 134 | <li>Openwrt (15.05 and later)</li> |
135 | <li>Windows 2000/XP and later <font size="-1">(MinGW or Visual C++ 2005/2008)</font></li> | 135 | <li>Windows 2000/XP and later <font size="-1">(MinGW or Visual C++ 2005/2008)</font></li> |
136 | <!-- | 136 | <!-- |
137 | Other OS'es here once people help test them. (and the tester's name) | 137 | Other OS'es here once people help test them. (and the tester's name) |
138 | 138 | ||
139 | Win64, BSD, Linux x64, Linux embedded, QNX, Solaris, ... | 139 | Win64, BSD, Linux x64, Linux embedded, QNX, Solaris, ... |
140 | --> | 140 | --> |
141 | </ul> | 141 | </ul> |
142 | 142 | ||
143 | </p> | 143 | </p> |
144 | <p> | 144 | <p> |
@@ -843,7 +843,7 @@ | |||
843 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> | 843 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"> |
844 | <tr> | 844 | <tr> |
845 | <td> | 845 | <td> |
846 | <pre> {}|nil = lanes.threads()</pre> | 846 | <pre> {{name = "name", status = "status", ...}|nil = lanes.threads()</pre> |
847 | </td> | 847 | </td> |
848 | </tr> | 848 | </tr> |
849 | </table> | 849 | </table> |
@@ -851,7 +851,7 @@ | |||
851 | <p> | 851 | <p> |
852 | 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. | 852 | 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. |
853 | <br/> | 853 | <br/> |
854 | 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. | 854 | Returns an array table where each entry is a table containing a lane's name and status. Returns <tt>nil</tt> if no lane is running. |
855 | </p> | 855 | </p> |
856 | 856 | ||
857 | 857 | ||
@@ -891,7 +891,7 @@ | |||
891 | </p> | 891 | </p> |
892 | 892 | ||
893 | <p> | 893 | <p> |
894 | <tt>stack_tbl</tt> is a table describing where the error was thrown. | 894 | <tt>stack_tbl</tt> is a table describing where the error was thrown. |
895 | <br/> | 895 | <br/> |
896 | 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>). | 896 | 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>). |
897 | <br/> | 897 | <br/> |
@@ -1434,7 +1434,7 @@ events to a common Linda, but... :).</font> | |||
1434 | </ul> | 1434 | </ul> |
1435 | </li> | 1435 | </li> |
1436 | <li>Coroutines cannot be passed. A coroutine's Lua state is tied to the Lua state that created it, and there is no way the mixed C/Lua stack of a coroutine can be transfered from one Lua state to another.</li> | 1436 | <li>Coroutines cannot be passed. A coroutine's Lua state is tied to the Lua state that created it, and there is no way the mixed C/Lua stack of a coroutine can be transfered from one Lua state to another.</li> |
1437 | <li>Starting with version 3.10.1, if the metatable contains <tt>__lanesignore</tt>, the object is skipped and <tt>nil</tt> is transfered instead.</li> | 1437 | <li>Starting with version 3.10.1, if the metatable contains <tt>__lanesignore</tt>, the object is skipped and <tt>nil</tt> is transfered instead.</li> |
1438 | </ul> | 1438 | </ul> |
1439 | </p> | 1439 | </p> |
1440 | 1440 | ||
@@ -1575,7 +1575,7 @@ static int clonable_lanesclone( lua_State* L) | |||
1575 | </p> | 1575 | </p> |
1576 | 1576 | ||
1577 | <p> | 1577 | <p> |
1578 | <b>NOTE</b>: In the event the source userdata has uservalues, it is not necessary to create them for the clone, Lanes will handle their cloning.<br/> | 1578 | <b>NOTE</b>: In the event the source userdata has uservalues, it is not necessary to create them for the clone, Lanes will handle their cloning.<br/> |
1579 | Of course, more complex objects may require smarter cloning behavior than a simple <tt>memcpy</tt>. Also, the module initialisation code should make each metatable accessible from the module table itself as in: | 1579 | Of course, more complex objects may require smarter cloning behavior than a simple <tt>memcpy</tt>. Also, the module initialisation code should make each metatable accessible from the module table itself as in: |
1580 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | 1580 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> |
1581 | int luaopen_deep_test(lua_State* L) | 1581 | int luaopen_deep_test(lua_State* L) |
@@ -1645,13 +1645,13 @@ int luaD_new_clonable( lua_State* L) | |||
1645 | </p> | 1645 | </p> |
1646 | 1646 | ||
1647 | <p> | 1647 | <p> |
1648 | Deep userdata in transit inside keeper states (sent in a linda but not yet consumed) don't call <tt>idfunc(eDO_delete)</tt> and aren't considered by reference counting. The rationale is the following: | 1648 | Deep userdata in transit inside keeper states (sent in a linda but not yet consumed) don't call <tt>idfunc(eDO_delete)</tt> and aren't considered by reference counting. The rationale is the following: |
1649 | <br /> | 1649 | <br /> |
1650 | If some non-keeper state holds a deep userdata for some deep object, then even if the keeper collects its own deep userdata, it shouldn't be cleaned up since the refcount is not 0. | 1650 | If some non-keeper state holds a deep userdata for some deep object, then even if the keeper collects its own deep userdata, it shouldn't be cleaned up since the refcount is not 0. |
1651 | <br /> | 1651 | <br /> |
1652 | OTOH, if a keeper state holds the last deep userdata for some deep object, then no lane can do actual work with it. Deep userdata's <tt>idfunc()</tt> is never called from a keeper state. | 1652 | OTOH, if a keeper state holds the last deep userdata for some deep object, then no lane can do actual work with it. Deep userdata's <tt>idfunc()</tt> is never called from a keeper state. |
1653 | <br /> | 1653 | <br /> |
1654 | Therefore, Lanes can just call <tt>idfunc(eDO_delete)</tt> when the last non-keeper-held deep userdata is collected, as long as it doesn't do the same in a keeper state after that, since any remaining deep userdata in keeper states now hold stale pointers. | 1654 | Therefore, Lanes can just call <tt>idfunc(eDO_delete)</tt> when the last non-keeper-held deep userdata is collected, as long as it doesn't do the same in a keeper state after that, since any remaining deep userdata in keeper states now hold stale pointers. |
1655 | </p> | 1655 | </p> |
1656 | 1656 | ||
1657 | <p> | 1657 | <p> |
diff --git a/src/lanes.c b/src/lanes.c index 8f159a9..f3fdc76 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -1652,26 +1652,31 @@ LUAG_FUNC( thread_index) | |||
1652 | // Return a list of all known lanes | 1652 | // Return a list of all known lanes |
1653 | LUAG_FUNC( threads) | 1653 | LUAG_FUNC( threads) |
1654 | { | 1654 | { |
1655 | int const top = lua_gettop( L); | 1655 | int const top = lua_gettop( L); |
1656 | Universe* U = universe_get( L); | 1656 | Universe* U = universe_get( L); |
1657 | 1657 | ||
1658 | // List _all_ still running threads | 1658 | // List _all_ still running threads |
1659 | // | 1659 | // |
1660 | MUTEX_LOCK( &U->tracking_cs); | 1660 | MUTEX_LOCK( &U->tracking_cs); |
1661 | if( U->tracking_first && U->tracking_first != TRACKING_END) | 1661 | if( U->tracking_first && U->tracking_first != TRACKING_END) |
1662 | { | 1662 | { |
1663 | Lane* s = U->tracking_first; | 1663 | Lane* s = U->tracking_first; |
1664 | lua_newtable( L); // {} | 1664 | int index = 0; |
1665 | while( s != TRACKING_END) | 1665 | lua_newtable( L); // {} |
1666 | { | 1666 | while( s != TRACKING_END) |
1667 | lua_pushstring( L, s->debug_name); // {} "name" | 1667 | { |
1668 | push_thread_status( L, s); // {} "name" "status" | 1668 | // insert a { name, status } tuple, so that several lanes with the same name can't clobber each other |
1669 | lua_rawset( L, -3); // {} | 1669 | lua_newtable( L); // {} {} |
1670 | s = s->tracking_next; | 1670 | lua_pushstring( L, s->debug_name); // {} {} "name" |
1671 | } | 1671 | lua_setfield( L, -2, "name"); // {} {} |
1672 | } | 1672 | push_thread_status( L, s); // {} {} "status" |
1673 | MUTEX_UNLOCK( &U->tracking_cs); | 1673 | lua_setfield( L, -2, "status"); // {} {} |
1674 | return lua_gettop( L) - top; | 1674 | lua_rawseti( L, -2, ++ index); // {} |
1675 | s = s->tracking_next; | ||
1676 | } | ||
1677 | } | ||
1678 | MUTEX_UNLOCK( &U->tracking_cs); | ||
1679 | return lua_gettop( L) - top; // 0 or 1 | ||
1675 | } | 1680 | } |
1676 | #endif // HAVE_LANE_TRACKING | 1681 | #endif // HAVE_LANE_TRACKING |
1677 | 1682 | ||
diff --git a/src/lanes.h b/src/lanes.h index da0dd26..1a38ba5 100644 --- a/src/lanes.h +++ b/src/lanes.h | |||
@@ -11,7 +11,7 @@ | |||
11 | #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 11 | #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
12 | 12 | ||
13 | #define LANES_VERSION_MAJOR 3 | 13 | #define LANES_VERSION_MAJOR 3 |
14 | #define LANES_VERSION_MINOR 14 | 14 | #define LANES_VERSION_MINOR 15 |
15 | #define LANES_VERSION_PATCH 0 | 15 | #define LANES_VERSION_PATCH 0 |
16 | 16 | ||
17 | #define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH)))) | 17 | #define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH)))) |
diff --git a/tests/track_lanes.lua b/tests/track_lanes.lua new file mode 100644 index 0000000..46cfdad --- /dev/null +++ b/tests/track_lanes.lua | |||
@@ -0,0 +1,72 @@ | |||
1 | local lanes = require "lanes" .configure{ with_timers = false, track_lanes = true} | ||
2 | |||
3 | local wait | ||
4 | do | ||
5 | local linda = lanes.linda() | ||
6 | wait = function( seconds_) | ||
7 | linda:receive( seconds_, "dummy_key") | ||
8 | end | ||
9 | end | ||
10 | |||
11 | print "hello" | ||
12 | |||
13 | local track = function( title_) | ||
14 | print( title_) | ||
15 | for k, v in pairs( lanes.threads()) | ||
16 | do | ||
17 | print( k, v.name, v.status) | ||
18 | end | ||
19 | print( "\n") | ||
20 | end | ||
21 | |||
22 | local sleeper = function( name_, seconds_) | ||
23 | -- print( "entering '" .. name_ .. "'") | ||
24 | local lanes = require "lanes" | ||
25 | -- no set_debug_threadname in main thread | ||
26 | if set_debug_threadname | ||
27 | then | ||
28 | -- print( "set_debug_threadname('" .. name_ .. "')") | ||
29 | set_debug_threadname( name_) | ||
30 | end | ||
31 | -- suspend the lane for the specified duration with a failed linda read | ||
32 | wait( seconds_) | ||
33 | -- print( "exiting '" .. name_ .. "'") | ||
34 | end | ||
35 | |||
36 | -- sleeper( "main", 1) | ||
37 | |||
38 | -- the generator | ||
39 | local g = lanes.gen( "*", sleeper) | ||
40 | |||
41 | -- start a forever-waiting lane (nil timeout) | ||
42 | g( "forever") | ||
43 | |||
44 | -- start a lane that will last 2 seconds | ||
45 | g( "two_seconds", 2) | ||
46 | |||
47 | -- give a bit of time to reach the linda waiting call | ||
48 | wait( 0.1) | ||
49 | |||
50 | -- list the known lanes | ||
51 | track( "============= START") | ||
52 | |||
53 | -- wait until "two_seconds has completed" | ||
54 | wait(2.1) | ||
55 | |||
56 | track( "============= two_seconds dead") | ||
57 | |||
58 | -- this will collect the completed lane (and remove it from the tracking queue) | ||
59 | -- collectgarbage() | ||
60 | |||
61 | -- track( "============= two_seconds dead (after collectgarbage)") | ||
62 | |||
63 | -- start another lane that will last 2 seconds, with the same name | ||
64 | g( "two_seconds", 2) | ||
65 | |||
66 | -- give a bit of time to reach the linda waiting call | ||
67 | wait( 0.1) | ||
68 | |||
69 | -- list the known lanes | ||
70 | track( "============= ANOTHER") | ||
71 | |||
72 | print "done" \ No newline at end of file | ||