diff options
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 8 | ||||
-rw-r--r-- | docs/index.html | 27 | ||||
-rw-r--r-- | make-vc.cmd | 6 | ||||
-rw-r--r-- | src/Makefile | 12 | ||||
-rw-r--r-- | src/keeper.c | 199 | ||||
-rw-r--r-- | src/keeper.h | 18 | ||||
-rw-r--r-- | src/keeper.lua | 28 | ||||
-rw-r--r-- | src/lanes.c | 215 | ||||
-rw-r--r-- | src/tools.c | 69 | ||||
-rw-r--r-- | src/tools.h | 3 |
11 files changed, 347 insertions, 242 deletions
@@ -3,6 +3,10 @@ CHANGES: | |||
3 | 3 | ||
4 | CHANGE X: | 4 | CHANGE X: |
5 | 5 | ||
6 | CHANGE 28 BGe 18-Feb-2011 | ||
7 | - 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 | ||
9 | |||
6 | CHANGE 27 BGe 17-Feb-2011 | 10 | CHANGE 27 BGe 17-Feb-2011 |
7 | - we know Lanes is loaded in the master state, so we don't force it | 11 | - we know Lanes is loaded in the master state, so we don't force it |
8 | to be required in every lane too when a linda deep userdata is copied | 12 | to be required in every lane too when a linda deep userdata is copied |
diff --git a/CMakeLists.txt b/CMakeLists.txt index be6534c..31883fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -25,13 +25,11 @@ IF(CYGWIN) | |||
25 | ENDIF(CYGWIN) | 25 | ENDIF(CYGWIN) |
26 | 26 | ||
27 | #2DO - use provided bin2c | 27 | #2DO - use provided bin2c |
28 | # Compile Lua bytecode to C | 28 | # Embed keeper.lua in text form in C (embedding bytecode is not LuaJIT2-compatible) |
29 | ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch | 29 | ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch |
30 | DEPENDS src/keeper.lua | 30 | DEPENDS src/keeper.lua |
31 | COMMAND "${LUAC}" "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lo" | ||
32 | "${CMAKE_CURRENT_SOURCE_DIR}/src/keeper.lua" | ||
33 | COMMAND "${LUA}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/bin2c.lua" | 31 | COMMAND "${LUA}" "${CMAKE_CURRENT_SOURCE_DIR}/tools/bin2c.lua" |
34 | "${CMAKE_CURRENT_BINARY_DIR}/keeper.lo" | 32 | "${CMAKE_CURRENT_SOURCE_DIR}/src/keeper.lua" |
35 | "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lch") | 33 | "-o" "${CMAKE_CURRENT_BINARY_DIR}/keeper.lch") |
36 | SET_SOURCE_FILES_PROPERTIES(src/lanes.c PROPERTIES OBJECT_DEPENDS | 34 | SET_SOURCE_FILES_PROPERTIES(src/lanes.c PROPERTIES OBJECT_DEPENDS |
37 | ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch) | 35 | ${CMAKE_CURRENT_BINARY_DIR}/keeper.lch) |
@@ -40,7 +38,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) | |||
40 | 38 | ||
41 | # Build | 39 | # Build |
42 | INCLUDE_DIRECTORIES(src) | 40 | INCLUDE_DIRECTORIES(src) |
43 | ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c) | 41 | ADD_LIBRARY(lua51-lanes MODULE src/lanes.c src/threading.c src/tools.c src/keeper.c) |
44 | 42 | ||
45 | IF(UNIX AND NOT CYGWIN) | 43 | IF(UNIX AND NOT CYGWIN) |
46 | SET(LIBS pthread) | 44 | SET(LIBS pthread) |
diff --git a/docs/index.html b/docs/index.html index 03c91f7..a28dd41 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -799,8 +799,31 @@ can be used for custom userdata as well. Here's what to do. | |||
799 | </p> | 799 | </p> |
800 | <ol> | 800 | <ol> |
801 | <li>Provide an <i>identity function</i> for your userdata, in C. This function is | 801 | <li>Provide an <i>identity function</i> for your userdata, in C. This function is |
802 | used for creation and deletion of your deep userdata (the shared resource), | 802 | used for creation and deletion of your deep userdata (the shared resource), |
803 | and for making metatables for the state-specific proxies for accessing it. | 803 | and for making metatables for the state-specific proxies for accessing it. The |
804 | prototype is | ||
805 | <table border="1" bgcolor="#E0E0FF" cellpadding="10"> | ||
806 | <tr> | ||
807 | <td> | ||
808 | <code>void idfunc( lua_State *L, char const * const which);</code> | ||
809 | </table> | ||
810 | <tt>which</tt> can be one of: | ||
811 | <ul> | ||
812 | <li> | ||
813 | <tt>"new"</tt>: requests the creation of a new object, whose pointer is pushed on the stack | ||
814 | as a light userdata. | ||
815 | </li> | ||
816 | <li> | ||
817 | <tt>"delete"</tt>: receives this same pointer on the stack, and should cleanup the object.</li> | ||
818 | <li> | ||
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> | ||
821 | <li> | ||
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 | ||
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> | ||
826 | </ul> | ||
804 | 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>. |
805 | </li> | 828 | </li> |
806 | <li>Instanciate your userdata using <tt>luaG_deep_userdata()</tt>, | 829 | <li>Instanciate your userdata using <tt>luaG_deep_userdata()</tt>, |
diff --git a/make-vc.cmd b/make-vc.cmd index 2b4a7f6..7711a44 100644 --- a/make-vc.cmd +++ b/make-vc.cmd | |||
@@ -114,13 +114,11 @@ goto ERR_NOLUA | |||
114 | :LUA_LIB_OK | 114 | :LUA_LIB_OK |
115 | 115 | ||
116 | @REM | 116 | @REM |
117 | @REM Precompile src/.lua -> .lch | 117 | @REM Embed src/keeper.lua -> .lch |
118 | @REM | 118 | @REM |
119 | @REM Note: we cannot use piping in Windows since we need binary output. | 119 | @REM Note: we cannot use piping in Windows since we need binary output. |
120 | @REM | 120 | @REM |
121 | "%LUAC_EXE%" -o delme src/keeper.lua | 121 | "%LUA_EXE%" tools/bin2c.lua -o src/keeper.lch src/keeper.lua |
122 | "%LUA_EXE%" tools/bin2c.lua -o src/keeper.lch delme | ||
123 | @del delme | ||
124 | 122 | ||
125 | @if "%VCINSTALLDIR%"=="" goto ERR_NOVC | 123 | @if "%VCINSTALLDIR%"=="" goto ERR_NOVC |
126 | 124 | ||
diff --git a/src/Makefile b/src/Makefile index a17e9cd..df65926 100644 --- a/src/Makefile +++ b/src/Makefile | |||
@@ -7,7 +7,7 @@ | |||
7 | 7 | ||
8 | MODULE=lanes | 8 | MODULE=lanes |
9 | 9 | ||
10 | SRC=lanes.c threading.c tools.c | 10 | SRC=lanes.c threading.c tools.c keeper.c |
11 | 11 | ||
12 | OBJ=$(SRC:.c=.o) | 12 | OBJ=$(SRC:.c=.o) |
13 | 13 | ||
@@ -134,10 +134,14 @@ lanes.o: keeper.lch | |||
134 | # Note: 'luac -o -' could be used on systems other than Windows (where pipes | 134 | # Note: 'luac -o -' could be used on systems other than Windows (where pipes |
135 | # are binary). We need to support MinGW as well, so a temporary file. | 135 | # are binary). We need to support MinGW as well, so a temporary file. |
136 | # | 136 | # |
137 | # mode 1: embed bytecode version | ||
138 | #%.lch: %.lua | ||
139 | # $(LUAC) -o $@.tmp $< | ||
140 | # $(LUA) ../tools/bin2c.lua $@.tmp -o $@ | ||
141 | # -rm $@.tmp | ||
142 | # mode 2: embed text version (LuaJIT2-compatible) | ||
137 | %.lch: %.lua | 143 | %.lch: %.lua |
138 | $(LUAC) -o $@.tmp $< | 144 | $(LUA) ../tools/bin2c.lua $< -o $@ |
139 | $(LUA) ../tools/bin2c.lua $@.tmp -o $@ | ||
140 | -rm $@.tmp | ||
141 | 145 | ||
142 | #--- | 146 | #--- |
143 | # NSLU2 "slug" Linux ARM | 147 | # NSLU2 "slug" Linux ARM |
diff --git a/src/keeper.c b/src/keeper.c new file mode 100644 index 0000000..f19beed --- /dev/null +++ b/src/keeper.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | -- | ||
3 | -- KEEPER.C | ||
4 | -- | ||
5 | -- Keeper state logic | ||
6 | -- | ||
7 | -- This code is read in for each "keeper state", which are the hidden, inter- | ||
8 | -- mediate data stores used by Lanes inter-state communication objects. | ||
9 | -- | ||
10 | -- Author: Benoit Germain <bnt.germain@gmail.com> | ||
11 | -- | ||
12 | -- C implementation replacement of the original keeper.lua | ||
13 | -- | ||
14 | --[[ | ||
15 | =============================================================================== | ||
16 | |||
17 | Copyright (C) 2011 Benoit Germain <bnt.germain@gmail.com> | ||
18 | |||
19 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
20 | of this software and associated documentation files (the "Software"), to deal | ||
21 | in the Software without restriction, including without limitation the rights | ||
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
23 | copies of the Software, and to permit persons to whom the Software is | ||
24 | furnished to do so, subject to the following conditions: | ||
25 | |||
26 | The above copyright notice and this permission notice shall be included in | ||
27 | all copies or substantial portions of the Software. | ||
28 | |||
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
35 | THE SOFTWARE. | ||
36 | |||
37 | =============================================================================== | ||
38 | ]]-- | ||
39 | */ | ||
40 | |||
41 | #include <string.h> | ||
42 | #include <stdio.h> | ||
43 | #include <stdlib.h> | ||
44 | #include <ctype.h> | ||
45 | |||
46 | #include "lua.h" | ||
47 | #include "lauxlib.h" | ||
48 | |||
49 | #include "threading.h" | ||
50 | #include "tools.h" | ||
51 | #include "keeper.h" | ||
52 | |||
53 | /*---=== Keeper states ===--- | ||
54 | */ | ||
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 | /* | ||
63 | * Pool of keeper states | ||
64 | * | ||
65 | * Access to keeper states is locked (only one OS thread at a time) so the | ||
66 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | ||
67 | * keepers randomly, by a hash. | ||
68 | */ | ||
69 | static struct s_Keeper GKeepers[KEEPER_STATES_N]; | ||
70 | |||
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 | |||
76 | /* | ||
77 | * Lua code for the keeper states (baked in) | ||
78 | */ | ||
79 | static char const keeper_chunk[]= | ||
80 | #include "keeper.lch" | ||
81 | |||
82 | /* | ||
83 | * Initialize keeper states | ||
84 | * | ||
85 | * If there is a problem, return an error message (NULL for okay). | ||
86 | * | ||
87 | * Note: Any problems would be design flaws; the created Lua state is left | ||
88 | * unclosed, because it does not really matter. In production code, this | ||
89 | * function never fails. | ||
90 | */ | ||
91 | const char *init_keepers(void) | ||
92 | { | ||
93 | unsigned int i; | ||
94 | for( i=0; i<KEEPER_STATES_N; i++ ) | ||
95 | { | ||
96 | |||
97 | // Initialize Keeper states with bare minimum of libs (those required | ||
98 | // by 'keeper.lua') | ||
99 | // | ||
100 | lua_State *L= luaL_newstate(); | ||
101 | if (!L) | ||
102 | return "out of memory"; | ||
103 | |||
104 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs | ||
105 | serialize_require( L); | ||
106 | |||
107 | lua_pushlightuserdata( L, &nil_sentinel ); | ||
108 | lua_setglobal( L, "nil_sentinel" ); | ||
109 | |||
110 | // Read in the preloaded chunk (and run it) | ||
111 | // | ||
112 | if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" )) | ||
113 | return "luaL_loadbuffer() failed"; // LUA_ERRMEM | ||
114 | |||
115 | if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) | ||
116 | { | ||
117 | // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR | ||
118 | // | ||
119 | const char *err= lua_tostring(L,-1); | ||
120 | assert(err); | ||
121 | return err; | ||
122 | } | ||
123 | |||
124 | MUTEX_INIT( &GKeepers[i].lock_ ); | ||
125 | GKeepers[i].L= L; | ||
126 | //GKeepers[i].count = 0; | ||
127 | } | ||
128 | return NULL; // ok | ||
129 | } | ||
130 | |||
131 | struct s_Keeper *keeper_acquire( const void *ptr) | ||
132 | { | ||
133 | /* | ||
134 | * Any hashing will do that maps pointers to 0..KEEPER_STATES_N-1 | ||
135 | * consistently. | ||
136 | * | ||
137 | * Pointers are often aligned by 8 or so - ignore the low order bits | ||
138 | */ | ||
139 | unsigned int i= ((unsigned long)(ptr) >> 3) % KEEPER_STATES_N; | ||
140 | struct s_Keeper *K= &GKeepers[i]; | ||
141 | |||
142 | MUTEX_LOCK( &K->lock_); | ||
143 | //++ K->count; | ||
144 | return K; | ||
145 | } | ||
146 | |||
147 | void keeper_release( struct s_Keeper *K) | ||
148 | { | ||
149 | //-- K->count; | ||
150 | MUTEX_UNLOCK( &K->lock_); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * Call a function ('func_name') in the keeper state, and pass on the returned | ||
155 | * values to 'L'. | ||
156 | * | ||
157 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | ||
158 | * 'starting_index': first of the rest of parameters (none if 0) | ||
159 | * | ||
160 | * Returns: number of return values (pushed to 'L') or -1 in case of error | ||
161 | */ | ||
162 | int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index) | ||
163 | { | ||
164 | int const args = starting_index ? (lua_gettop(L) - starting_index +1) : 0; | ||
165 | int const Ktos = lua_gettop(K); | ||
166 | int retvals = -1; | ||
167 | |||
168 | STACK_GROW( K, 2); | ||
169 | |||
170 | lua_getglobal( K, func_name); | ||
171 | ASSERT_L( lua_isfunction(K, -1)); | ||
172 | |||
173 | lua_pushlightuserdata( K, linda); | ||
174 | |||
175 | if( (args == 0) || luaG_inter_copy( L, K, args) == 0) // L->K | ||
176 | { | ||
177 | lua_call( K, 1 + args, LUA_MULTRET); | ||
178 | |||
179 | retvals = lua_gettop( K) - Ktos; | ||
180 | if( (retvals > 0) && luaG_inter_move( K, L, retvals) != 0) // K->L | ||
181 | { | ||
182 | retvals = -1; | ||
183 | } | ||
184 | } | ||
185 | // whatever happens, restore the stack to where it was at the origin | ||
186 | lua_settop( K, Ktos); | ||
187 | return retvals; | ||
188 | } | ||
189 | |||
190 | void close_keepers(void) | ||
191 | { | ||
192 | int i; | ||
193 | for(i=0;i<KEEPER_STATES_N;i++) | ||
194 | { | ||
195 | lua_close( GKeepers[i].L); | ||
196 | GKeepers[i].L = 0; | ||
197 | //assert( GKeepers[i].count == 0); | ||
198 | } | ||
199 | } | ||
diff --git a/src/keeper.h b/src/keeper.h new file mode 100644 index 0000000..66d8d7e --- /dev/null +++ b/src/keeper.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #if !defined( __keeper_h__) | ||
2 | #define __keeper_h__ 1 | ||
3 | |||
4 | struct s_Keeper | ||
5 | { | ||
6 | MUTEX_T lock_; | ||
7 | lua_State *L; | ||
8 | //int count; | ||
9 | }; | ||
10 | |||
11 | const char *init_keepers(void); | ||
12 | struct s_Keeper *keeper_acquire( const void *ptr); | ||
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); | ||
15 | void close_keepers(void); | ||
16 | |||
17 | |||
18 | #endif // __keeper_h__ \ No newline at end of file | ||
diff --git a/src/keeper.lua b/src/keeper.lua index 9256a4b..2c38c0b 100644 --- a/src/keeper.lua +++ b/src/keeper.lua | |||
@@ -43,12 +43,26 @@ assert( nil_sentinel ) | |||
43 | local table_remove= assert( table.remove ) | 43 | local table_remove= assert( table.remove ) |
44 | local table_concat= assert( table.concat ) | 44 | local table_concat= assert( table.concat ) |
45 | 45 | ||
46 | --[[ | ||
46 | local function WR(...) | 47 | local function WR(...) |
47 | if io then | 48 | if io then |
48 | io.stderr:write( table_concat({...},'\t').."\n" ) | 49 | io.stderr:write( table_concat({...},'\t').."\n" ) |
49 | end | 50 | end |
50 | end | 51 | end |
51 | 52 | ||
53 | local function DEBUG(title,ud,key) | ||
54 | assert( title and ud and key ) | ||
55 | |||
56 | local data,incoming,_= tables(ud) | ||
57 | |||
58 | local s= tostring(data[key]) | ||
59 | for _,v in ipairs( incoming[key] or {} ) do | ||
60 | s= s..", "..tostring(v) | ||
61 | end | ||
62 | WR( "*** "..title.." ("..tostring(key).."): ", s ) | ||
63 | end | ||
64 | --]] | ||
65 | |||
52 | ----- | 66 | ----- |
53 | -- Actual data store | 67 | -- Actual data store |
54 | -- | 68 | -- |
@@ -92,20 +106,6 @@ local function tables( ud ) | |||
92 | return _data[ud], _incoming[ud], _limits[ud] | 106 | return _data[ud], _incoming[ud], _limits[ud] |
93 | end | 107 | end |
94 | 108 | ||
95 | |||
96 | local function DEBUG(title,ud,key) | ||
97 | assert( title and ud and key ) | ||
98 | |||
99 | local data,incoming,_= tables(ud) | ||
100 | |||
101 | local s= tostring(data[key]) | ||
102 | for _,v in ipairs( incoming[key] or {} ) do | ||
103 | s= s..", "..tostring(v) | ||
104 | end | ||
105 | WR( "*** "..title.." ("..tostring(key).."): ", s ) | ||
106 | end | ||
107 | |||
108 | |||
109 | ----- | 109 | ----- |
110 | -- bool= send( linda_deep_ud, key, ... ) | 110 | -- bool= send( linda_deep_ud, key, ... ) |
111 | -- | 111 | -- |
diff --git a/src/lanes.c b/src/lanes.c index 0a89959..a8aba71 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -102,6 +102,7 @@ THE SOFTWARE. | |||
102 | 102 | ||
103 | #include "threading.h" | 103 | #include "threading.h" |
104 | #include "tools.h" | 104 | #include "tools.h" |
105 | #include "keeper.h" | ||
105 | 106 | ||
106 | #if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) | 107 | #if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) |
107 | # include <sys/time.h> | 108 | # include <sys/time.h> |
@@ -113,12 +114,6 @@ THE SOFTWARE. | |||
113 | # include <sys/types.h> | 114 | # include <sys/types.h> |
114 | #endif | 115 | #endif |
115 | 116 | ||
116 | /* The selected number is not optimal; needs to be tested. Even using just | ||
117 | * one keeper state may be good enough (depends on the number of Lindas used | ||
118 | * in the applications). | ||
119 | */ | ||
120 | #define KEEPER_STATES_N 1 // 6 | ||
121 | |||
122 | /* Do you want full call stacks, or just the line where the error happened? | 117 | /* Do you want full call stacks, or just the line where the error happened? |
123 | * | 118 | * |
124 | * TBD: The full stack feature does not seem to work (try 'make error'). | 119 | * TBD: The full stack feature does not seem to work (try 'make error'). |
@@ -129,12 +124,6 @@ THE SOFTWARE. | |||
129 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | 124 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key |
130 | #endif | 125 | #endif |
131 | 126 | ||
132 | /* | ||
133 | * Lua code for the keeper states (baked in) | ||
134 | */ | ||
135 | static char keeper_chunk[]= | ||
136 | #include "keeper.lch" | ||
137 | |||
138 | // NOTE: values to be changed by either thread, during execution, without | 127 | // NOTE: values to be changed by either thread, during execution, without |
139 | // locking, are marked "volatile" | 128 | // locking, are marked "volatile" |
140 | // | 129 | // |
@@ -254,199 +243,6 @@ static bool_t push_registry_table( lua_State *L, void *key, bool_t create ) { | |||
254 | } | 243 | } |
255 | 244 | ||
256 | 245 | ||
257 | /*---=== Serialize require ===--- | ||
258 | */ | ||
259 | |||
260 | static MUTEX_T require_cs; | ||
261 | |||
262 | //--- | ||
263 | // [val]= new_require( ... ) | ||
264 | // | ||
265 | // Call 'old_require' but only one lane at a time. | ||
266 | // | ||
267 | // Upvalues: [1]: original 'require' function | ||
268 | // | ||
269 | static int new_require( lua_State *L ) { | ||
270 | int rc; | ||
271 | int args= lua_gettop(L); | ||
272 | |||
273 | STACK_GROW(L,1); | ||
274 | STACK_CHECK(L) | ||
275 | |||
276 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | ||
277 | // leave us locked, blocking any future 'require' calls from other lanes. | ||
278 | // | ||
279 | MUTEX_LOCK( &require_cs ); | ||
280 | { | ||
281 | lua_pushvalue( L, lua_upvalueindex(1) ); | ||
282 | lua_insert( L, 1 ); | ||
283 | |||
284 | rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); | ||
285 | // | ||
286 | // LUA_ERRRUN / LUA_ERRMEM | ||
287 | } | ||
288 | MUTEX_UNLOCK( &require_cs ); | ||
289 | |||
290 | if (rc) lua_error(L); // error message already at [-1] | ||
291 | |||
292 | STACK_END(L,0) | ||
293 | return 1; | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * Serialize calls to 'require', if it exists | ||
298 | */ | ||
299 | static | ||
300 | void serialize_require( lua_State *L ) { | ||
301 | |||
302 | STACK_GROW(L,1); | ||
303 | STACK_CHECK(L) | ||
304 | |||
305 | // Check 'require' is there; if not, do nothing | ||
306 | // | ||
307 | lua_getglobal( L, "require" ); | ||
308 | if (lua_isfunction( L, -1 )) { | ||
309 | // [-1]: original 'require' function | ||
310 | |||
311 | lua_pushcclosure( L, new_require, 1 /*upvalues*/ ); | ||
312 | lua_setglobal( L, "require" ); | ||
313 | |||
314 | } else { | ||
315 | // [-1]: nil | ||
316 | lua_pop(L,1); | ||
317 | } | ||
318 | |||
319 | STACK_END(L,0) | ||
320 | } | ||
321 | |||
322 | |||
323 | /*---=== Keeper states ===--- | ||
324 | */ | ||
325 | |||
326 | /* | ||
327 | * Pool of keeper states | ||
328 | * | ||
329 | * Access to keeper states is locked (only one OS thread at a time) so the | ||
330 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | ||
331 | * keepers randomly, by a hash. | ||
332 | */ | ||
333 | struct s_Keeper | ||
334 | { | ||
335 | MUTEX_T lock_; | ||
336 | lua_State *L; | ||
337 | //int count; | ||
338 | } GKeepers[KEEPER_STATES_N]; | ||
339 | |||
340 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe | ||
341 | * checking for a lightuserdata is faster. | ||
342 | */ | ||
343 | static bool_t nil_sentinel; | ||
344 | |||
345 | /* | ||
346 | * Initialize keeper states | ||
347 | * | ||
348 | * If there is a problem, return an error message (NULL for okay). | ||
349 | * | ||
350 | * Note: Any problems would be design flaws; the created Lua state is left | ||
351 | * unclosed, because it does not really matter. In production code, this | ||
352 | * function never fails. | ||
353 | */ | ||
354 | static const char *init_keepers(void) { | ||
355 | unsigned int i; | ||
356 | for( i=0; i<KEEPER_STATES_N; i++ ) { | ||
357 | |||
358 | // Initialize Keeper states with bare minimum of libs (those required | ||
359 | // by 'keeper.lua') | ||
360 | // | ||
361 | lua_State *L= luaL_newstate(); | ||
362 | if (!L) return "out of memory"; | ||
363 | |||
364 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs | ||
365 | serialize_require( L); | ||
366 | |||
367 | lua_pushlightuserdata( L, &nil_sentinel ); | ||
368 | lua_setglobal( L, "nil_sentinel" ); | ||
369 | |||
370 | // Read in the preloaded chunk (and run it) | ||
371 | // | ||
372 | if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" )) | ||
373 | return "luaL_loadbuffer() failed"; // LUA_ERRMEM | ||
374 | |||
375 | if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) { | ||
376 | // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR | ||
377 | // | ||
378 | const char *err= lua_tostring(L,-1); | ||
379 | assert(err); | ||
380 | return err; | ||
381 | } | ||
382 | |||
383 | MUTEX_INIT( &GKeepers[i].lock_ ); | ||
384 | GKeepers[i].L= L; | ||
385 | //GKeepers[i].count = 0; | ||
386 | } | ||
387 | return NULL; // ok | ||
388 | } | ||
389 | |||
390 | static struct s_Keeper *keeper_acquire( const void *ptr) | ||
391 | { | ||
392 | /* | ||
393 | * Any hashing will do that maps pointers to 0..KEEPER_STATES_N-1 | ||
394 | * consistently. | ||
395 | * | ||
396 | * Pointers are often aligned by 8 or so - ignore the low order bits | ||
397 | */ | ||
398 | unsigned int i= ((unsigned long)(ptr) >> 3) % KEEPER_STATES_N; | ||
399 | struct s_Keeper *K= &GKeepers[i]; | ||
400 | |||
401 | MUTEX_LOCK( &K->lock_); | ||
402 | //++ K->count; | ||
403 | return K; | ||
404 | } | ||
405 | |||
406 | static void keeper_release( struct s_Keeper *K) | ||
407 | { | ||
408 | //-- K->count; | ||
409 | MUTEX_UNLOCK( &K->lock_); | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * Call a function ('func_name') in the keeper state, and pass on the returned | ||
414 | * values to 'L'. | ||
415 | * | ||
416 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | ||
417 | * 'starting_index': first of the rest of parameters (none if 0) | ||
418 | * | ||
419 | * Returns: number of return values (pushed to 'L') or -1 in case of error | ||
420 | */ | ||
421 | static int keeper_call( lua_State *K, char const *func_name, lua_State *L, struct s_Linda *linda, uint_t starting_index) | ||
422 | { | ||
423 | int const args = starting_index ? (lua_gettop(L) - starting_index +1) : 0; | ||
424 | int const Ktos = lua_gettop(K); | ||
425 | int retvals = -1; | ||
426 | |||
427 | STACK_GROW( K, 2); | ||
428 | |||
429 | lua_getglobal( K, func_name); | ||
430 | ASSERT_L( lua_isfunction(K, -1)); | ||
431 | |||
432 | lua_pushlightuserdata( K, linda); | ||
433 | |||
434 | if( (args == 0) || luaG_inter_copy( L, K, args) == 0) // L->K | ||
435 | { | ||
436 | lua_call( K, 1 + args, LUA_MULTRET); | ||
437 | |||
438 | retvals = lua_gettop( K) - Ktos; | ||
439 | if( (retvals > 0) && luaG_inter_move( K, L, retvals) != 0) // K->L | ||
440 | { | ||
441 | retvals = -1; | ||
442 | } | ||
443 | } | ||
444 | // whatever happens, restore the stack to where it was at the origin | ||
445 | lua_settop( K, Ktos); | ||
446 | return retvals; | ||
447 | } | ||
448 | |||
449 | |||
450 | /*---=== Linda ===--- | 246 | /*---=== Linda ===--- |
451 | */ | 247 | */ |
452 | 248 | ||
@@ -1189,14 +985,7 @@ static void selfdestruct_atexit( void ) { | |||
1189 | DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); | 985 | DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); |
1190 | #endif | 986 | #endif |
1191 | } | 987 | } |
1192 | { | 988 | close_keepers(); |
1193 | int i; | ||
1194 | for(i=0;i<KEEPER_STATES_N;i++){ | ||
1195 | lua_close( GKeepers[i].L); | ||
1196 | GKeepers[i].L = 0; | ||
1197 | //assert( GKeepers[i].count == 0); | ||
1198 | } | ||
1199 | } | ||
1200 | } | 989 | } |
1201 | 990 | ||
1202 | 991 | ||
diff --git a/src/tools.c b/src/tools.c index 29959a8..d475fc0 100644 --- a/src/tools.c +++ b/src/tools.c | |||
@@ -1278,3 +1278,72 @@ int luaG_inter_move( lua_State* L, lua_State *L2, uint_t n ) | |||
1278 | lua_pop( L, (int) n); | 1278 | lua_pop( L, (int) n); |
1279 | return ret; | 1279 | return ret; |
1280 | } | 1280 | } |
1281 | |||
1282 | /*---=== Serialize require ===--- | ||
1283 | */ | ||
1284 | |||
1285 | MUTEX_T require_cs; | ||
1286 | |||
1287 | //--- | ||
1288 | // [val]= new_require( ... ) | ||
1289 | // | ||
1290 | // Call 'old_require' but only one lane at a time. | ||
1291 | // | ||
1292 | // Upvalues: [1]: original 'require' function | ||
1293 | // | ||
1294 | static int new_require( lua_State *L ) | ||
1295 | { | ||
1296 | int rc; | ||
1297 | int args= lua_gettop(L); | ||
1298 | |||
1299 | STACK_GROW(L,1); | ||
1300 | STACK_CHECK(L) | ||
1301 | |||
1302 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | ||
1303 | // leave us locked, blocking any future 'require' calls from other lanes. | ||
1304 | // | ||
1305 | MUTEX_LOCK( &require_cs); | ||
1306 | { | ||
1307 | lua_pushvalue( L, lua_upvalueindex(1) ); | ||
1308 | lua_insert( L, 1 ); | ||
1309 | |||
1310 | rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); | ||
1311 | // | ||
1312 | // LUA_ERRRUN / LUA_ERRMEM | ||
1313 | } | ||
1314 | MUTEX_UNLOCK( &require_cs); | ||
1315 | |||
1316 | if (rc) | ||
1317 | lua_error(L); // error message already at [-1] | ||
1318 | |||
1319 | STACK_END(L,0) | ||
1320 | return 1; | ||
1321 | } | ||
1322 | |||
1323 | /* | ||
1324 | * Serialize calls to 'require', if it exists | ||
1325 | */ | ||
1326 | void serialize_require( lua_State *L ) | ||
1327 | { | ||
1328 | STACK_GROW(L,1); | ||
1329 | STACK_CHECK(L) | ||
1330 | |||
1331 | // Check 'require' is there; if not, do nothing | ||
1332 | // | ||
1333 | lua_getglobal( L, "require" ); | ||
1334 | if (lua_isfunction( L, -1 )) | ||
1335 | { | ||
1336 | // [-1]: original 'require' function | ||
1337 | |||
1338 | lua_pushcclosure( L, new_require, 1 /*upvalues*/ ); | ||
1339 | lua_setglobal( L, "require" ); | ||
1340 | |||
1341 | } | ||
1342 | else | ||
1343 | { | ||
1344 | // [-1]: nil | ||
1345 | lua_pop(L,1); | ||
1346 | } | ||
1347 | |||
1348 | STACK_END(L,0) | ||
1349 | } | ||
diff --git a/src/tools.h b/src/tools.h index 0907df2..a080257 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -72,5 +72,8 @@ int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n); | |||
72 | extern MUTEX_T deep_lock; | 72 | extern MUTEX_T deep_lock; |
73 | extern MUTEX_T mtid_lock; | 73 | extern MUTEX_T mtid_lock; |
74 | 74 | ||
75 | void serialize_require( lua_State *L); | ||
76 | extern MUTEX_T require_cs; | ||
77 | |||
75 | #endif | 78 | #endif |
76 | // TOOLS_H | 79 | // TOOLS_H |