diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2011-02-21 20:33:39 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2011-02-21 20:33:39 +0100 |
commit | 974aa4343cf900938b5d357d10798d91faf60f5a (patch) | |
tree | b71a342e26ce04c447955f2fc135b69760ab7837 | |
parent | 1760eafa1d2ebce8f07e11414a53d4a251af5b8e (diff) | |
download | lanes-974aa4343cf900938b5d357d10798d91faf60f5a.tar.gz lanes-974aa4343cf900938b5d357d10798d91faf60f5a.tar.bz2 lanes-974aa4343cf900938b5d357d10798d91faf60f5a.zip |
Make the number of internal keeper states selectable by an optional parameter passed to require.
-rw-r--r-- | CHANGES | 11 | ||||
-rw-r--r-- | docs/index.html | 41 | ||||
-rw-r--r-- | src/keeper.c | 39 | ||||
-rw-r--r-- | src/keeper.h | 2 | ||||
-rw-r--r-- | src/lanes.c | 33 | ||||
-rw-r--r-- | src/lanes.lua | 2 |
6 files changed, 71 insertions, 57 deletions
@@ -3,9 +3,12 @@ CHANGES: | |||
3 | 3 | ||
4 | CHANGE X: | 4 | CHANGE X: |
5 | 5 | ||
6 | CHANGE 29 BGe 21-Feb-2011 | ||
7 | Make the number of internal keeper states selectable by an optional parameter passed to require. | ||
8 | |||
6 | CHANGE 28 BGe 18-Feb-2011 | 9 | CHANGE 28 BGe 18-Feb-2011 |
7 | - moved keeper-related code in a separate source file | 10 | Moved keeper-related code in a separate source file |
8 | - keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility | 11 | keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility |
9 | 12 | ||
10 | CHANGE 27 BGe 17-Feb-2011 | 13 | CHANGE 27 BGe 17-Feb-2011 |
11 | - we know Lanes is loaded in the master state, so we don't force it | 14 | - we know Lanes is loaded in the master state, so we don't force it |
@@ -26,7 +29,7 @@ CHANGE 26 BGe 14-Feb-2011: | |||
26 | inter-state data copy for unsupported types | 29 | inter-state data copy for unsupported types |
27 | 30 | ||
28 | CHANGE 25 BGe 12-Feb-2011: | 31 | CHANGE 25 BGe 12-Feb-2011: |
29 | Changed idfunc signature and contract to clarify that fact it is not lua-callable | 32 | Changed idfunc signature and contract to clarify the fact it is not lua-callable |
30 | and to be able to require the module it was exported from in the target lanes | 33 | and to be able to require the module it was exported from in the target lanes |
31 | 34 | ||
32 | CHANGE 24 DPtr 25-Jan-2011: | 35 | CHANGE 24 DPtr 25-Jan-2011: |
@@ -56,7 +59,7 @@ CHANGE 19 BGe 2-Dec-2010: | |||
56 | 59 | ||
57 | CHANGE 18 BGe 6-Oct-2010: | 60 | CHANGE 18 BGe 6-Oct-2010: |
58 | Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown | 61 | Fixed 'memory leak' in some situations where a free running lane is collected before application shutdown |
59 | A bit of code cleanup | 62 | A bit of code cleanup |
60 | 63 | ||
61 | CHANGE 17 BGe 21-Sept-2010: | 64 | CHANGE 17 BGe 21-Sept-2010: |
62 | Fixed stupid compilation errors. | 65 | Fixed stupid compilation errors. |
diff --git a/docs/index.html b/docs/index.html index a28dd41..3d2ecf2 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -56,7 +56,7 @@ | |||
56 | 56 | ||
57 | <p><br/><font size="-1"><i>Copyright © 2007-11 Asko Kauppi. All rights reserved.</i> | 57 | <p><br/><font size="-1"><i>Copyright © 2007-11 Asko Kauppi. All rights reserved.</i> |
58 | <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. | 58 | <br>Lua Lanes is published under the same <A HREF="http://en.wikipedia.org/wiki/MIT_License">MIT license</A> as Lua 5.1. |
59 | </p><p>This document was revised on 12-Feb-11, and applies to version 2.0.11. | 59 | </p><p>This document was revised on 21-Feb-11, and applies to version 2.1.0. |
60 | </font></p> | 60 | </font></p> |
61 | 61 | ||
62 | </center> | 62 | </center> |
@@ -817,12 +817,12 @@ can be used for custom userdata as well. Here's what to do. | |||
817 | <tt>"delete"</tt>: receives this same pointer on the stack, and should cleanup the object.</li> | 817 | <tt>"delete"</tt>: receives this same pointer on the stack, and should cleanup the object.</li> |
818 | <li> | 818 | <li> |
819 | <tt>"metatable"</tt>: should build a metatable for the object. Don't cache the metatable | 819 | <tt>"metatable"</tt>: should build a metatable for the object. Don't cache the metatable |
820 | yourself, Lanes takes care of it ("metatable" should only be invoked once).</li> | 820 | yourself, Lanes takes care of it ("metatable" should only be invoked once).</li> |
821 | <li> | 821 | <li> |
822 | <tt>"module"</tt>: is the name of the module that exports the idfunc, | 822 | <tt>"module"</tt>: is the name of the module that exports the idfunc, |
823 | to be pushed on the stack as a string. It is necessary so that Lanes can require it in | 823 | to be pushed on the stack as a string. It is necessary so that Lanes can require it in |
824 | any Lane and keeper state that receives a userdata. This is to prevent crashes in situations | 824 | any Lane and keeper state that receives a userdata. This is to prevent crashes in situations |
825 | where the module could be unloaded while the idfunc pointer is still held.</li> | 825 | where the module could be unloaded while the idfunc pointer is still held.</li> |
826 | </ul> | 826 | </ul> |
827 | Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. | 827 | Take a look at <tt>linda_id</tt> in <tt>lanes.c</tt>. |
828 | </li> | 828 | </li> |
@@ -917,10 +917,10 @@ Here are some things one should consider, if best performance is vital: | |||
917 | merged into one main timer state (see <tt>timer.lua</tt>); no OS side | 917 | merged into one main timer state (see <tt>timer.lua</tt>); no OS side |
918 | timers are utilized. | 918 | timers are utilized. |
919 | </li> | 919 | </li> |
920 | <li>Lindas are hashed to a fixed number of "keeper states", which are a locking entity. | 920 | <li>Lindas are hashed to a number of "keeper states", which are a locking entity. |
921 | If you are using a lot of Linda objects, | 921 | If you are using a lot of Linda objects, it may be useful to try having more of |
922 | it may be useful to try having more of these keeper states. By default, | 922 | these keeper states. By default, only one is used but this is an implementation detail. |
923 | only one is used (see <tt>KEEPER_STATES_N</tt>), but this is an implementation detail. | 923 | It is possible to <tt>require( "lanes", N)</tt> to use more keeper states. |
924 | </li> | 924 | </li> |
925 | </ul> | 925 | </ul> |
926 | </p> | 926 | </p> |
@@ -952,6 +952,29 @@ its actual value. | |||
952 | <h2 id="changes">Change log</h2> | 952 | <h2 id="changes">Change log</h2> |
953 | 953 | ||
954 | <p> | 954 | <p> |
955 | Feb-2011 (2.1.0) | ||
956 | <ul> | ||
957 | <li>Added an auto-require mechanism to ensure any deep userdata transiting in a lane causes its parent module to be required in that lane | ||
958 | <ul> | ||
959 | <li>Changed idfunc signature and contract to clarify the fact it is not lua-callable and to be able to require the module it was exported from in the target lanes</li> | ||
960 | <li>When a deep userdata idfunc requests a module to be required, manually check that it is not loaded before requiring it instead of relying on the require function's loop detection feature</li> | ||
961 | <li>When a module must be required, raise an error if the 'require' function is not found in the target state</li> | ||
962 | <li>We know Lanes is loaded in the master state, so we don't force it to be required in every lane too when a linda deep userdata is copied</li> | ||
963 | </ul> | ||
964 | </li> | ||
965 | <li>Fixed application hang-up because keeper state was not released in case of errors thrown by inter-state data copy for unsupported types</li> | ||
966 | <li>Refactor lane proxy implementation: it is now a full userdata instead of a table, and its methods are implemented in C instead of Lua | ||
967 | <ul> | ||
968 | <li>its metatable is no longer accessible</li> | ||
969 | <li>writing to the proxy raises an error</li> | ||
970 | <li>it is no longer possible to overwrite its join() and cancel() methods</li> | ||
971 | </ul> | ||
972 | </li> | ||
973 | <li>Moved keeper-related code in a separate source file</li> | ||
974 | <li>keeper.lua is now embedded in text form instead of bytecode to improve LuaJIT2-compatibility</li> | ||
975 | <li>Make the number of internal keeper states selctable by an optional parameter passed to require.</li> | ||
976 | </ul> | ||
977 | |||
955 | Feb-2011 (2.0.11): | 978 | Feb-2011 (2.0.11): |
956 | <ul> | 979 | <ul> |
957 | <li>Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).</li> | 980 | <li>Fixed bug where reference to Linda object was dropped for a short time (crashing if GC was run during that time).</li> |
diff --git a/src/keeper.c b/src/keeper.c index f19beed..f89c638 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -53,12 +53,6 @@ | |||
53 | /*---=== Keeper states ===--- | 53 | /*---=== Keeper states ===--- |
54 | */ | 54 | */ |
55 | 55 | ||
56 | /* The selected number is not optimal; needs to be tested. Even using just | ||
57 | * one keeper state may be good enough (depends on the number of Lindas used | ||
58 | * in the applications). | ||
59 | */ | ||
60 | #define KEEPER_STATES_N 1 // 6 | ||
61 | |||
62 | /* | 56 | /* |
63 | * Pool of keeper states | 57 | * Pool of keeper states |
64 | * | 58 | * |
@@ -66,12 +60,8 @@ | |||
66 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | 60 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the |
67 | * keepers randomly, by a hash. | 61 | * keepers randomly, by a hash. |
68 | */ | 62 | */ |
69 | static struct s_Keeper GKeepers[KEEPER_STATES_N]; | 63 | static struct s_Keeper *GKeepers = NULL; |
70 | 64 | static int GNbKeepers = 0; | |
71 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe | ||
72 | * checking for a lightuserdata is faster. | ||
73 | */ | ||
74 | static bool_t nil_sentinel = 0; | ||
75 | 65 | ||
76 | /* | 66 | /* |
77 | * Lua code for the keeper states (baked in) | 67 | * Lua code for the keeper states (baked in) |
@@ -88,10 +78,13 @@ static char const keeper_chunk[]= | |||
88 | * unclosed, because it does not really matter. In production code, this | 78 | * unclosed, because it does not really matter. In production code, this |
89 | * function never fails. | 79 | * function never fails. |
90 | */ | 80 | */ |
91 | const char *init_keepers(void) | 81 | char const *init_keepers( int const _nbKeepers) |
92 | { | 82 | { |
93 | unsigned int i; | 83 | int i; |
94 | for( i=0; i<KEEPER_STATES_N; i++ ) | 84 | assert( _nbKeepers >= 1); |
85 | GNbKeepers = _nbKeepers; | ||
86 | GKeepers = malloc( _nbKeepers * sizeof( struct s_Keeper)); | ||
87 | for( i = 0; i < _nbKeepers; ++ i) | ||
95 | { | 88 | { |
96 | 89 | ||
97 | // Initialize Keeper states with bare minimum of libs (those required | 90 | // Initialize Keeper states with bare minimum of libs (those required |
@@ -104,8 +97,11 @@ const char *init_keepers(void) | |||
104 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs | 97 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs |
105 | serialize_require( L); | 98 | serialize_require( L); |
106 | 99 | ||
107 | lua_pushlightuserdata( L, &nil_sentinel ); | 100 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe |
108 | lua_setglobal( L, "nil_sentinel" ); | 101 | * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours) |
102 | */ | ||
103 | lua_pushlightuserdata( L, &GNbKeepers); | ||
104 | lua_setglobal( L, "nil_sentinel"); | ||
109 | 105 | ||
110 | // Read in the preloaded chunk (and run it) | 106 | // Read in the preloaded chunk (and run it) |
111 | // | 107 | // |
@@ -131,12 +127,12 @@ const char *init_keepers(void) | |||
131 | struct s_Keeper *keeper_acquire( const void *ptr) | 127 | struct s_Keeper *keeper_acquire( const void *ptr) |
132 | { | 128 | { |
133 | /* | 129 | /* |
134 | * Any hashing will do that maps pointers to 0..KEEPER_STATES_N-1 | 130 | * Any hashing will do that maps pointers to 0..GNbKeepers-1 |
135 | * consistently. | 131 | * consistently. |
136 | * | 132 | * |
137 | * Pointers are often aligned by 8 or so - ignore the low order bits | 133 | * Pointers are often aligned by 8 or so - ignore the low order bits |
138 | */ | 134 | */ |
139 | unsigned int i= ((unsigned long)(ptr) >> 3) % KEEPER_STATES_N; | 135 | unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers; |
140 | struct s_Keeper *K= &GKeepers[i]; | 136 | struct s_Keeper *K= &GKeepers[i]; |
141 | 137 | ||
142 | MUTEX_LOCK( &K->lock_); | 138 | MUTEX_LOCK( &K->lock_); |
@@ -190,10 +186,13 @@ int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, | |||
190 | void close_keepers(void) | 186 | void close_keepers(void) |
191 | { | 187 | { |
192 | int i; | 188 | int i; |
193 | for(i=0;i<KEEPER_STATES_N;i++) | 189 | for( i = 0; i < GNbKeepers; ++ i) |
194 | { | 190 | { |
195 | lua_close( GKeepers[i].L); | 191 | lua_close( GKeepers[i].L); |
196 | GKeepers[i].L = 0; | 192 | GKeepers[i].L = 0; |
197 | //assert( GKeepers[i].count == 0); | 193 | //assert( GKeepers[i].count == 0); |
198 | } | 194 | } |
195 | if( GKeepers) free( GKeepers); | ||
196 | GKeepers = NULL; | ||
197 | GNbKeepers = 0; | ||
199 | } | 198 | } |
diff --git a/src/keeper.h b/src/keeper.h index 66d8d7e..e959c7c 100644 --- a/src/keeper.h +++ b/src/keeper.h | |||
@@ -8,7 +8,7 @@ struct s_Keeper | |||
8 | //int count; | 8 | //int count; |
9 | }; | 9 | }; |
10 | 10 | ||
11 | const char *init_keepers(void); | 11 | const char *init_keepers( int const _nbKeepers); |
12 | struct s_Keeper *keeper_acquire( const void *ptr); | 12 | struct s_Keeper *keeper_acquire( const void *ptr); |
13 | void keeper_release( struct s_Keeper *K); | 13 | void keeper_release( struct s_Keeper *K); |
14 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); | 14 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); |
diff --git a/src/lanes.c b/src/lanes.c index a8aba71..8b62532 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -4,20 +4,7 @@ | |||
4 | * Multithreading in Lua. | 4 | * Multithreading in Lua. |
5 | * | 5 | * |
6 | * History: | 6 | * History: |
7 | * 3-Jan-11 (2.0.10): linda_send bugfix, was waiting on the wrong signal | 7 | * See CHANGES |
8 | * 3-Dec-10 (2.0.9): Added support to generate a lane from a string | ||
9 | * 2-Dec-10 (2.0.8): Fix LuaJIT2 incompatibility (no 'tostring' hijack anymore) | ||
10 | * ???????? (2.0.7): Fixed 'memory leak' in some situations where a free running | ||
11 | * lane is collected before application shutdown | ||
12 | * 24-Aug-10 (2.0.6): Mem fixes, argument checking (lua_toLinda result), thread name | ||
13 | * 24-Jun-09 (2.0.4): Made friendly to already multithreaded host apps. | ||
14 | * 20-Oct-08 (2.0.2): Added closing of free-running threads, but it does | ||
15 | * not seem to eliminate the occasional segfaults at process | ||
16 | * exit. | ||
17 | * ... | ||
18 | * 24-Jun-08 .. 14-Aug-08 AKa: Major revise, Lanes 2008 version (2.0 rc1) | ||
19 | * ... | ||
20 | * 18-Sep-06 AKa: Started the module. | ||
21 | * | 8 | * |
22 | * Platforms (tested internally): | 9 | * Platforms (tested internally): |
23 | * OS X (10.5.7 PowerPC/Intel) | 10 | * OS X (10.5.7 PowerPC/Intel) |
@@ -64,7 +51,7 @@ | |||
64 | * ... | 51 | * ... |
65 | */ | 52 | */ |
66 | 53 | ||
67 | const char *VERSION= "2.0.11"; | 54 | const char *VERSION= "2.1.0"; |
68 | 55 | ||
69 | /* | 56 | /* |
70 | =============================================================================== | 57 | =============================================================================== |
@@ -1913,7 +1900,7 @@ static const struct luaL_reg lanes_functions [] = { | |||
1913 | /* | 1900 | /* |
1914 | * One-time initializations | 1901 | * One-time initializations |
1915 | */ | 1902 | */ |
1916 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref ) | 1903 | static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref, int const nbKeepers) |
1917 | { | 1904 | { |
1918 | const char *err; | 1905 | const char *err; |
1919 | 1906 | ||
@@ -1964,7 +1951,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ | |||
1964 | } | 1951 | } |
1965 | #endif | 1952 | #endif |
1966 | #endif | 1953 | #endif |
1967 | err= init_keepers(); | 1954 | err= init_keepers( nbKeepers); |
1968 | if (err) | 1955 | if (err) |
1969 | { | 1956 | { |
1970 | luaL_error( L, "Unable to initialize: %s", err ); | 1957 | luaL_error( L, "Unable to initialize: %s", err ); |
@@ -2001,8 +1988,11 @@ int | |||
2001 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) | 1988 | #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) |
2002 | __declspec(dllexport) | 1989 | __declspec(dllexport) |
2003 | #endif | 1990 | #endif |
2004 | luaopen_lanes( lua_State *L ) { | 1991 | luaopen_lanes( lua_State *L ) |
2005 | 1992 | { | |
1993 | static volatile int /*bool*/ go_ahead; // = 0 | ||
1994 | int const nbKeepers = luaL_optint( L, 2, 1); | ||
1995 | luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); | ||
2006 | /* | 1996 | /* |
2007 | * Making one-time initializations. | 1997 | * Making one-time initializations. |
2008 | * | 1998 | * |
@@ -2010,7 +2000,6 @@ __declspec(dllexport) | |||
2010 | * there is no problem. But if the host is multithreaded, we need to lock around the | 2000 | * there is no problem. But if the host is multithreaded, we need to lock around the |
2011 | * initializations. | 2001 | * initializations. |
2012 | */ | 2002 | */ |
2013 | static volatile int /*bool*/ go_ahead; // = 0 | ||
2014 | #ifdef PLATFORM_WIN32 | 2003 | #ifdef PLATFORM_WIN32 |
2015 | { | 2004 | { |
2016 | // TBD: Someone please replace this with reliable Win32 API code. Problem is, | 2005 | // TBD: Someone please replace this with reliable Win32 API code. Problem is, |
@@ -2022,7 +2011,7 @@ __declspec(dllexport) | |||
2022 | static volatile unsigned my_number; // = 0 | 2011 | static volatile unsigned my_number; // = 0 |
2023 | 2012 | ||
2024 | if (my_number++ == 0) { // almost atomic | 2013 | if (my_number++ == 0) { // almost atomic |
2025 | init_once_LOCKED(L, &timer_deep); | 2014 | init_once_LOCKED(L, &timer_deep, nbKeepers); |
2026 | go_ahead= 1; // let others pass | 2015 | go_ahead= 1; // let others pass |
2027 | } else { | 2016 | } else { |
2028 | while( !go_ahead ) { Sleep(1); } // changes threads | 2017 | while( !go_ahead ) { Sleep(1); } // changes threads |
@@ -2036,7 +2025,7 @@ __declspec(dllexport) | |||
2036 | // Recheck now that we're within the lock | 2025 | // Recheck now that we're within the lock |
2037 | // | 2026 | // |
2038 | if (!go_ahead) { | 2027 | if (!go_ahead) { |
2039 | init_once_LOCKED(L, &timer_deep); | 2028 | init_once_LOCKED(L, &timer_deep, nbKeepers); |
2040 | go_ahead= 1; | 2029 | go_ahead= 1; |
2041 | } | 2030 | } |
2042 | } | 2031 | } |
diff --git a/src/lanes.lua b/src/lanes.lua index 95bdeeb..78582f9 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -52,7 +52,6 @@ local _version= assert(mm._version) | |||
52 | 52 | ||
53 | local now_secs= assert( mm.now_secs ) | 53 | local now_secs= assert( mm.now_secs ) |
54 | local wakeup_conv= assert( mm.wakeup_conv ) | 54 | local wakeup_conv= assert( mm.wakeup_conv ) |
55 | local timer_gateway= assert( mm.timer_gateway ) | ||
56 | 55 | ||
57 | local max_prio= assert( mm.max_prio ) | 56 | local max_prio= assert( mm.max_prio ) |
58 | 57 | ||
@@ -239,6 +238,7 @@ linda = mm.linda | |||
239 | 238 | ||
240 | ---=== Timers ===--- | 239 | ---=== Timers ===--- |
241 | 240 | ||
241 | local timer_gateway= assert( mm.timer_gateway ) | ||
242 | -- | 242 | -- |
243 | -- On first 'require "lanes"', a timer lane is spawned that will maintain | 243 | -- On first 'require "lanes"', a timer lane is spawned that will maintain |
244 | -- timer tables and sleep in between the timer events. All interaction with | 244 | -- timer tables and sleep in between the timer events. All interaction with |