aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile2
-rw-r--r--src/keeper.c152
-rw-r--r--src/keeper.h4
-rw-r--r--src/keeper.lua222
-rw-r--r--src/lanes.c696
-rw-r--r--src/lanes.lua87
-rw-r--r--src/threading.c62
-rw-r--r--src/threading.h55
-rw-r--r--src/tools.c671
-rw-r--r--src/tools.h13
10 files changed, 1431 insertions, 533 deletions
diff --git a/src/Makefile b/src/Makefile
index df65926..03f5558 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -124,7 +124,7 @@ all: lua51-$(MODULE)$(_SO)
124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think) 124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think)
125# 125#
126lua51-$(MODULE)$(_SO): $(OBJ) 126lua51-$(MODULE)$(_SO): $(OBJ)
127 $(CC) $(LIBFLAG) $(LIBS) $^ $(LUA_LIBS) -o $@ 127 $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@
128 128
129clean: 129clean:
130 -rm -rf lua51-$(MODULE)$(_SO) *.lch *.o *.tmp *.map 130 -rm -rf lua51-$(MODULE)$(_SO) *.lch *.o *.tmp *.map
diff --git a/src/keeper.c b/src/keeper.c
index 01e8880..6f5bd95 100644
--- a/src/keeper.c
+++ b/src/keeper.c
@@ -78,7 +78,7 @@ static char const keeper_chunk[]=
78* unclosed, because it does not really matter. In production code, this 78* unclosed, because it does not really matter. In production code, this
79* function never fails. 79* function never fails.
80*/ 80*/
81char const *init_keepers( int const _nbKeepers) 81char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create)
82{ 82{
83 int i; 83 int i;
84 assert( _nbKeepers >= 1); 84 assert( _nbKeepers >= 1);
@@ -87,69 +87,146 @@ char const *init_keepers( int const _nbKeepers)
87 for( i = 0; i < _nbKeepers; ++ i) 87 for( i = 0; i < _nbKeepers; ++ i)
88 { 88 {
89 89
90 // Initialize Keeper states with bare minimum of libs (those required 90 // Initialize Keeper states with bare minimum of libs (those required by 'keeper.lua')
91 // by 'keeper.lua') 91 //
92 // 92 // 'io' for debugging messages, 'package' because we need to require modules exporting idfuncs
93 lua_State *L= luaL_newstate(); 93 // the others because they export functions that we may store in a keeper for transfer between lanes
94 if (!L) 94 lua_State* K = luaG_newstate( "*", _on_state_create);
95 if (!K)
95 return "out of memory"; 96 return "out of memory";
96 97
97 // to see VM name in Decoda debugger 98 // to see VM name in Decoda debugger
98 lua_pushliteral( L, "Keeper #"); 99 lua_pushliteral( K, "Keeper #");
99 lua_pushinteger( L, i + 1); 100 lua_pushinteger( K, i + 1);
100 lua_concat( L, 2); 101 lua_concat( K, 2);
101 lua_setglobal( L, "decoda_name"); 102 lua_setglobal( K, "decoda_name");
102
103 luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs
104 serialize_require( L);
105
106 /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
107 * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
108 */
109 lua_pushlightuserdata( L, &GNbKeepers);
110 lua_setglobal( L, "nil_sentinel");
111 103
112 // Read in the preloaded chunk (and run it) 104 // Read in the preloaded chunk (and run it)
113 // 105 //
114 if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" )) 106 if( luaL_loadbuffer( K, keeper_chunk, sizeof(keeper_chunk), "@keeper.lua"))
115 return "luaL_loadbuffer() failed"; // LUA_ERRMEM 107 return "luaL_loadbuffer() failed"; // LUA_ERRMEM
116 108
117 if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) 109 if( lua_pcall( K, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/))
118 { 110 {
119 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR 111 // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR
120 // 112 //
121 const char *err= lua_tostring(L,-1); 113 char const* err = lua_tostring( K, -1);
122 assert(err); 114 assert( err);
123 return err; 115 return err;
124 } 116 }
125 117
126 MUTEX_INIT( &GKeepers[i].lock_ ); 118 MUTEX_INIT( &GKeepers[i].lock_);
127 GKeepers[i].L= L; 119 GKeepers[i].L = K;
128 //GKeepers[i].count = 0; 120 //GKeepers[i].count = 0;
129 } 121 }
130 return NULL; // ok 122 return NULL; // ok
131} 123}
132 124
125// cause each keeper state to populate its database of transferable functions with those from the specified module
126void populate_keepers( lua_State *L)
127{
128 size_t name_len;
129 char const *name = luaL_checklstring( L, -1, &name_len);
130 size_t package_path_len;
131 char const *package_path;
132 size_t package_cpath_len;
133 char const *package_cpath;
134 int i;
135
136 // we need to make sure that package.path & package.cpath are the same in the keepers
137// than what is currently in use when the module is required in the caller's Lua state
138 STACK_CHECK(L)
139 STACK_GROW( L, 3);
140 lua_getglobal( L, "package");
141 lua_getfield( L, -1, "path");
142 package_path = luaL_checklstring( L, -1, &package_path_len);
143 lua_getfield( L, -2, "cpath");
144 package_cpath = luaL_checklstring( L, -1, &package_cpath_len);
145
146 for( i = 0; i < GNbKeepers; ++ i)
147 {
148 lua_State *K = GKeepers[i].L;
149 int res;
150 MUTEX_LOCK( &GKeepers[i].lock_);
151 STACK_CHECK(K)
152 STACK_GROW( K, 2);
153 lua_getglobal( K, "package");
154 lua_pushlstring( K, package_path, package_path_len);
155 lua_setfield( K, -2, "path");
156 lua_pushlstring( K, package_cpath, package_cpath_len);
157 lua_setfield( K, -2, "cpath");
158 lua_pop( K, 1);
159 lua_getglobal( K, "require");
160 lua_pushlstring( K, name, name_len);
161 res = lua_pcall( K, 1, 0, 0);
162 if( res != 0)
163 {
164 char const *err = luaL_checkstring( K, -1);
165 luaL_error( L, "error requiring '%s' in keeper state: %s", name, err);
166 }
167 STACK_END(K, 0)
168 MUTEX_UNLOCK( &GKeepers[i].lock_);
169 }
170 lua_pop( L, 3);
171 STACK_END(L, 0)
172}
173
133struct s_Keeper *keeper_acquire( const void *ptr) 174struct s_Keeper *keeper_acquire( const void *ptr)
134{ 175{
135 /* 176 // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers)
136 * Any hashing will do that maps pointers to 0..GNbKeepers-1 177 if( GNbKeepers == 0)
137 * consistently. 178 {
138 * 179 return NULL;
139 * Pointers are often aligned by 8 or so - ignore the low order bits 180 }
140 */ 181 else
141 unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers; 182 {
142 struct s_Keeper *K= &GKeepers[i]; 183 /*
184 * Any hashing will do that maps pointers to 0..GNbKeepers-1
185 * consistently.
186 *
187 * Pointers are often aligned by 8 or so - ignore the low order bits
188 */
189 unsigned int i= ((unsigned long)(ptr) >> 3) % GNbKeepers;
190 struct s_Keeper *K= &GKeepers[i];
143 191
144 MUTEX_LOCK( &K->lock_); 192 MUTEX_LOCK( &K->lock_);
145 //++ K->count; 193 //++ K->count;
146 return K; 194 return K;
195 }
147} 196}
148 197
149void keeper_release( struct s_Keeper *K) 198void keeper_release( struct s_Keeper *K)
150{ 199{
151 //-- K->count; 200 //-- K->count;
152 MUTEX_UNLOCK( &K->lock_); 201 if( K) MUTEX_UNLOCK( &K->lock_);
202}
203
204void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel)
205{
206 int i, n = lua_gettop( L);
207 /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
208 * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours)
209 */
210 void *nil_sentinel = &GNbKeepers;
211 for( i = _val_i; i <= n; ++ i)
212 {
213 if( _nil_to_sentinel)
214 {
215 if( lua_isnil( L, i))
216 {
217 lua_pushlightuserdata( L, nil_sentinel);
218 lua_replace( L, i);
219 }
220 }
221 else
222 {
223 if( lua_touserdata( L, i) == nil_sentinel)
224 {
225 lua_pushnil( L);
226 lua_replace( L, i);
227 }
228 }
229 }
153} 230}
154 231
155/* 232/*
@@ -197,6 +274,7 @@ void close_keepers(void)
197 lua_close( GKeepers[i].L); 274 lua_close( GKeepers[i].L);
198 GKeepers[i].L = 0; 275 GKeepers[i].L = 0;
199 //assert( GKeepers[i].count == 0); 276 //assert( GKeepers[i].count == 0);
277 MUTEX_FREE( &GKeepers[i].lock_);
200 } 278 }
201 if( GKeepers) free( GKeepers); 279 if( GKeepers) free( GKeepers);
202 GKeepers = NULL; 280 GKeepers = NULL;
diff --git a/src/keeper.h b/src/keeper.h
index e959c7c..0990846 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -8,9 +8,11 @@ struct s_Keeper
8 //int count; 8 //int count;
9}; 9};
10 10
11const char *init_keepers( int const _nbKeepers); 11char const* init_keepers( int const _nbKeepers, lua_CFunction _on_state_create);
12void populate_keepers( lua_State *L);
12struct s_Keeper *keeper_acquire( const void *ptr); 13struct s_Keeper *keeper_acquire( const void *ptr);
13void keeper_release( struct s_Keeper *K); 14void keeper_release( struct s_Keeper *K);
15void keeper_toggle_nil_sentinels( lua_State *L, int _val_i, int _nil_to_sentinel);
14int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index); 16int keeper_call( lua_State *K, char const *func_name, lua_State *L, void *linda, uint_t starting_index);
15void close_keepers(void); 17void close_keepers(void);
16 18
diff --git a/src/keeper.lua b/src/keeper.lua
index 2c38c0b..77bf880 100644
--- a/src/keeper.lua
+++ b/src/keeper.lua
@@ -34,29 +34,27 @@ THE SOFTWARE.
34=============================================================================== 34===============================================================================
35]]-- 35]]--
36 36
37-- unique key instead of 'nil' in queues
38--
39assert( nil_sentinel )
40
41-- We only need to have base and table libraries (and io for debugging) 37-- We only need to have base and table libraries (and io for debugging)
42-- 38--
43local table_remove= assert( table.remove ) 39local table_concat = assert( table.concat)
44local table_concat= assert( table.concat ) 40local table_insert = assert( table.insert)
41local table_remove = assert( table.remove)
42local select, unpack = assert( select), assert( unpack)
45 43
46--[[ 44--[[
47local function WR(...) 45local function WR(...)
48 if io then 46 if io then
49 io.stderr:write( table_concat({...},'\t').."\n" ) 47 io.stderr:write( table_concat({...},'\t').."\n" )
50 end 48 end
51end 49end
52 50
53local function DEBUG(title,ud,key) 51local function DEBUG(title,ud,key)
54 assert( title and ud and key ) 52 assert( title and ud and key )
55 53
56 local data,incoming,_= tables(ud) 54 local data,_= tables(ud)
57 55
58 local s= tostring(data[key]) 56 local s= tostring(data[key])
59 for _,v in ipairs( incoming[key] or {} ) do 57 for _,v in ipairs( data[key] or {} ) do
60 s= s..", "..tostring(v) 58 s= s..", "..tostring(v)
61 end 59 end
62 WR( "*** "..title.." ("..tostring(key).."): ", s ) 60 WR( "*** "..title.." ("..tostring(key).."): ", s )
@@ -64,34 +62,64 @@ end
64--]] 62--]]
65 63
66----- 64-----
67-- Actual data store 65-- FIFO for a key
68--
69-- { [linda_deep_ud]= { key= val [, ...] }
70-- ...
71-- }
72-- 66--
73local _data= {} 67
68local fifo_new = function()
69 return { first = 1, count = 0}
70end
71
72local fifo_push = function( fifo, ...)
73 local first, count, added = fifo.first, fifo.count, select( '#', ...)
74 local start = first + count - 1
75 for i = 1, added do
76 fifo[start + i] = select( i, ...)
77 end
78 fifo.count = count + added
79end
80
81local fifo_peek = function( fifo, count)
82 if count <= fifo.count then
83 local first = fifo.first
84 local last = first + count - 1
85 return unpack( fifo, first, last)
86 end
87end
88
89local fifo_pop = function( fifo, count)
90 if count > fifo.count then error("list is too short") end
91 local first = fifo.first
92 local last = first + count - 1
93 local out = { unpack( fifo, first, last)}
94 for i = first, last do
95 fifo[i] = nil
96 end
97 fifo.first = first + count
98 fifo.count = fifo.count - count
99 return unpack( out)
100end
101
74 102
75----- 103-----
76-- Entries queued for use when the existing 'data[ud][key]' entry is consumed. 104-- Actual data store
77-- 105--
78-- { [linda_deep_ud]= { key= { val [, ... } [, ...] } 106-- { [linda_deep_ud]= { key= { val [, ... ] } [, ...] }
79-- ... 107-- ...
80-- } 108-- }
81-- 109--
82local _incoming= {} 110local _data= {}
83 111
84----- 112-----
85-- Length limits (if any) for queues 113-- Length limits (if any) for queues
86-- 114--
87-- 0: don't queue values at all; ':send()' waits if the slot is not vacant 115-- 0: don't queue values at all; ':send()' waits if the slot is not vacant
88-- N: allow N values to be queued (slot itself + N-1); wait if full 116-- N: allow N values to be queued (slot itself + N-1); wait if full
89-- nil: no limits, '_incoming' may grow endlessly 117-- nil: no limits, '_data' may grow endlessly
90-- 118--
91local _limits= {} 119local _limits= {}
92 120
93----- 121-----
94-- data_tbl, incoming_tbl, limits_tbl = tables( linda_deep_ud ) 122-- data_tbl, limits_tbl = tables( linda_deep_ud )
95-- 123--
96-- Gives appropriate tables for a certain Linda (creates them if needed) 124-- Gives appropriate tables for a certain Linda (creates them if needed)
97-- 125--
@@ -100,14 +128,13 @@ local function tables( ud )
100 -- 128 --
101 if not _data[ud] then 129 if not _data[ud] then
102 _data[ud]= {} 130 _data[ud]= {}
103 _incoming[ud]= {}
104 _limits[ud]= {} 131 _limits[ud]= {}
105 end 132 end
106 return _data[ud], _incoming[ud], _limits[ud] 133 return _data[ud], _limits[ud]
107end 134end
108 135
109----- 136-----
110-- bool= send( linda_deep_ud, key, ... ) 137-- bool= send( linda_deep_ud, key, ...)
111-- 138--
112-- Send new data (1..N) to 'key' slot. This send is atomic; all the values 139-- Send new data (1..N) to 'key' slot. This send is atomic; all the values
113-- end up one after each other (this is why having possibility for sending 140-- end up one after each other (this is why having possibility for sending
@@ -118,42 +145,27 @@ end
118-- Returns: 'true' if all the values were placed 145-- Returns: 'true' if all the values were placed
119-- 'false' if sending would exceed the queue limit (wait & retry) 146-- 'false' if sending would exceed the queue limit (wait & retry)
120-- 147--
121function send( ud, key, ... ) 148function send( ud, key, ...)
122 149
123 local data,incoming,limits= tables(ud) 150 local data, limits = tables( ud)
124 151
125 local n= select('#',...) 152 local n = select( '#', ...)
126 if n==0 then return true end -- nothing to send
127 153
128 -- Initialize queue for all keys that have been used with ':send()' 154 -- Initialize queue for all keys that have been used with ':send()'
129 -- 155 --
130 if incoming[key]==nil then 156 if data[key] == nil then
131 incoming[key]= {} 157 data[key] = fifo_new()
132 end 158 end
159 local fifo = data[key]
133 160
134 local len= data[key] and 1+#incoming[key] or 0 161 local len = fifo.count
135 local m= limits[key] 162 local m = limits[key]
136 163
137 if m and len+n > m then 164 if m and len+n > m then
138 return false -- would exceed the limit; try again later 165 return false -- would exceed the limit; try again later
139 end 166 end
140 167
141 for i=1,n do 168 fifo_push( fifo, ...)
142 local val= select(i,...)
143
144 -- 'nil' in the data replaced by sentinel
145 if val==nil then
146 val= nil_sentinel
147 end
148
149 if len==0 then
150 data[key]= val
151 len= 1
152 else
153 incoming[key][len]= val
154 len= len+1
155 end
156 end
157 return true 169 return true
158end 170end
159 171
@@ -164,81 +176,125 @@ end
164-- Read any of the given keys, consuming the data found. Keys are read in 176-- Read any of the given keys, consuming the data found. Keys are read in
165-- order. 177-- order.
166-- 178--
167function receive( ud, ... ) 179function receive( ud, ...)
168 180
169 local data,incoming,_= tables(ud) 181 local data, _ = tables( ud)
170 182
171 for i=1,select('#',...) do 183 for i = 1, select( '#', ...) do
172 local key= select(i,...) 184 local key = select( i, ...)
173 local val= data[key] 185 local fifo = data[key]
174 186 if fifo and fifo.count > 0 then
175 if val~=nil then 187 local val = fifo_pop( fifo, 1)
176 if incoming[key] and incoming[key][1]~=nil then 188 if val ~= nil then
177 -- pop [1] from 'incoming[key]' into the actual slot 189 return val, key
178 data[key]= table_remove( incoming[key], 1 )
179 else
180 data[key]= nil -- empty the slot
181 end
182 if val==nil_sentinel then
183 val= nil
184 end 190 end
185 return val, key
186 end 191 end
187 end 192 end
188 --return nil 193end
194
195
196-----
197-- [val1, ... valCOUNT]= receive_batched( linda_deep_ud, batch_sentinel, key , COUNT)
198--
199-- Read any of the given keys, consuming the data found. Keys are read in
200-- order.
201--
202receive_batched = function( ud, batch_sentinel, key, count)
203 if count > 0 then
204 local data, _ = tables( ud)
205 local fifo = data[key]
206 if fifo and fifo.count >= count then
207 return fifo_pop( fifo, count)
208 end
209 end
189end 210end
190 211
191 212
192----- 213-----
193-- = limit( linda_deep_ud, key, uint ) 214-- = limit( linda_deep_ud, key, uint )
194-- 215--
195function limit( ud, key, n ) 216function limit( ud, key, n)
196 217
197 local _,_,limits= tables(ud) 218 local _, limits = tables( ud)
198 219
199 limits[key]= n 220 limits[key] = n
200end 221end
201 222
202 223
203----- 224-----
204-- void= set( linda_deep_ud, key, [val] ) 225-- void= set( linda_deep_ud, key, [val] )
205-- 226--
206function set( ud, key, val ) 227function set( ud, key, val)
207 228
208 local data,incoming,_= tables(ud) 229 local data, _ = tables( ud)
209 230
210 -- Setting a key to 'nil' really clears it; only queing uses sentinels. 231 -- Setting a key to 'nil' really clears it; only queing uses sentinels.
211 -- 232 --
212 data[key]= val 233 if val ~= nil then
213 incoming[key]= nil 234 local fifo = fifo_new()
235 fifo_push( fifo, val)
236 data[key] = fifo
237 else
238 data[key] = nil
239 end
214end 240end
215 241
216 242
217----- 243-----
218-- [val]= get( linda_deep_ud, key ) 244-- [val]= get( linda_deep_ud, key )
219-- 245--
220function get( ud, key ) 246function get( ud, key)
247 local data, _ = tables( ud)
248 local fifo = data[key]
249 return fifo and fifo_peek( fifo, 1)
250end
221 251
222 local data,_,_= tables(ud)
223 252
224 local val= data[key] 253-----
225 if val==nil_sentinel then 254-- [val]= count( linda_deep_ud, ...)
226 val= nil 255--
256-- 3 modes of operation
257-- linda:count() -> returns a table of key/count pairs
258-- linda:count(key) returns the number of items waiting in the key
259-- linda:count(key,...) -> returns a table telling, for each key, the number of items
260function count( ud, ...)
261 local data, _ = tables( ud)
262 local n = select( '#', ...)
263 if n == 0 then
264 local out
265 for key, _ in pairs( data) do
266 local fifo = data[key]
267 local count = fifo and fifo.count or 0
268 out = out or {}
269 out[key] = count
270 found = true
271 end
272 return out
273 elseif n == 1 then
274 local key = ...
275 local fifo = data[key]
276 return fifo and fifo.count or nil
277 else -- more than 1 key
278 local out
279 for i = 1, n do
280 local key = select( i, ...)
281 local fifo = data[key]
282 local count = fifo and fifo.count or nil
283 out = out or {}
284 out[key] = count
285 end
286 return out
227 end 287 end
228 return val
229end 288end
230 289
231 290
232----- 291-----
233-- void= clear( linda_deep_ud ) 292-- void= clear( linda_deep_ud)
234-- 293--
235-- Clear the data structures used for a Linda (at its destructor) 294-- Clear the data structures used for a Linda (at its destructor)
236-- 295--
237function clear( ud ) 296function clear( ud)
238 297
239 _data[ud]= nil 298 _data[ud]= nil
240 _incoming[ud]= nil
241 _limits[ud]= nil 299 _limits[ud]= nil
242end 300end
243
244
diff --git a/src/lanes.c b/src/lanes.c
index 0c943aa..513a006 100644
--- a/src/lanes.c
+++ b/src/lanes.c
@@ -51,7 +51,7 @@
51 * ... 51 * ...
52 */ 52 */
53 53
54const char *VERSION= "2.1.0"; 54char const* VERSION = "3.1.0";
55 55
56/* 56/*
57=============================================================================== 57===============================================================================
@@ -139,7 +139,7 @@ struct s_lane {
139 // M: sets to FALSE, flags TRUE for cancel request 139 // M: sets to FALSE, flags TRUE for cancel request
140 // S: reads to see if cancel is requested 140 // S: reads to see if cancel is requested
141 141
142#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) 142#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
143 SIGNAL_T done_signal_; 143 SIGNAL_T done_signal_;
144 // 144 //
145 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN) 145 // M: Waited upon at lane ending (if Posix with no PTHREAD_TIMEDJOIN)
@@ -149,7 +149,7 @@ struct s_lane {
149 // 149 //
150 // Lock required by 'done_signal' condition variable, protecting 150 // Lock required by 'done_signal' condition variable, protecting
151 // lane status changes to DONE/ERROR_ST/CANCELLED. 151 // lane status changes to DONE/ERROR_ST/CANCELLED.
152#endif 152#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
153 153
154 volatile enum { 154 volatile enum {
155 NORMAL, // normal master side state 155 NORMAL, // normal master side state
@@ -249,6 +249,20 @@ static void linda_id( lua_State*, char const * const which);
249#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) 249#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n ))
250 250
251 251
252static void check_key_types( lua_State *L, int _start, int _end)
253{
254 int i;
255 for( i = _start; i <= _end; ++ i)
256 {
257 int t = lua_type( L, i);
258 if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA)
259 {
260 continue;
261 }
262 luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i);
263 }
264}
265
252/* 266/*
253* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) 267* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... )
254* 268*
@@ -271,15 +285,24 @@ LUAG_FUNC( linda_send)
271 if( lua_isnumber(L, 2)) 285 if( lua_isnumber(L, 2))
272 { 286 {
273 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); 287 timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
274 key_i++; 288 ++ key_i;
275 } 289 }
276 else if( lua_isnil( L, 2)) 290 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
277 { 291 {
278 key_i++; 292 ++ key_i;
279 } 293 }
280 294
281 if( lua_isnil( L, key_i)) 295 // make sure the keys are of a valid type
282 luaL_error( L, "nil key" ); 296 check_key_types( L, key_i, key_i);
297
298 // make sure there is something to send
299 if( (uint_t)lua_gettop( L) == key_i)
300 {
301 luaL_error( L, "no data to send");
302 }
303
304 // convert nils to some special non-nil sentinel in sent values
305 keeper_toggle_nil_sentinels( L, key_i + 1, 1);
283 306
284 STACK_GROW(L, 1); 307 STACK_GROW(L, 1);
285 { 308 {
@@ -315,11 +338,11 @@ LUAG_FUNC( linda_send)
315 338
316 cancel = cancel_test( L); // testing here causes no delays 339 cancel = cancel_test( L); // testing here causes no delays
317 if (cancel) 340 if (cancel)
341 {
318 break; 342 break;
343 }
319 344
320 // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" 345 // change status of lane to "waiting"
321 //
322#if 1
323 { 346 {
324 struct s_lane *s; 347 struct s_lane *s;
325 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 348 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
@@ -338,6 +361,7 @@ LUAG_FUNC( linda_send)
338 ASSERT_L( s->waiting_on == NULL); 361 ASSERT_L( s->waiting_on == NULL);
339 s->waiting_on = &linda->read_happened; 362 s->waiting_on = &linda->read_happened;
340 } 363 }
364 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
341 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) 365 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout))
342 { 366 {
343 if( s) 367 if( s)
@@ -353,12 +377,6 @@ LUAG_FUNC( linda_send)
353 s->status = prev_status; 377 s->status = prev_status;
354 } 378 }
355 } 379 }
356#else
357 // K lock will be released for the duration of wait and re-acquired
358 //
359 if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout))
360 break; // timeout
361#endif
362 } 380 }
363 STACK_END( KL, 0) 381 STACK_END( KL, 0)
364 keeper_release( K); 382 keeper_release( K);
@@ -379,18 +397,23 @@ LUAG_FUNC( linda_send)
379 397
380 398
381/* 399/*
382* [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) 400 * 2 modes of operation
383* 401 * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] )
384* Receive a value from Linda, consuming it. 402 * Consumes a single value from the Linda, in any key.
385* 403 * Returns: received value (which is consumed from the slot), and the key which had it
386* Returns: value received (which is consumed from the slot) 404
387* key which had it 405 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, COUNT)
388*/ 406 * Consumes COUNT values from the linda, from a single key.
407 * returns the COUNT consumed values, or nil if there weren't enough values to consume
408 *
409 */
410#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475"
389LUAG_FUNC( linda_receive) 411LUAG_FUNC( linda_receive)
390{ 412{
391 struct s_Linda *linda = lua_toLinda( L, 1); 413 struct s_Linda *linda = lua_toLinda( L, 1);
392 int pushed; 414 int pushed, expected_pushed;
393 bool_t cancel = FALSE; 415 bool_t cancel = FALSE;
416 char *keeper_receive;
394 417
395 time_d timeout = -1.0; 418 time_d timeout = -1.0;
396 uint_t key_i = 2; 419 uint_t key_i = 2;
@@ -400,26 +423,44 @@ LUAG_FUNC( linda_receive)
400 if( lua_isnumber( L, 2)) 423 if( lua_isnumber( L, 2))
401 { 424 {
402 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 425 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2));
403 key_i++; 426 ++ key_i;
427 }
428 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key
429 {
430 ++ key_i;
431 }
432
433 // make sure the keys are of a valid type
434 check_key_types( L, key_i, lua_gettop( L));
435
436 // are we in batched mode?
437 lua_pushliteral( L, BATCH_SENTINEL);
438 if( lua_equal( L, key_i, -1))
439 {
440 keeper_receive = "receive_batched";
441 expected_pushed = (int)luaL_checkinteger( L, key_i + 2);
404 } 442 }
405 else if( lua_isnil( L, 2)) 443 else
406 { 444 {
407 key_i++; 445 keeper_receive = "receive";
446 expected_pushed = 2;
408 } 447 }
448 lua_pop( L, 1);
409 449
410 { 450 {
411 struct s_Keeper *K = keeper_acquire( linda); 451 struct s_Keeper *K = keeper_acquire( linda);
412 for( ;;) 452 for( ;;)
413 { 453 {
414 pushed = keeper_call( K->L, "receive", L, linda, key_i); 454 pushed = keeper_call( K->L, keeper_receive, L, linda, key_i);
415 if( pushed < 0) 455 if( pushed < 0)
416 { 456 {
417 break; 457 break;
418 } 458 }
419 if( pushed > 0) 459 if( pushed > 0)
420 { 460 {
421 ASSERT_L( pushed == 2); 461 ASSERT_L( pushed == expected_pushed);
422 462 // replace sentinels with real nils
463 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0);
423 // To be done from within the 'K' locking area 464 // To be done from within the 'K' locking area
424 // 465 //
425 SIGNAL_ALL( &linda->read_happened); 466 SIGNAL_ALL( &linda->read_happened);
@@ -438,9 +479,7 @@ LUAG_FUNC( linda_receive)
438 break; 479 break;
439 } 480 }
440 481
441 // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" 482 // change status of lane to "waiting"
442 //
443#if 1
444 { 483 {
445 struct s_lane *s; 484 struct s_lane *s;
446 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 485 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
@@ -459,6 +498,7 @@ LUAG_FUNC( linda_receive)
459 ASSERT_L( s->waiting_on == NULL); 498 ASSERT_L( s->waiting_on == NULL);
460 s->waiting_on = &linda->write_happened; 499 s->waiting_on = &linda->write_happened;
461 } 500 }
501 // not enough data to read: wakeup when data was sent, or when timeout is reached
462 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) 502 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout))
463 { 503 {
464 if( s) 504 if( s)
@@ -474,12 +514,6 @@ LUAG_FUNC( linda_receive)
474 s->status = prev_status; 514 s->status = prev_status;
475 } 515 }
476 } 516 }
477#else
478 // Release the K lock for the duration of wait, and re-acquire
479 //
480 if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout))
481 break;
482#endif
483 } 517 }
484 keeper_release( K); 518 keeper_release( K);
485 } 519 }
@@ -501,6 +535,7 @@ LUAG_FUNC( linda_receive)
501* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) 535* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] )
502* 536*
503* Set a value to Linda. 537* Set a value to Linda.
538* TODO: what do we do if we set to non-nil and limit is 0?
504* 539*
505* Existing slot value is replaced, and possible queue entries removed. 540* Existing slot value is replaced, and possible queue entries removed.
506*/ 541*/
@@ -510,9 +545,14 @@ LUAG_FUNC( linda_set)
510 bool_t has_value = !lua_isnil( L, 3); 545 bool_t has_value = !lua_isnil( L, 3);
511 luaL_argcheck( L, linda, 1, "expected a linda object!"); 546 luaL_argcheck( L, linda, 1, "expected a linda object!");
512 547
548 // make sure the key is of a valid type
549 check_key_types( L, 2, 2);
550
513 { 551 {
552 int pushed;
514 struct s_Keeper *K = keeper_acquire( linda); 553 struct s_Keeper *K = keeper_acquire( linda);
515 int pushed = keeper_call( K->L, "set", L, linda, 2); 554 // no nil->sentinel toggling, we really clear the linda contents for the given key with a set()
555 pushed = keeper_call( K->L, "set", L, linda, 2);
516 if( pushed >= 0) // no error? 556 if( pushed >= 0) // no error?
517 { 557 {
518 ASSERT_L( pushed == 0); 558 ASSERT_L( pushed == 0);
@@ -537,20 +577,55 @@ LUAG_FUNC( linda_set)
537 577
538 578
539/* 579/*
580 * [val] = linda_count( linda_ud, [key [, ...]])
581 *
582 * Get a count of the pending elements in the specified keys
583 */
584LUAG_FUNC( linda_count)
585{
586 struct s_Linda *linda= lua_toLinda( L, 1);
587 int pushed;
588
589 luaL_argcheck( L, linda, 1, "expected a linda object!");
590 // make sure the keys are of a valid type
591 check_key_types( L, 2, lua_gettop( L));
592
593 {
594 struct s_Keeper *K = keeper_acquire( linda);
595 pushed = keeper_call( K->L, "count", L, linda, 2);
596 keeper_release( K);
597 if( pushed < 0)
598 {
599 luaL_error( L, "tried to count an invalid key");
600 }
601 }
602 return pushed;
603}
604
605
606/*
540* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) 607* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata )
541* 608*
542* Get a value from Linda. 609* Get a value from Linda.
610* TODO: add support to get multiple values?
543*/ 611*/
544LUAG_FUNC( linda_get) 612LUAG_FUNC( linda_get)
545{ 613{
546 struct s_Linda *linda= lua_toLinda( L, 1); 614 struct s_Linda *linda= lua_toLinda( L, 1);
547 int pushed; 615 int pushed;
616
548 luaL_argcheck( L, linda, 1, "expected a linda object!"); 617 luaL_argcheck( L, linda, 1, "expected a linda object!");
618 // make sure the key is of a valid type
619 check_key_types( L, 2, 2);
549 620
550 { 621 {
551 struct s_Keeper *K = keeper_acquire( linda); 622 struct s_Keeper *K = keeper_acquire( linda);
552 pushed = keeper_call( K->L, "get", L, linda, 2); 623 pushed = keeper_call( K->L, "get", L, linda, 2);
553 ASSERT_L( pushed==0 || pushed==1 ); 624 ASSERT_L( pushed==0 || pushed==1 );
625 if( pushed > 0)
626 {
627 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0);
628 }
554 keeper_release(K); 629 keeper_release(K);
555 // must trigger error after keeper state has been released 630 // must trigger error after keeper state has been released
556 if( pushed < 0) 631 if( pushed < 0)
@@ -571,7 +646,10 @@ LUAG_FUNC( linda_get)
571LUAG_FUNC( linda_limit) 646LUAG_FUNC( linda_limit)
572{ 647{
573 struct s_Linda *linda= lua_toLinda( L, 1 ); 648 struct s_Linda *linda= lua_toLinda( L, 1 );
649
574 luaL_argcheck( L, linda, 1, "expected a linda object!"); 650 luaL_argcheck( L, linda, 1, "expected a linda object!");
651 // make sure the key is of a valid type
652 check_key_types( L, 2, 2);
575 653
576 { 654 {
577 struct s_Keeper *K = keeper_acquire( linda); 655 struct s_Keeper *K = keeper_acquire( linda);
@@ -608,6 +686,57 @@ LUAG_FUNC( linda_deep ) {
608 686
609 687
610/* 688/*
689* string = linda:__tostring( linda_ud)
690*
691* Return the stringification of a linda
692*
693* Useful for concatenation or debugging purposes
694*/
695LUAG_FUNC( linda_tostring)
696{
697 char text[32];
698 struct s_Linda *linda = lua_toLinda( L, 1);
699 luaL_argcheck( L, linda, 1, "expected a linda object!");
700 sprintf( text, "linda: %p", linda);
701 lua_pushstring( L, text);
702 return 1;
703}
704
705
706/*
707* string = linda:__concat( a, b)
708*
709* Return the concatenation of a pair of items, one of them being a linda
710*
711* Useful for concatenation or debugging purposes
712*/
713LUAG_FUNC( linda_concat)
714{
715 struct s_Linda *linda1 = lua_toLinda( L, 1);
716 struct s_Linda *linda2 = lua_toLinda( L, 2);
717 // lua semantics should enforce that one of the parameters we got is a linda
718 luaL_argcheck( L, linda1 || linda2, 1, "expected a linda object!");
719 // replace the lindas by their string equivalents in the stack
720 if ( linda1)
721 {
722 char text[32];
723 sprintf( text, "linda: %p", linda1);
724 lua_pushstring( L, text);
725 lua_replace( L, 1);
726 }
727 if ( linda2)
728 {
729 char text[32];
730 sprintf( text, "linda: %p", linda2);
731 lua_pushstring( L, text);
732 lua_replace( L, 2);
733 }
734 // concat the result
735 lua_concat( L, 2);
736 return 1;
737}
738
739/*
611* Identity function of a shared userdata object. 740* Identity function of a shared userdata object.
612* 741*
613* lightuserdata= linda_id( "new" [, ...] ) 742* lightuserdata= linda_id( "new" [, ...] )
@@ -658,10 +787,11 @@ static void linda_id( lua_State *L, char const * const which)
658 /* Clean associated structures in the keeper state. 787 /* Clean associated structures in the keeper state.
659 */ 788 */
660 K= keeper_acquire(s); 789 K= keeper_acquire(s);
790 if( K) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup)
661 { 791 {
662 keeper_call( K->L, "clear", L, s, 0 ); 792 keeper_call( K->L, "clear", L, s, 0 );
793 keeper_release(K);
663 } 794 }
664 keeper_release(K);
665 795
666 /* There aren't any lanes waiting on these lindas, since all proxies 796 /* There aren't any lanes waiting on these lindas, since all proxies
667 * have been gc'ed. Right? 797 * have been gc'ed. Right?
@@ -678,29 +808,43 @@ static void linda_id( lua_State *L, char const * const which)
678 // metatable is its own index 808 // metatable is its own index
679 lua_pushvalue( L, -1); 809 lua_pushvalue( L, -1);
680 lua_setfield( L, -2, "__index"); 810 lua_setfield( L, -2, "__index");
811
681 // protect metatable from external access 812 // protect metatable from external access
682 lua_pushboolean( L, 0); 813 lua_pushboolean( L, 0);
683 lua_setfield( L, -2, "__metatable"); 814 lua_setfield( L, -2, "__metatable");
815
816 lua_pushcfunction( L, LG_linda_tostring);
817 lua_setfield( L, -2, "__tostring");
818
819 lua_pushcfunction( L, LG_linda_concat);
820 lua_setfield( L, -2, "__concat");
821
684 // 822 //
685 // [-1]: linda metatable 823 // [-1]: linda metatable
686 lua_pushcfunction( L, LG_linda_send ); 824 lua_pushcfunction( L, LG_linda_send );
687 lua_setfield( L, -2, "send" ); 825 lua_setfield( L, -2, "send" );
688 826
689 lua_pushcfunction( L, LG_linda_receive ); 827 lua_pushcfunction( L, LG_linda_receive );
690 lua_setfield( L, -2, "receive" ); 828 lua_setfield( L, -2, "receive" );
691 829
692 lua_pushcfunction( L, LG_linda_limit ); 830 lua_pushcfunction( L, LG_linda_limit );
693 lua_setfield( L, -2, "limit" ); 831 lua_setfield( L, -2, "limit" );
694 832
695 lua_pushcfunction( L, LG_linda_set ); 833 lua_pushcfunction( L, LG_linda_set );
696 lua_setfield( L, -2, "set" ); 834 lua_setfield( L, -2, "set" );
697 835
836 lua_pushcfunction( L, LG_linda_count );
837 lua_setfield( L, -2, "count" );
838
698 lua_pushcfunction( L, LG_linda_get ); 839 lua_pushcfunction( L, LG_linda_get );
699 lua_setfield( L, -2, "get" ); 840 lua_setfield( L, -2, "get" );
700 841
701 lua_pushcfunction( L, LG_linda_deep ); 842 lua_pushcfunction( L, LG_linda_deep );
702 lua_setfield( L, -2, "deep" ); 843 lua_setfield( L, -2, "deep" );
703 844
845 lua_pushliteral( L, BATCH_SENTINEL);
846 lua_setfield(L, -2, "batched");
847
704 STACK_END(L,1) 848 STACK_END(L,1)
705 } 849 }
706 else if( strcmp( which, "module") == 0) 850 else if( strcmp( which, "module") == 0)
@@ -714,6 +858,11 @@ static void linda_id( lua_State *L, char const * const which)
714 } 858 }
715} 859}
716 860
861/*
862 * ud = lanes.linda()
863 *
864 * returns a linda object
865 */
717LUAG_FUNC( linda) 866LUAG_FUNC( linda)
718{ 867{
719 return luaG_deep_userdata( L, linda_id); 868 return luaG_deep_userdata( L, linda_id);
@@ -845,8 +994,9 @@ static void selfdestruct_add( struct s_lane *s ) {
845/* 994/*
846* A free-running lane has ended; remove it from selfdestruct chain 995* A free-running lane has ended; remove it from selfdestruct chain
847*/ 996*/
848static void selfdestruct_remove( struct s_lane *s ) { 997static bool_t selfdestruct_remove( struct s_lane *s )
849 998{
999 bool_t found = FALSE;
850 MUTEX_LOCK( &selfdestruct_cs ); 1000 MUTEX_LOCK( &selfdestruct_cs );
851 { 1001 {
852 // Make sure (within the MUTEX) that we actually are in the chain 1002 // Make sure (within the MUTEX) that we actually are in the chain
@@ -855,7 +1005,6 @@ static void selfdestruct_remove( struct s_lane *s ) {
855 // 1005 //
856 if (s->selfdestruct_next != NULL) { 1006 if (s->selfdestruct_next != NULL) {
857 struct s_lane **ref= (struct s_lane **) &selfdestruct_first; 1007 struct s_lane **ref= (struct s_lane **) &selfdestruct_first;
858 bool_t found= FALSE;
859 1008
860 while( *ref != SELFDESTRUCT_END ) { 1009 while( *ref != SELFDESTRUCT_END ) {
861 if (*ref == s) { 1010 if (*ref == s) {
@@ -870,6 +1019,7 @@ static void selfdestruct_remove( struct s_lane *s ) {
870 } 1019 }
871 } 1020 }
872 MUTEX_UNLOCK( &selfdestruct_cs ); 1021 MUTEX_UNLOCK( &selfdestruct_cs );
1022 return found;
873} 1023}
874 1024
875// Initialized by 'init_once_LOCKED()': the deep userdata Linda object 1025// Initialized by 'init_once_LOCKED()': the deep userdata Linda object
@@ -880,9 +1030,10 @@ volatile DEEP_PRELUDE *timer_deep; // = NULL
880/* 1030/*
881* Process end; cancel any still free-running threads 1031* Process end; cancel any still free-running threads
882*/ 1032*/
883static void selfdestruct_atexit( void ) 1033static int selfdestruct_atexit( lua_State *L)
884{ 1034{
885 if (selfdestruct_first == SELFDESTRUCT_END) return; // no free-running threads 1035 (void)L; // unused
1036 if (selfdestruct_first == SELFDESTRUCT_END) return 0; // no free-running threads
886 1037
887 // Signal _all_ still running threads to exit (including the timer thread) 1038 // Signal _all_ still running threads to exit (including the timer thread)
888 // 1039 //
@@ -899,7 +1050,7 @@ static void selfdestruct_atexit( void )
899 // signal the linda the wake up the thread so that it can react to the cancel query 1050 // signal the linda the wake up the thread so that it can react to the cancel query
900 // let us hope we never land here with a pointer on a linda that has been destroyed... 1051 // let us hope we never land here with a pointer on a linda that has been destroyed...
901 SIGNAL_T *waiting_on = s->waiting_on; 1052 SIGNAL_T *waiting_on = s->waiting_on;
902 s->waiting_on = NULL; 1053 //s->waiting_on = NULL; // useful, or not?
903 SIGNAL_ALL( waiting_on); 1054 SIGNAL_ALL( waiting_on);
904 } 1055 }
905 s = s->selfdestruct_next; 1056 s = s->selfdestruct_next;
@@ -969,6 +1120,7 @@ static void selfdestruct_atexit( void )
969 // 1120 //
970 if ( selfdestruct_first != SELFDESTRUCT_END ) { 1121 if ( selfdestruct_first != SELFDESTRUCT_END ) {
971 unsigned n=0; 1122 unsigned n=0;
1123#if 0
972 MUTEX_LOCK( &selfdestruct_cs ); 1124 MUTEX_LOCK( &selfdestruct_cs );
973 { 1125 {
974 struct s_lane *s= selfdestruct_first; 1126 struct s_lane *s= selfdestruct_first;
@@ -983,15 +1135,14 @@ static void selfdestruct_atexit( void )
983 // and works without the block (so let's leave those lanes running) 1135 // and works without the block (so let's leave those lanes running)
984 // 1136 //
985//we want to free memory and such when we exit. 1137//we want to free memory and such when we exit.
986#if 0
987 // 2.0.2: at least timer lane is still here 1138 // 2.0.2: at least timer lane is still here
988 // 1139 //
989 DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n )); 1140 DEBUGEXEC(fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n ));
1141 n=0;
990#else 1142#else
991 // first thing we did was to raise the linda signals the threads were waiting on (if any) 1143 // first thing we did was to raise the linda signals the threads were waiting on (if any)
992 // therefore, any well-behaved thread should be in CANCELLED state 1144 // therefore, any well-behaved thread should be in CANCELLED state
993 // these are not running, and the state can be closed 1145 // these are not running, and the state can be closed
994 n=0;
995 MUTEX_LOCK( &selfdestruct_cs ); 1146 MUTEX_LOCK( &selfdestruct_cs );
996 { 1147 {
997 struct s_lane *s= selfdestruct_first; 1148 struct s_lane *s= selfdestruct_first;
@@ -999,8 +1150,19 @@ static void selfdestruct_atexit( void )
999 { 1150 {
1000 struct s_lane *next_s= s->selfdestruct_next; 1151 struct s_lane *next_s= s->selfdestruct_next;
1001 s->selfdestruct_next= NULL; // detach from selfdestruct chain 1152 s->selfdestruct_next= NULL; // detach from selfdestruct chain
1002 THREAD_KILL( &s->thread); 1153 if( !THREAD_ISNULL( s->thread)) // can be NULL if previous 'soft' termination succeeded
1154 {
1155 THREAD_KILL( &s->thread);
1156#if THREADAPI == THREADAPI_PTHREAD
1157 // pthread: make sure the thread is really stopped!
1158 THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status);
1159#endif // THREADAPI == THREADAPI_PTHREAD
1160 }
1003 // NO lua_close() in this case because we don't know where execution of the state was interrupted 1161 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1162#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1163 SIGNAL_FREE( &s->done_signal_);
1164 MUTEX_FREE( &s->done_lock_);
1165#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1004 free( s); 1166 free( s);
1005 s = next_s; 1167 s = next_s;
1006 n++; 1168 n++;
@@ -1013,6 +1175,7 @@ static void selfdestruct_atexit( void )
1013#endif 1175#endif
1014 } 1176 }
1015 close_keepers(); 1177 close_keepers();
1178 return 0;
1016} 1179}
1017 1180
1018 1181
@@ -1205,15 +1368,15 @@ void SetThreadName( DWORD dwThreadID, char const *_threadName)
1205 1368
1206LUAG_FUNC( set_debug_threadname) 1369LUAG_FUNC( set_debug_threadname)
1207{ 1370{
1208 char const *threadName;
1209 luaL_checktype( L, -1, LUA_TSTRING); 1371 luaL_checktype( L, -1, LUA_TSTRING);
1210 threadName = lua_tostring( L, -1);
1211
1212#if defined PLATFORM_WIN32 && !defined __GNUC__ 1372#if defined PLATFORM_WIN32 && !defined __GNUC__
1213 // to see thead name in Visual Studio C debugger 1373 {
1214 SetThreadName(-1, threadName); 1374 char const *threadName = lua_tostring( L, -1);
1215#endif
1216 1375
1376 // to see thead name in Visual Studio C debugger
1377 SetThreadName(-1, threadName);
1378 }
1379#endif // defined PLATFORM_WIN32 && !defined __GNUC__
1217 // to see VM name in Decoda debugger Virtual Machine window 1380 // to see VM name in Decoda debugger Virtual Machine window
1218 lua_setglobal( L, "decoda_name"); 1381 lua_setglobal( L, "decoda_name");
1219 1382
@@ -1221,11 +1384,7 @@ LUAG_FUNC( set_debug_threadname)
1221} 1384}
1222 1385
1223//--- 1386//---
1224#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 1387static THREAD_RETURN_T THREAD_CALLCONV lane_main( void *vs)
1225 static THREAD_RETURN_T __stdcall lane_main( void *vs )
1226#else
1227 static THREAD_RETURN_T lane_main( void *vs )
1228#endif
1229{ 1388{
1230 struct s_lane *s= (struct s_lane *)vs; 1389 struct s_lane *s= (struct s_lane *)vs;
1231 int rc, rc2; 1390 int rc, rc2;
@@ -1315,20 +1474,22 @@ LUAG_FUNC( set_debug_threadname)
1315 lua_newtable(L); 1474 lua_newtable(L);
1316 } 1475 }
1317 s->waiting_on = NULL; // just in case 1476 s->waiting_on = NULL; // just in case
1318 if (s->selfdestruct_next != NULL) { 1477 if( selfdestruct_remove( s)) // check and remove (under lock!)
1478 {
1319 // We're a free-running thread and no-one's there to clean us up. 1479 // We're a free-running thread and no-one's there to clean us up.
1320 // 1480 //
1321 lua_close( s->L ); 1481 lua_close( s->L );
1322 s->L = L = 0; 1482 s->L = L = 0;
1323 1483
1324 #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) 1484 #if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1325 SIGNAL_FREE( &s->done_signal_ ); 1485 SIGNAL_FREE( &s->done_signal_);
1326 MUTEX_FREE( &s->done_lock_ ); 1486 MUTEX_FREE( &s->done_lock_);
1327 #endif 1487 #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1328 selfdestruct_remove(s); // away from selfdestruct chain
1329 free(s); 1488 free(s);
1330 1489
1331 } else { 1490 }
1491 else
1492 {
1332 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them 1493 // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
1333 1494
1334 enum e_status st= 1495 enum e_status st=
@@ -1339,16 +1500,16 @@ LUAG_FUNC( set_debug_threadname)
1339 // Posix no PTHREAD_TIMEDJOIN: 1500 // Posix no PTHREAD_TIMEDJOIN:
1340 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change 1501 // 'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
1341 // 1502 //
1342 #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1503#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1343 s->status= st; 1504 MUTEX_LOCK( &s->done_lock_);
1344 #else
1345 MUTEX_LOCK( &s->done_lock_ );
1346 { 1505 {
1347 s->status= st; 1506#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1348 SIGNAL_ONE( &s->done_signal_ ); // wake up master (while 's->done_lock' is on) 1507 s->status = st;
1508#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1509 SIGNAL_ONE( &s->done_signal_); // wake up master (while 's->done_lock' is on)
1349 } 1510 }
1350 MUTEX_UNLOCK( &s->done_lock_ ); 1511 MUTEX_UNLOCK( &s->done_lock_);
1351 #endif 1512#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1352 } 1513 }
1353 return 0; // ignored 1514 return 0; // ignored
1354} 1515}
@@ -1359,22 +1520,52 @@ LUAG_FUNC( set_debug_threadname)
1359// [cancelstep_uint=0], 1520// [cancelstep_uint=0],
1360// [prio_int=0], 1521// [prio_int=0],
1361// [globals_tbl], 1522// [globals_tbl],
1523// [package_tbl],
1524// [required],
1362// [... args ...] ) 1525// [... args ...] )
1363// 1526//
1364// Upvalues: metatable to use for 'lane_ud' 1527// Upvalues: metatable to use for 'lane_ud'
1365// 1528//
1529
1530// helper function to require a module in the keeper states and in the target state
1531// source state contains module name at the top of the stack
1532static void require_one_module( lua_State *L, lua_State *L2, bool_t _fatal)
1533{
1534 size_t len;
1535 char const *name = lua_tolstring( L, -1, &len);
1536 // require the module in the target lane
1537 STACK_GROW( L2, 2);
1538 lua_getglobal( L2, "require");
1539 if( lua_isnil( L2, -1))
1540 {
1541 lua_pop( L2, 1);
1542 if( _fatal)
1543 luaL_error( L, "cannot pre-require modules without loading 'package' library first");
1544 }
1545 else
1546 {
1547 lua_pushlstring( L2, name, len);
1548 lua_pcall( L2, 1, 0, 0);
1549 // we need to require this module in the keeper states as well
1550 populate_keepers( L);
1551 }
1552}
1553
1366LUAG_FUNC( thread_new ) 1554LUAG_FUNC( thread_new )
1367{ 1555{
1368 lua_State *L2; 1556 lua_State *L2;
1369 struct s_lane *s; 1557 struct s_lane *s;
1370 struct s_lane **ud; 1558 struct s_lane **ud;
1371 1559
1372 const char *libs= lua_tostring( L, 2 ); 1560 char const* libs = lua_tostring( L, 2);
1373 uint_t cs= luaG_optunsigned( L, 3,0); 1561 lua_CFunction on_state_create = lua_iscfunction( L, 3) ? lua_tocfunction( L, 3) : NULL;
1374 int prio= (int)luaL_optinteger( L, 4,0); 1562 uint_t cs = luaG_optunsigned( L, 4, 0);
1375 uint_t glob= luaG_isany(L,5) ? 5:0; 1563 int prio = (int) luaL_optinteger( L, 5, 0);
1564 uint_t glob = luaG_isany( L, 6) ? 6 : 0;
1565 uint_t package = luaG_isany( L,7) ? 7 : 0;
1566 uint_t required = luaG_isany( L, 8) ? 8 : 0;
1376 1567
1377#define FIXED_ARGS (5) 1568#define FIXED_ARGS 8
1378 uint_t args= lua_gettop(L) - FIXED_ARGS; 1569 uint_t args= lua_gettop(L) - FIXED_ARGS;
1379 1570
1380 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) 1571 if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX)
@@ -1385,45 +1576,109 @@ LUAG_FUNC( thread_new )
1385 1576
1386 /* --- Create and prepare the sub state --- */ 1577 /* --- Create and prepare the sub state --- */
1387 1578
1388 L2 = luaL_newstate(); // uses standard 'realloc()'-based allocator, 1579 // populate with selected libraries at the same time
1389 // sets the panic callback 1580 //
1390 1581 L2 = luaG_newstate( libs, on_state_create);
1391 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" ); 1582 if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" );
1392 1583
1393 STACK_GROW( L,2 ); 1584 STACK_GROW( L, 2);
1394 1585
1395 // Setting the globals table (needs to be done before loading stdlibs, 1586 ASSERT_L( lua_gettop(L2) == 0);
1396 // and the lane function)
1397 //
1398 if (glob!=0)
1399 {
1400 STACK_CHECK(L)
1401 if (!lua_istable(L,glob))
1402 luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) );
1403 1587
1404 lua_pushvalue( L, glob ); 1588 // package.path
1589 STACK_CHECK(L)
1590 STACK_CHECK(L2)
1591 if( package)
1592 {
1593 if (lua_type(L,package) != LUA_TTABLE)
1594 luaL_error( L, "expected package as table, got %s", luaL_typename(L,package));
1595 lua_getglobal( L2, "package");
1596 if( !lua_isnil( L2, -1)) // package library not loaded: do nothing
1597 {
1598 int i;
1599 char const *entries[] = { "path", "cpath", "preload", "loaders", NULL};
1600 for( i = 0; entries[i]; ++ i)
1601 {
1602 lua_getfield( L, package, entries[i]);
1603 if( lua_isnil( L, -1))
1604 {
1605 lua_pop( L, 1);
1606 }
1607 else
1608 {
1609 luaG_inter_move( L, L2, 1); // moves the entry to L2
1610 lua_setfield( L2, -2, entries[i]); // set package[entries[i]]
1611 }
1612 }
1613 }
1614 lua_pop( L2, 1);
1615 }
1616 STACK_END(L2,0)
1617 STACK_END(L,0)
1405 1618
1406 luaG_inter_move( L, L2, 1); // moves the table to L2 1619 // modules to require in the target lane *before* the function is transfered!
1407 1620
1408 // L2 [-1]: table of globals 1621 //start by requiring lua51-lanes, since it is a bit special
1622 // it is not fatal if 'require' isn't loaded, just ignore (may cause function transfer errors later on if the lane pulls the lanes module itself)
1623 STACK_CHECK(L)
1624 STACK_CHECK(L2)
1625 lua_pushliteral( L, "lua51-lanes");
1626 require_one_module( L, L2, FALSE);
1627 lua_pop( L, 1);
1628 STACK_END(L2,0)
1629 STACK_END(L,0)
1409 1630
1410 // "You can change the global environment of a Lua thread using lua_replace" 1631 STACK_CHECK(L)
1411 // (refman-5.0.pdf p. 30) 1632 STACK_CHECK(L2)
1412 // 1633 if( required)
1413 lua_replace( L2, LUA_GLOBALSINDEX ); 1634 {
1414 STACK_END(L,0) 1635 int nbRequired = 1;
1636 // should not happen, was checked in lanes.lua before calling thread_new()
1637 if (lua_type(L, required) != LUA_TTABLE)
1638 luaL_error( L, "expected required module list as a table, got %s", luaL_typename( L, required));
1639 lua_pushnil( L);
1640 while( lua_next( L, required) != 0)
1641 {
1642 if (lua_type(L,-1) != LUA_TSTRING || lua_type(L,-2) != LUA_TNUMBER || lua_tonumber( L, -2) != nbRequired)
1643 {
1644 luaL_error( L, "required module list should be a list of strings.");
1645 }
1646 else
1647 {
1648 require_one_module( L, L2, TRUE);
1649 }
1650 lua_pop( L, 1);
1651 ++ nbRequired;
1652 }
1415 } 1653 }
1654 STACK_END(L2,0)
1655 STACK_END(L,0)
1416 1656
1417 // Selected libraries 1657 // Appending the specified globals to the global environment
1658 // *after* stdlibs have been loaded and modules required, in case we transfer references to native functions they exposed...
1418 // 1659 //
1419 if (libs) 1660 if (glob!=0)
1420 { 1661 {
1421 const char *err= luaG_openlibs( L2, libs ); 1662 STACK_CHECK(L)
1422 ASSERT_L( !err ); // bad libs should have been noticed by 'lanes.lua' 1663 STACK_CHECK(L2)
1664 if (!lua_istable(L,glob))
1665 luaL_error( L, "Expected table, got %s", luaL_typename(L,glob));
1423 1666
1424 serialize_require( L2 ); 1667 lua_pushnil( L);
1668 while( lua_next( L, glob))
1669 {
1670 luaG_inter_copy( L, L2, 2); // moves the key/value pair to the L2 stack
1671 // assign it in the globals table
1672 lua_rawset( L2, LUA_GLOBALSINDEX);
1673 lua_pop( L, 1);
1674 }
1675
1676 STACK_END(L2, 0)
1677 STACK_END(L, 0)
1425 } 1678 }
1426 1679
1680 ASSERT_L( lua_gettop(L2) == 0);
1681
1427 // Lane main function 1682 // Lane main function
1428 // 1683 //
1429 STACK_CHECK(L) 1684 STACK_CHECK(L)
@@ -1470,10 +1725,10 @@ LUAG_FUNC( thread_new )
1470 s->waiting_on = NULL; 1725 s->waiting_on = NULL;
1471 s->cancel_request= FALSE; 1726 s->cancel_request= FALSE;
1472 1727
1473#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) ) 1728#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1474 MUTEX_INIT( &s->done_lock_ ); 1729 MUTEX_INIT( &s->done_lock_);
1475 SIGNAL_INIT( &s->done_signal_ ); 1730 SIGNAL_INIT( &s->done_signal_);
1476#endif 1731#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1477 s->mstatus= NORMAL; 1732 s->mstatus= NORMAL;
1478 s->selfdestruct_next= NULL; 1733 s->selfdestruct_next= NULL;
1479 1734
@@ -1488,7 +1743,7 @@ LUAG_FUNC( thread_new )
1488 lua_newtable( L); 1743 lua_newtable( L);
1489 lua_setfenv( L, -2); 1744 lua_setfenv( L, -2);
1490 1745
1491 // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still 1746 // Place 's' in registry, for 'cancel_test()' (even if 'cs'==0 we still
1492 // do cancel tests at pending send/receive). 1747 // do cancel tests at pending send/receive).
1493 // 1748 //
1494 lua_pushlightuserdata( L2, CANCEL_TEST_KEY ); 1749 lua_pushlightuserdata( L2, CANCEL_TEST_KEY );
@@ -1545,6 +1800,7 @@ LUAG_FUNC( thread_gc )
1545 { 1800 {
1546 // Make sure a kill has proceeded, before cleaning up the data structure. 1801 // Make sure a kill has proceeded, before cleaning up the data structure.
1547 // 1802 //
1803 // NO lua_close() in this case because we don't know where execution of the state was interrupted
1548 // If not doing 'THREAD_WAIT()' we should close the Lua state here 1804 // If not doing 'THREAD_WAIT()' we should close the Lua state here
1549 // (can it be out of order, since we killed the lane abruptly?) 1805 // (can it be out of order, since we killed the lane abruptly?)
1550 // 1806 //
@@ -1553,11 +1809,7 @@ LUAG_FUNC( thread_gc )
1553 s->L = 0; 1809 s->L = 0;
1554#else // 0 1810#else // 0
1555 DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" )); 1811 DEBUGEXEC(fprintf( stderr, "** Joining with a killed thread (needs testing) **" ));
1556#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1812 THREAD_WAIT( &s->thread, -1, &s->done_signal_, &s->done_lock_, &s->status);
1557 THREAD_WAIT( &s->thread, -1 );
1558#else
1559 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 );
1560#endif
1561 DEBUGEXEC(fprintf( stderr, "** Joined ok **" )); 1813 DEBUGEXEC(fprintf( stderr, "** Joined ok **" ));
1562#endif // 0 1814#endif // 0
1563 } 1815 }
@@ -1569,12 +1821,12 @@ LUAG_FUNC( thread_gc )
1569 1821
1570 // Clean up after a (finished) thread 1822 // Clean up after a (finished) thread
1571 // 1823 //
1572#if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN))) 1824#if THREADWAIT_METHOD == THREADWAIT_CONDVAR
1573 SIGNAL_FREE( &s->done_signal_ ); 1825 SIGNAL_FREE( &s->done_signal_);
1574 MUTEX_FREE( &s->done_lock_ ); 1826 MUTEX_FREE( &s->done_lock_);
1575#endif 1827#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
1576 1828
1577 free(s); 1829 free( s);
1578 1830
1579 return 0; 1831 return 0;
1580} 1832}
@@ -1603,13 +1855,19 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1603 // 1855 //
1604 if( s->status < DONE) 1856 if( s->status < DONE)
1605 { 1857 {
1606 s->cancel_request = TRUE; // it's now signalled to stop 1858 s->cancel_request = TRUE; // it's now signaled to stop
1607 done= 1859 // signal the linda the wake up the thread so that it can react to the cancel query
1608#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 1860 // let us hope we never land here with a pointer on a linda that has been destroyed...
1609 THREAD_WAIT( &s->thread, secs); 1861 //MUTEX_LOCK( &selfdestruct_cs );
1610#else 1862 {
1611 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs); 1863 SIGNAL_T *waiting_on = s->waiting_on;
1612#endif 1864 if( s->status == WAITING && waiting_on != NULL)
1865 {
1866 SIGNAL_ALL( waiting_on);
1867 }
1868 }
1869 //MUTEX_UNLOCK( &selfdestruct_cs );
1870 done = THREAD_WAIT( &s->thread, secs, &s->done_signal_, &s->done_lock_, &s->status);
1613 1871
1614 if ((!done) && force) 1872 if ((!done) && force)
1615 { 1873 {
@@ -1627,23 +1885,32 @@ static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force)
1627 1885
1628LUAG_FUNC( thread_cancel) 1886LUAG_FUNC( thread_cancel)
1629{ 1887{
1630 struct s_lane *s= lua_toLane(L,1); 1888 if( lua_gettop( L) != 1 || lua_type( L, 1) != LUA_TUSERDATA)
1631 double secs= 0.0; 1889 {
1632 uint_t force_i=2; 1890 return luaL_error( L, "invalid argument #1, did you use ':' as you should?");
1633 bool_t force, done= TRUE; 1891 }
1892 else
1893 {
1894 struct s_lane *s = lua_toLane( L, 1);
1895 double secs = 0.0;
1896 uint_t force_i = 2;
1897 bool_t force, done= TRUE;
1634 1898
1635 if (lua_isnumber(L,2)) { 1899 if( lua_isnumber( L, 2))
1636 secs= lua_tonumber(L,2); 1900 {
1637 force_i++; 1901 secs = lua_tonumber( L, 2);
1638 } else if (lua_isnil(L,2)) 1902 ++ force_i;
1639 force_i++; 1903 }
1904 else if( lua_isnil( L, 2))
1905 ++ force_i;
1640 1906
1641 force= lua_toboolean(L,force_i); // FALSE if nothing there 1907 force = lua_toboolean( L, force_i); // FALSE if nothing there
1642 1908
1643 done = thread_cancel( s, secs, force); 1909 done = thread_cancel( s, secs, force);
1644 1910
1645 lua_pushboolean( L, done); 1911 lua_pushboolean( L, done);
1646 return 1; 1912 return 1;
1913 }
1647} 1914}
1648 1915
1649//--- 1916//---
@@ -1698,12 +1965,7 @@ LUAG_FUNC( thread_join )
1698 int ret; 1965 int ret;
1699 bool_t done; 1966 bool_t done;
1700 1967
1701 done = (s->thread == 0) || 1968 done = THREAD_ISNULL( s->thread) || THREAD_WAIT( &s->thread, wait_secs, &s->done_signal_, &s->done_lock_, &s->status);
1702#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
1703 THREAD_WAIT( &s->thread, wait_secs );
1704#else
1705 THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, wait_secs);
1706#endif
1707 if (!done || !L2) 1969 if (!done || !L2)
1708 return 0; // timeout: pushes none, leaves 'L2' alive 1970 return 0; // timeout: pushes none, leaves 'L2' alive
1709 1971
@@ -1915,7 +2177,8 @@ LUAG_FUNC( now_secs )
1915LUAG_FUNC( wakeup_conv ) 2177LUAG_FUNC( wakeup_conv )
1916{ 2178{
1917 int year, month, day, hour, min, sec, isdst; 2179 int year, month, day, hour, min, sec, isdst;
1918 struct tm tm= {0}; 2180 struct tm t;
2181 memset( &t, 0, sizeof( t));
1919 // 2182 //
1920 // .year (four digits) 2183 // .year (four digits)
1921 // .month (1..12) 2184 // .month (1..12)
@@ -1942,15 +2205,15 @@ LUAG_FUNC( wakeup_conv )
1942 lua_pop(L,1); 2205 lua_pop(L,1);
1943 STACK_END(L,0) 2206 STACK_END(L,0)
1944 2207
1945 tm.tm_year= year-1900; 2208 t.tm_year= year-1900;
1946 tm.tm_mon= month-1; // 0..11 2209 t.tm_mon= month-1; // 0..11
1947 tm.tm_mday= day; // 1..31 2210 t.tm_mday= day; // 1..31
1948 tm.tm_hour= hour; // 0..23 2211 t.tm_hour= hour; // 0..23
1949 tm.tm_min= min; // 0..59 2212 t.tm_min= min; // 0..59
1950 tm.tm_sec= sec; // 0..60 2213 t.tm_sec= sec; // 0..60
1951 tm.tm_isdst= isdst; // 0/1/negative 2214 t.tm_isdst= isdst; // 0/1/negative
1952 2215
1953 lua_pushnumber( L, (double) mktime( &tm ) ); // ms=0 2216 lua_pushnumber( L, (double) mktime( &t)); // ms=0
1954 return 1; 2217 return 1;
1955} 2218}
1956 2219
@@ -1968,7 +2231,7 @@ static const struct luaL_reg lanes_functions [] = {
1968/* 2231/*
1969* One-time initializations 2232* One-time initializations
1970*/ 2233*/
1971static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_ref, int const nbKeepers) 2234static void init_once_LOCKED( lua_State* L, volatile DEEP_PRELUDE** timer_deep_ref, int const nbKeepers, lua_CFunction _on_state_create)
1972{ 2235{
1973 const char *err; 2236 const char *err;
1974 2237
@@ -1994,7 +2257,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
1994 // Selfdestruct chain handling 2257 // Selfdestruct chain handling
1995 // 2258 //
1996 MUTEX_INIT( &selfdestruct_cs ); 2259 MUTEX_INIT( &selfdestruct_cs );
1997 atexit( selfdestruct_atexit ); 2260 //atexit( selfdestruct_atexit );
1998 2261
1999 //--- 2262 //---
2000 // Linux needs SCHED_RR to change thread priorities, and that is only 2263 // Linux needs SCHED_RR to change thread priorities, and that is only
@@ -2019,7 +2282,7 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2019 } 2282 }
2020 #endif 2283 #endif
2021#endif 2284#endif
2022 err= init_keepers( nbKeepers); 2285 err = init_keepers( nbKeepers, _on_state_create);
2023 if (err) 2286 if (err)
2024 { 2287 {
2025 luaL_error( L, "Unable to initialize: %s", err ); 2288 luaL_error( L, "Unable to initialize: %s", err );
@@ -2044,7 +2307,17 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2044 2307
2045 // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid. 2308 // The host Lua state must always have a reference to this Linda object in order for our 'timer_deep_ref' to be valid.
2046 // So store a reference that we will never actually use. 2309 // So store a reference that we will never actually use.
2047 lua_pushlightuserdata(L, (void *)init_once_LOCKED); 2310 // at the same time, use this object as a 'desinit' marker:
2311 // when the main lua State is closed, this object will be GC'ed
2312 {
2313 lua_newuserdata( L, 1);
2314 lua_newtable( L);
2315 lua_pushcfunction( L, selfdestruct_atexit);
2316 lua_setfield( L, -2, "__gc");
2317 lua_pushliteral( L, "AtExit");
2318 lua_setfield( L, -2, "__metatable");
2319 lua_setmetatable( L, -2);
2320 }
2048 lua_insert(L, -2); // Swap key with the Linda object 2321 lua_insert(L, -2); // Swap key with the Linda object
2049 lua_rawset(L, LUA_REGISTRYINDEX); 2322 lua_rawset(L, LUA_REGISTRYINDEX);
2050 2323
@@ -2052,15 +2325,15 @@ static void init_once_LOCKED( lua_State *L, volatile DEEP_PRELUDE ** timer_deep_
2052 STACK_END(L,0) 2325 STACK_END(L,0)
2053} 2326}
2054 2327
2055int 2328static volatile long s_initCount = 0;
2056#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 2329
2057__declspec(dllexport) 2330LUAG_FUNC( configure )
2058#endif
2059luaopen_lanes( lua_State *L )
2060{ 2331{
2061 static volatile int /*bool*/ go_ahead; // = 0 2332 char const* name = luaL_checkstring( L, lua_upvalueindex( 1));
2062 int const nbKeepers = luaL_optint( L, 2, 1); 2333 int const nbKeepers = luaL_optint( L, 1, 1);
2063 luaL_argcheck( L, nbKeepers > 0, 2, "Number of keeper states must be > 0"); 2334 lua_CFunction on_state_create = lua_iscfunction( L, 2) ? lua_tocfunction( L, 2) : NULL;
2335 luaL_argcheck( L, nbKeepers > 0, 1, "Number of keeper states must be > 0");
2336 luaL_argcheck( L, lua_iscfunction( L, 2) || lua_isnil( L, 2), 2, "on_state_create should be a C function");
2064 /* 2337 /*
2065 * Making one-time initializations. 2338 * Making one-time initializations.
2066 * 2339 *
@@ -2068,42 +2341,43 @@ luaopen_lanes( lua_State *L )
2068 * there is no problem. But if the host is multithreaded, we need to lock around the 2341 * there is no problem. But if the host is multithreaded, we need to lock around the
2069 * initializations. 2342 * initializations.
2070 */ 2343 */
2071#ifdef PLATFORM_WIN32 2344#if THREADAPI == THREADAPI_WINDOWS
2072 { 2345 {
2073 // TBD: Someone please replace this with reliable Win32 API code. Problem is, 2346 static volatile int /*bool*/ go_ahead; // = 0
2074 // there's no autoinitializing locks (s.a. PTHREAD_MUTEX_INITIALIZER) in 2347 if( InterlockedCompareExchange( &s_initCount, 1, 0) == 0)
2075 // Windows so 'InterlockedIncrement' or something needs to be used. 2348 {
2076 // This is 99.9999% safe, though (and always safe if host is single-threaded) 2349 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create);
2077 // -- AKa 24-Jun-2009
2078 //
2079 static volatile unsigned my_number; // = 0
2080
2081 if (my_number++ == 0) { // almost atomic
2082 init_once_LOCKED(L, &timer_deep, nbKeepers);
2083 go_ahead= 1; // let others pass 2350 go_ahead= 1; // let others pass
2084 } else { 2351 }
2352 else
2353 {
2085 while( !go_ahead ) { Sleep(1); } // changes threads 2354 while( !go_ahead ) { Sleep(1); } // changes threads
2086 } 2355 }
2087 } 2356 }
2088#else 2357#else // THREADAPI == THREADAPI_PTHREAD
2089 if (!go_ahead) { 2358 if( s_initCount == 0)
2359 {
2090 static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER; 2360 static pthread_mutex_t my_lock= PTHREAD_MUTEX_INITIALIZER;
2091 pthread_mutex_lock(&my_lock); 2361 pthread_mutex_lock( &my_lock);
2092 { 2362 {
2093 // Recheck now that we're within the lock 2363 // Recheck now that we're within the lock
2094 // 2364 //
2095 if (!go_ahead) { 2365 if( s_initCount == 0)
2096 init_once_LOCKED(L, &timer_deep, nbKeepers); 2366 {
2097 go_ahead= 1; 2367 init_once_LOCKED( L, &timer_deep, nbKeepers, on_state_create);
2368 s_initCount = 1;
2098 } 2369 }
2099 } 2370 }
2100 pthread_mutex_unlock(&my_lock); 2371 pthread_mutex_unlock(&my_lock);
2101 } 2372 }
2102#endif 2373#endif // THREADAPI == THREADAPI_PTHREAD
2103 assert( timer_deep != 0 ); 2374 assert( timer_deep != 0 );
2104 2375
2105 // Create main module interface table 2376 // Create main module interface table
2106 lua_newtable(L); 2377 lua_pushvalue( L, lua_upvalueindex( 2));
2378 // remove configure() (this function) from the module interface
2379 lua_pushnil( L);
2380 lua_setfield( L, -2, "configure");
2107 luaL_register(L, NULL, lanes_functions); 2381 luaL_register(L, NULL, lanes_functions);
2108 2382
2109 // metatable for threads 2383 // metatable for threads
@@ -2124,7 +2398,7 @@ luaopen_lanes( lua_State *L )
2124 lua_setfield( L, -2, "join"); 2398 lua_setfield( L, -2, "join");
2125 lua_pushcfunction( L, LG_thread_cancel); 2399 lua_pushcfunction( L, LG_thread_cancel);
2126 lua_setfield( L, -2, "cancel"); 2400 lua_setfield( L, -2, "cancel");
2127 lua_pushboolean( L, 0); 2401 lua_pushliteral( L, "Lane");
2128 lua_setfield( L, -2, "__metatable"); 2402 lua_setfield( L, -2, "__metatable");
2129 2403
2130 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param 2404 lua_pushcclosure( L, LG_thread_new, 1 ); // metatable as closure param
@@ -2142,8 +2416,42 @@ luaopen_lanes( lua_State *L )
2142 lua_pushlightuserdata( L, CANCEL_ERROR ); 2416 lua_pushlightuserdata( L, CANCEL_ERROR );
2143 lua_setfield(L, -2, "cancel_error"); 2417 lua_setfield(L, -2, "cancel_error");
2144 2418
2145 // Return the local module table 2419 // register all native functions found in that module in the transferable functions database
2146 return 1; 2420 // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names)
2421 populate_func_lookup_table( L, -1, name);
2422 // record all existing C/JIT-fast functions
2423 populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL);
2424 // Return nothing
2425 lua_pop( L, 1);
2426 return 0;
2427}
2428
2429int
2430#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
2431__declspec(dllexport)
2432#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
2433luaopen_lanes( lua_State *L )
2434{
2435 // Create main module interface table
2436 // we only have 1 closure, which must be called to configure Lanes
2437 STACK_GROW( L, 3);
2438 STACK_CHECK( L)
2439 lua_newtable(L);
2440 lua_pushvalue(L, 1); // module name
2441 lua_pushvalue(L, -2); // module table
2442 lua_pushcclosure( L, LG_configure, 2);
2443 if( s_initCount == 0)
2444 {
2445 lua_setfield( L, -2, "configure");
2446 }
2447 else // already initialized: call it immediately and be done
2448 {
2449 lua_pushinteger( L, 666); // any value will do, it will be ignored
2450 lua_pushnil( L); // almost idem
2451 lua_call( L, 2, 0);
2452 }
2453 STACK_END( L, 1)
2454 return 1;
2147} 2455}
2148 2456
2149 2457
diff --git a/src/lanes.lua b/src/lanes.lua
index 05c2ff1..ec5a4e4 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -39,11 +39,27 @@ THE SOFTWARE.
39=============================================================================== 39===============================================================================
40]]-- 40]]--
41 41
42module( "lanes", package.seeall ) 42-- Lua 5.1: module() creates a global variable
43-- Lua 5.2: module() might go away
44-- almost everything module() does is done by require()
45-- -> simply create a table, populate it, return it, and be done
46local lanes = {}
47
48lanes.configure = function( _params)
49_params = _params or { nb_keepers = 1, with_timers = true, on_state_create = nil}
50if type( _params) ~= "table" then
51 error( "Bad parameter #1 to lanes.configure(), should be a table")
52end
53-- on_state_create may be nil or a function
54if _params.on_state_create and (type( _params.on_state_create) ~= "function") then
55 error( "Bad on_state_create: " .. tostring( _params.on_state_create), 2)
56end
43 57
44local mm = require "lua51-lanes" 58local mm = require "lua51-lanes"
45assert( type(mm)=="table" ) 59assert( type(mm)=="table" )
46 60
61-- configure() is available only the first time lua51-lanes is required process-wide, and we *must* call it to have the other functions in the interface
62if mm.configure then mm.configure( _params.nb_keepers, _params.on_state_create) end
47 63
48local thread_new = assert(mm.thread_new) 64local thread_new = assert(mm.thread_new)
49 65
@@ -74,7 +90,7 @@ local pairs= assert( pairs )
74local tostring= assert( tostring ) 90local tostring= assert( tostring )
75local error= assert( error ) 91local error= assert( error )
76 92
77ABOUT= 93lanes.ABOUT=
78{ 94{
79 author= "Asko Kauppi <akauppi@gmail.com>", 95 author= "Asko Kauppi <akauppi@gmail.com>",
80 description= "Running multiple Lua states in parallel", 96 description= "Running multiple Lua states in parallel",
@@ -140,6 +156,7 @@ end
140-- 156--
141-- .globals: table of globals to set for a new thread (passed by value) 157-- .globals: table of globals to set for a new thread (passed by value)
142-- 158--
159-- .required: table of packages to require
143-- ... (more options may be introduced later) ... 160-- ... (more options may be introduced later) ...
144-- 161--
145-- Calling with a function parameter ('lane_func') ends the string/table 162-- Calling with a function parameter ('lane_func') ends the string/table
@@ -161,7 +178,8 @@ local valid_libs= {
161 ["*"]= true 178 ["*"]= true
162} 179}
163 180
164function gen( ... ) 181-- PUBLIC LANES API
182local function gen( ... )
165 local opt= {} 183 local opt= {}
166 local libs= nil 184 local libs= nil
167 local lev= 2 -- level for errors 185 local lev= 2 -- level for errors
@@ -204,25 +222,31 @@ function gen( ... )
204 end 222 end
205 end 223 end
206 224
207 local prio, cs, g_tbl 225 local prio, cs, g_tbl, package_tbl, required
208 226
209 for k,v in pairs(opt) do 227 for k,v in pairs(opt) do
210 if k=="priority" then prio= v 228 if k=="priority" then prio= v
211 elseif k=="cancelstep" then cs= (v==true) and 100 or 229 elseif k=="cancelstep" then
212 (v==false) and 0 or 230 cs = (v==true) and 100 or
213 type(v)=="number" and v or 231 (v==false) and 0 or
214 error( "Bad cancelstep: "..tostring(v), lev ) 232 type(v)=="number" and v or
233 error( "Bad cancelstep: "..tostring(v), lev )
215 elseif k=="globals" then g_tbl= v 234 elseif k=="globals" then g_tbl= v
235 elseif k=="package" then
236 package_tbl = (type( v) == "table") and v or error( "Bad package: " .. tostring( v), lev)
237 elseif k=="required" then
238 required= (type( v) == "table") and v or error( "Bad required: " .. tostring( v), lev)
216 --.. 239 --..
217 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev ) 240 elseif k==1 then error( "unkeyed option: ".. tostring(v), lev )
218 else error( "Bad option: ".. tostring(k), lev ) 241 else error( "Bad option: ".. tostring(k), lev )
219 end 242 end
220 end 243 end
221 244
245 if not package_tbl then package_tbl = package end
222 -- Lane generator 246 -- Lane generator
223 -- 247 --
224 return function(...) 248 return function(...)
225 return thread_new( func, libs, cs, prio, g_tbl, ...) -- args 249 return thread_new( func, libs, _params.on_state_create, cs, prio, g_tbl, package_tbl, required, ...) -- args
226 end 250 end
227end 251end
228 252
@@ -233,11 +257,17 @@ end
233----- 257-----
234-- lanes.linda() -> linda_ud 258-- lanes.linda() -> linda_ud
235-- 259--
236linda = mm.linda 260-- PUBLIC LANES API
261local linda = mm.linda
237 262
238 263
239---=== Timers ===--- 264---=== Timers ===---
240 265
266-- PUBLIC LANES API
267local timer = function() error "timers are not active" end
268
269if _params.with_timers ~= false then
270
241local timer_gateway= assert( mm.timer_gateway ) 271local timer_gateway= assert( mm.timer_gateway )
242-- 272--
243-- On first 'require "lanes"', a timer lane is spawned that will maintain 273-- On first 'require "lanes"', a timer lane is spawned that will maintain
@@ -420,6 +450,8 @@ if first_time then
420 assert( key and wakeup_at and period ) 450 assert( key and wakeup_at and period )
421 451
422 set_timer( linda, key, wakeup_at, period>0 and period or nil ) 452 set_timer( linda, key, wakeup_at, period>0 and period or nil )
453 elseif secs == 0 then -- got no value while block-waiting?
454 WR( "timer lane: no linda, aborted?")
423 end 455 end
424 end 456 end
425 end )() 457 end )()
@@ -428,7 +460,8 @@ end
428----- 460-----
429-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] ) 461-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
430-- 462--
431function timer( linda, key, a, period ) 463-- PUBLIC LANES API
464timer = function( linda, key, a, period )
432 465
433 if a==0.0 then 466 if a==0.0 then
434 -- Caller expects to get current time stamp in Linda, on return 467 -- Caller expects to get current time stamp in Linda, on return
@@ -452,6 +485,7 @@ function timer( linda, key, a, period )
452 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period ) 485 timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
453end 486end
454 487
488end -- _params.with_timers
455 489
456---=== Lock & atomic generators ===--- 490---=== Lock & atomic generators ===---
457 491
@@ -468,7 +502,8 @@ end
468-- Returns an access function that allows 'N' simultaneous entries between 502-- Returns an access function that allows 'N' simultaneous entries between
469-- acquire (+M) and release (-M). For binary locks, use M==1. 503-- acquire (+M) and release (-M). For binary locks, use M==1.
470-- 504--
471function genlock( linda, key, N ) 505-- PUBLIC LANES API
506local function genlock( linda, key, N )
472 linda:limit(key,N) 507 linda:limit(key,N)
473 linda:set(key,nil) -- clears existing data 508 linda:set(key,nil) -- clears existing data
474 509
@@ -501,7 +536,8 @@ end
501-- Returns an access function that allows atomic increment/decrement of the 536-- Returns an access function that allows atomic increment/decrement of the
502-- number in 'key'. 537-- number in 'key'.
503-- 538--
504function genatomic( linda, key, initial_val ) 539-- PUBLIC LANES API
540local function genatomic( linda, key, initial_val )
505 linda:limit(key,2) -- value [,true] 541 linda:limit(key,2) -- value [,true]
506 linda:set(key,initial_val or 0.0) -- clears existing data (also queue) 542 linda:set(key,initial_val or 0.0) -- clears existing data (also queue)
507 543
@@ -517,4 +553,29 @@ end
517 553
518-- newuserdata = mm.newuserdata 554-- newuserdata = mm.newuserdata
519 555
556 -- activate full interface
557 lanes.gen = gen
558 lanes.linda = mm.linda
559 lanes.timer = timer
560 lanes.genlock = genlock
561 lanes.genatomic = genatomic
562 -- from now on, calling configure does nothing but checking that we don't call it with parameters that changed compared to the first invocation
563 lanes.configure = function( _params2)
564 _params2 = _params2 or _params
565 if _params2.nb_keepers ~= _params.nb_keepers then
566 error( "mismatched configuration: " .. tostring( _params2.nb_keepers) .. " keepers instead of " .. tostring( _params.nb_keepers))
567 end
568 if _params2.with_timers ~= _params.with_timers then
569 error( "mismatched configuration: " .. tostring( _params2.with_timers) .. " timer activity instead of " .. tostring( _params.with_timers))
570 end
571 if _params2.on_create_state and _params2.on_create_state ~= _params.on_create_state then
572 error( "mismatched configuration: " .. tostring( _params2.on_create_state) .. " timer activity instead of " .. tostring( _params.on_create_state))
573 end
574 return lanes
575 end
576 return lanes
577end -- lanes.configure
578
520--the end 579--the end
580return lanes
581
diff --git a/src/threading.c b/src/threading.c
index 00be243..8966dc6 100644
--- a/src/threading.c
+++ b/src/threading.c
@@ -41,9 +41,9 @@ THE SOFTWARE.
41#include "threading.h" 41#include "threading.h"
42#include "lua.h" 42#include "lua.h"
43 43
44#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) 44#if THREADAPI == THREADAPI_PTHREAD
45# include <sys/time.h> 45# include <sys/time.h>
46#endif 46#endif // THREADAPI == THREADAPI_PTHREAD
47 47
48 48
49#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN) 49#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN)
@@ -71,12 +71,15 @@ THE SOFTWARE.
71* FAIL is for unexpected API return values - essentially programming 71* FAIL is for unexpected API return values - essentially programming
72* error in _this_ code. 72* error in _this_ code.
73*/ 73*/
74#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 74#if THREADAPI == THREADAPI_WINDOWS
75static void FAIL( const char *funcname, int rc ) { 75static void FAIL( const char *funcname, int rc ) {
76 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc ); 76 fprintf( stderr, "%s() failed! (%d)\n", funcname, rc );
77#ifdef _MSC_VER
78 __debugbreak(); // give a chance to the debugger!
79#endif // _MSC_VER
77 abort(); 80 abort();
78} 81}
79#endif 82#endif // THREADAPI == THREADAPI_WINDOWS
80 83
81 84
82/* 85/*
@@ -87,7 +90,7 @@ static void FAIL( const char *funcname, int rc ) {
87*/ 90*/
88time_d now_secs(void) { 91time_d now_secs(void) {
89 92
90#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 93#if THREADAPI == THREADAPI_WINDOWS
91 /* 94 /*
92 * Windows FILETIME values are "100-nanosecond intervals since 95 * Windows FILETIME values are "100-nanosecond intervals since
93 * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as 96 * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as
@@ -142,7 +145,7 @@ time_d now_secs(void) {
142 // <= 2.0.2 code 145 // <= 2.0.2 code
143 return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0; 146 return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0;
144# endif 147# endif
145#else 148#else // THREADAPI == THREADAPI_PTHREAD
146 struct timeval tv; 149 struct timeval tv;
147 // { 150 // {
148 // time_t tv_sec; /* seconds since Jan. 1, 1970 */ 151 // time_t tv_sec; /* seconds since Jan. 1, 1970 */
@@ -153,7 +156,7 @@ time_d now_secs(void) {
153 assert( rc==0 ); 156 assert( rc==0 );
154 157
155 return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0; 158 return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0;
156#endif 159#endif // THREADAPI THREADAPI_PTHREAD
157} 160}
158 161
159 162
@@ -165,7 +168,7 @@ time_d SIGNAL_TIMEOUT_PREPARE( double secs ) {
165} 168}
166 169
167 170
168#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) 171#if THREADAPI == THREADAPI_PTHREAD
169/* 172/*
170* Prepare 'abs_secs' kind of timeout to 'timespec' format 173* Prepare 'abs_secs' kind of timeout to 'timespec' format
171*/ 174*/
@@ -184,7 +187,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
184 ts->tv_sec = ts->tv_sec + 1; 187 ts->tv_sec = ts->tv_sec + 1;
185 } 188 }
186} 189}
187#endif 190#endif // THREADAPI == THREADAPI_PTHREAD
188 191
189 192
190/*---=== Threading ===---*/ 193/*---=== Threading ===---*/
@@ -227,7 +230,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
227# endif 230# endif
228#endif 231#endif
229 232
230#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 233#if THREADAPI == THREADAPI_WINDOWS
231 // 234 //
232 void MUTEX_INIT( MUTEX_T *ref ) { 235 void MUTEX_INIT( MUTEX_T *ref ) {
233 *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL ); 236 *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL );
@@ -279,10 +282,11 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
279 *ref= h; 282 *ref= h;
280 } 283 }
281 // 284 //
282 bool_t THREAD_WAIT( THREAD_T *ref, double secs ) { 285bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs)
283 long ms= (long)((secs*1000.0)+0.5); 286{
287 DWORD ms = (secs<0.0) ? INFINITE : (DWORD)((secs*1000.0)+0.5);
284 288
285 DWORD rc= WaitForSingleObject( *ref, ms<0 ? INFINITE:ms /*timeout*/ ); 289 DWORD rc= WaitForSingleObject( *ref, ms /*timeout*/ );
286 // 290 //
287 // (WAIT_ABANDONED) 291 // (WAIT_ABANDONED)
288 // WAIT_OBJECT_0 success (0) 292 // WAIT_OBJECT_0 success (0)
@@ -370,7 +374,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
370 if (!PulseEvent( *ref )) 374 if (!PulseEvent( *ref ))
371 FAIL( "PulseEvent", GetLastError() ); 375 FAIL( "PulseEvent", GetLastError() );
372 } 376 }
373#else 377#else // THREADAPI == THREADAPI_PTHREAD
374 // PThread (Linux, OS X, ...) 378 // PThread (Linux, OS X, ...)
375 // 379 //
376 // On OS X, user processes seem to be able to change priorities. 380 // On OS X, user processes seem to be able to change priorities.
@@ -649,7 +653,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
649 } 653 }
650 } 654 }
651 // 655 //
652 /* 656 /*
653 * Wait for a thread to finish. 657 * Wait for a thread to finish.
654 * 658 *
655 * 'mu_ref' is a lock we should use for the waiting; initially unlocked. 659 * 'mu_ref' is a lock we should use for the waiting; initially unlocked.
@@ -657,11 +661,7 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
657 * 661 *
658 * Returns TRUE for succesful wait, FALSE for timed out 662 * Returns TRUE for succesful wait, FALSE for timed out
659 */ 663 */
660#ifdef PTHREAD_TIMEDJOIN 664bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref)
661 bool_t THREAD_WAIT( THREAD_T *ref, double secs )
662#else
663 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs )
664#endif
665{ 665{
666 struct timespec ts_store; 666 struct timespec ts_store;
667 const struct timespec *timeout= NULL; 667 const struct timespec *timeout= NULL;
@@ -669,16 +669,17 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
669 669
670 // Do timeout counting before the locks 670 // Do timeout counting before the locks
671 // 671 //
672#ifdef PTHREAD_TIMEDJOIN 672#if THREADWAIT_METHOD == THREADWAIT_TIMEOUT
673 if (secs>=0.0) { 673 if (secs>=0.0)
674#else 674#else // THREADWAIT_METHOD == THREADWAIT_CONDVAR
675 if (secs>0.0) { 675 if (secs>0.0)
676#endif 676#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
677 {
677 prepare_timeout( &ts_store, now_secs()+secs ); 678 prepare_timeout( &ts_store, now_secs()+secs );
678 timeout= &ts_store; 679 timeout= &ts_store;
679 } 680 }
680 681
681#ifdef PTHREAD_TIMEDJOIN 682#if THREADWAIT_METHOD == THREADWAIT_TIMEOUT
682 /* Thread is joinable 683 /* Thread is joinable
683 */ 684 */
684 if (!timeout) { 685 if (!timeout) {
@@ -691,10 +692,11 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
691 } 692 }
692 done= rc==0; 693 done= rc==0;
693 } 694 }
694#else 695#else // THREADWAIT_METHOD == THREADWAIT_CONDVAR
695 /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot 696 /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot
696 * join with it. Use the cond.var. 697 * join with it. Use the cond.var.
697 */ 698 */
699 (void) ref; // unused
698 MUTEX_LOCK( mu_ref ); 700 MUTEX_LOCK( mu_ref );
699 701
700 // 'secs'==0.0 does not need to wait, just take the current status 702 // 'secs'==0.0 does not need to wait, just take the current status
@@ -714,13 +716,13 @@ static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
714 done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED 716 done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED
715 717
716 MUTEX_UNLOCK( mu_ref ); 718 MUTEX_UNLOCK( mu_ref );
717#endif 719#endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR
718 return done; 720 return done;
719 } 721 }
720 // 722 //
721 void THREAD_KILL( THREAD_T *ref ) { 723 void THREAD_KILL( THREAD_T *ref ) {
722 pthread_cancel( *ref ); 724 pthread_cancel( *ref );
723 } 725 }
724#endif 726#endif // THREADAPI == THREADAPI_PTHREAD
725 727
726static const lua_Alloc alloc_f= 0; 728static const lua_Alloc alloc_f= 0;
diff --git a/src/threading.h b/src/threading.h
index 2bf48e5..b0a3db0 100644
--- a/src/threading.h
+++ b/src/threading.h
@@ -1,8 +1,8 @@
1/* 1/*
2* THREADING.H 2* THREADING.H
3*/ 3*/
4#ifndef THREADING_H 4#ifndef __threading_h__
5#define THREADING_H 5#define __threading_h__ 1
6 6
7/* Platform detection 7/* Platform detection
8*/ 8*/
@@ -38,11 +38,19 @@ typedef unsigned int uint_t;
38*/ 38*/
39enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; 39enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
40 40
41#define THREADAPI_WINDOWS 1
42#define THREADAPI_PTHREAD 2
43
44#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
45#define THREADAPI THREADAPI_WINDOWS
46#else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
47#define THREADAPI THREADAPI_PTHREAD
48#endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
41 49
42/*---=== Locks & Signals ===--- 50/*---=== Locks & Signals ===---
43*/ 51*/
44 52
45#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 53#if THREADAPI == THREADAPI_WINDOWS
46 #define WIN32_LEAN_AND_MEAN 54 #define WIN32_LEAN_AND_MEAN
47 // 'SignalObjectAndWait' needs this (targets Windows 2000 and above) 55 // 'SignalObjectAndWait' needs this (targets Windows 2000 and above)
48 #define _WIN32_WINNT 0x0400 56 #define _WIN32_WINNT 0x0400
@@ -61,12 +69,13 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
61 void MUTEX_LOCK( MUTEX_T *ref ); 69 void MUTEX_LOCK( MUTEX_T *ref );
62 void MUTEX_UNLOCK( MUTEX_T *ref ); 70 void MUTEX_UNLOCK( MUTEX_T *ref );
63 71
64 typedef unsigned THREAD_RETURN_T; 72 typedef unsigned int THREAD_RETURN_T;
65 73
66 #define SIGNAL_T HANDLE 74 #define SIGNAL_T HANDLE
67 75
68 #define YIELD() Sleep(0) 76 #define YIELD() Sleep(0)
69#else 77 #define THREAD_CALLCONV __stdcall
78#else // THREADAPI == THREADAPI_PTHREAD
70 // PThread (Linux, OS X, ...) 79 // PThread (Linux, OS X, ...)
71 // 80 //
72 #include <pthread.h> 81 #include <pthread.h>
@@ -107,7 +116,8 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
107 #else 116 #else
108 #define YIELD() pthread_yield() 117 #define YIELD() pthread_yield()
109 #endif 118 #endif
110#endif 119 #define THREAD_CALLCONV
120#endif //THREADAPI == THREADAPI_PTHREAD
111 121
112void SIGNAL_INIT( SIGNAL_T *ref ); 122void SIGNAL_INIT( SIGNAL_T *ref );
113void SIGNAL_FREE( SIGNAL_T *ref ); 123void SIGNAL_FREE( SIGNAL_T *ref );
@@ -129,9 +139,10 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
129/*---=== Threading ===--- 139/*---=== Threading ===---
130*/ 140*/
131 141
132#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 142#if THREADAPI == THREADAPI_WINDOWS
133 143
134 typedef HANDLE THREAD_T; 144 typedef HANDLE THREAD_T;
145# define THREAD_ISNULL( _h) (_h == 0)
135 // 146 //
136 void THREAD_CREATE( THREAD_T *ref, 147 void THREAD_CREATE( THREAD_T *ref,
137 THREAD_RETURN_T (__stdcall *func)( void * ), 148 THREAD_RETURN_T (__stdcall *func)( void * ),
@@ -140,7 +151,7 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
140# define THREAD_PRIO_MIN (-3) 151# define THREAD_PRIO_MIN (-3)
141# define THREAD_PRIO_MAX (+3) 152# define THREAD_PRIO_MAX (+3)
142 153
143#else 154#else // THREADAPI == THREADAPI_PTHREAD
144 /* Platforms that have a timed 'pthread_join()' can get away with a simpler 155 /* Platforms that have a timed 'pthread_join()' can get away with a simpler
145 * implementation. Others will use a condition variable. 156 * implementation. Others will use a condition variable.
146 */ 157 */
@@ -154,6 +165,7 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
154# endif 165# endif
155 166
156 typedef pthread_t THREAD_T; 167 typedef pthread_t THREAD_T;
168# define THREAD_ISNULL( _h) 0 // pthread_t may be a structure: never 'null' by itself
157 169
158 void THREAD_CREATE( THREAD_T *ref, 170 void THREAD_CREATE( THREAD_T *ref,
159 THREAD_RETURN_T (*func)( void * ), 171 THREAD_RETURN_T (*func)( void * ),
@@ -171,19 +183,30 @@ bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
171# define THREAD_PRIO_MIN (-2) 183# define THREAD_PRIO_MIN (-2)
172# define THREAD_PRIO_MAX (+2) 184# define THREAD_PRIO_MAX (+2)
173# endif 185# endif
174#endif 186#endif // THREADAPI == THREADAPI_WINDOWS
175 187
176/* 188/*
177* Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout. 189* Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout.
178* Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach. 190* Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach.
179*/ 191*/
180#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) 192#define THREADWAIT_TIMEOUT 1
181 bool_t THREAD_WAIT( THREAD_T *ref, double secs ); 193#define THREADWAIT_CONDVAR 2
182#else 194
183 bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs ); 195#if THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN)
184#endif 196#define THREADWAIT_METHOD THREADWAIT_TIMEOUT
197#else // THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN)
198#define THREADWAIT_METHOD THREADWAIT_CONDVAR
199#endif // THREADAPI == THREADAPI_WINDOWS || (defined PTHREAD_TIMEDJOIN)
200
201
202#if THREADWAIT_METHOD == THREADWAIT_TIMEOUT
203bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs);
204#define THREAD_WAIT( a, b, c, d, e) THREAD_WAIT_IMPL( a, b)
205#else // THREADWAIT_METHOD == THREADWAIT_CONDVAR
206bool_t THREAD_WAIT_IMPL( THREAD_T *ref, double secs, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref);
207#define THREAD_WAIT THREAD_WAIT_IMPL
208#endif // // THREADWAIT_METHOD == THREADWAIT_CONDVAR
185 209
186void THREAD_KILL( THREAD_T *ref ); 210void THREAD_KILL( THREAD_T *ref );
187 211
188#endif 212#endif // __threading_h__
189 // THREADING_H
diff --git a/src/tools.c b/src/tools.c
index d475fc0..212cf52 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -31,6 +31,7 @@ THE SOFTWARE.
31*/ 31*/
32 32
33#include "tools.h" 33#include "tools.h"
34#include "keeper.h"
34 35
35#include "lualib.h" 36#include "lualib.h"
36#include "lauxlib.h" 37#include "lauxlib.h"
@@ -66,7 +67,7 @@ void luaG_dump( lua_State* L ) {
66 // enable it for more debugging. 67 // enable it for more debugging.
67 // 68 //
68 STACK_CHECK(L) 69 STACK_CHECK(L)
69 STACK_GROW( L, 2 ) 70 STACK_GROW( L, 2);
70 71
71 lua_getglobal( L, "tostring" ); 72 lua_getglobal( L, "tostring" );
72 // 73 //
@@ -90,7 +91,7 @@ void luaG_dump( lua_State* L ) {
90} 91}
91 92
92 93
93/*---=== luaG_openlibs ===---*/ 94/*---=== luaG_newstate ===---*/
94 95
95static const luaL_Reg libs[] = { 96static const luaL_Reg libs[] = {
96 { LUA_LOADLIBNAME, luaopen_package }, 97 { LUA_LOADLIBNAME, luaopen_package },
@@ -108,21 +109,303 @@ static const luaL_Reg libs[] = {
108 109
109static bool_t openlib( lua_State *L, const char *name, size_t len ) { 110static bool_t openlib( lua_State *L, const char *name, size_t len ) {
110 111
111 unsigned i; 112 unsigned i;
112 bool_t all= strncmp( name, "*", len ) == 0; 113 bool_t all= strncmp( name, "*", len ) == 0;
113 114
114 for( i=0; libs[i].name; i++ ) { 115 for( i=0; libs[i].name; i++ )
115 if (all || (strncmp(name, libs[i].name, len) ==0)) { 116 {
116 if (libs[i].func) { 117 if (all || (strncmp(name, libs[i].name, len) ==0))
117 STACK_GROW(L,2); 118 {
118 lua_pushcfunction( L, libs[i].func ); 119 if (libs[i].func)
119 lua_pushstring( L, libs[i].name ); 120 {
120 lua_call( L, 1, 0 ); 121 STACK_GROW(L,1);
121 } 122 STACK_CHECK(L)
122 if (!all) return TRUE; 123 lua_pushcfunction( L, libs[i].func);
123 } 124 // pushes the module table on the stack
124 } 125 lua_call( L, 0, 1);
125 return all; 126 populate_func_lookup_table( L, -1, libs[i].name);
127 // remove the module when we are done
128 lua_pop( L, 1);
129 STACK_END(L, 0)
130 }
131 if (!all) return TRUE;
132 }
133 }
134 return all;
135}
136
137static int dummy_writer(lua_State *L, const void* p, size_t sz, void* ud)
138{
139 (void)L; (void)p; (void)sz; (void) ud; // unused
140 return 666;
141}
142
143
144/*
145 * differentiation between C, bytecode and JIT-fast functions
146 *
147 *
148 * +----------+------------+----------+
149 * | bytecode | C function | JIT-fast |
150 * +-----------------+----------+------------+----------+
151 * | lua_topointer | | | |
152 * +-----------------+----------+------------+----------+
153 * | lua_tocfunction | NULL | | NULL |
154 * +-----------------+----------+------------+----------+
155 * | lua_dump | 666 | 1 | 1 |
156 * +-----------------+----------+------------+----------+
157 */
158
159typedef enum
160{
161 FST_Bytecode,
162 FST_Native,
163 FST_FastJIT
164} FuncSubType;
165
166FuncSubType luaG_getfuncsubtype( lua_State *L, int _i)
167{
168 if( lua_tocfunction( L, _i))
169 {
170 return FST_Native;
171 }
172 {
173 int mustpush = 0, dumpres;
174 if( STACK_ABS( L, _i) != lua_gettop( L))
175 {
176 lua_pushvalue( L, _i);
177 mustpush = 1;
178 }
179 // the provided writer fails with code 666
180 // therefore, anytime we get 666, this means that lua_dump() attempted a dump
181 // all other cases mean this is either a C or LuaJIT-fast function
182 dumpres = lua_dump( L, dummy_writer, NULL);
183 lua_pop( L, mustpush);
184 if( dumpres == 666)
185 {
186 return FST_Bytecode;
187 }
188 }
189 return FST_FastJIT;
190}
191
192static lua_CFunction luaG_tocfunction( lua_State *L, int _i, FuncSubType *_out)
193{
194 lua_CFunction p = lua_tocfunction( L, _i);
195 *_out = luaG_getfuncsubtype( L, _i);
196 return p;
197}
198
199
200#define LOOKUP_KEY "ddea37aa-50c7-4d3f-8e0b-fb7a9d62bac5"
201#define LOOKUP_KEY_CACHE "d1059270-4976-4193-a55b-c952db5ab7cd"
202
203
204// inspired from tconcat() in ltablib.c
205static char const * luaG_pushFQN(lua_State *L, int t, int last)
206{
207 int i = 1;
208 luaL_Buffer b;
209 STACK_CHECK( L)
210 luaL_buffinit(L, &b);
211 for( ; i < last; i++)
212 {
213 lua_rawgeti( L, t, i);
214 luaL_addvalue( &b);
215 luaL_addlstring(&b, ".", 1);
216 }
217 if (i == last) /* add last value (if interval was not empty) */
218 {
219 lua_rawgeti( L, t, i);
220 luaL_addvalue( &b);
221 }
222 luaL_pushresult( &b);
223 STACK_END( L, 1)
224 return lua_tostring( L, -1);
225}
226
227
228static void populate_func_lookup_table_recur( lua_State *L, int _ctx_base, int _i, int _depth)
229{
230 lua_Integer visit_count;
231 // slot 1 in the stack contains the table that receives everything we found
232 int const dest = _ctx_base;
233 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
234 int const fqn = _ctx_base + 1;
235 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops
236 int const cache = _ctx_base + 2;
237 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search)
238 int const breadth_first_cache = lua_gettop( L) + 1;
239
240 STACK_GROW( L, 6);
241 // slot _i contains a table where we search for functions
242 STACK_CHECK( L) // ... {_i}
243
244 // if table is already visited, we are done
245 lua_pushvalue( L, _i); // ... {_i} {}
246 lua_rawget( L, cache); // ... {_i} nil|n
247 visit_count = lua_tointeger( L, -1); // 0 if nil, else n
248 lua_pop( L, 1); // ... {_i}
249 STACK_MID( L, 0)
250 if( visit_count > 0)
251 {
252 return;
253 }
254
255 // remember we visited this table (1-visit count)
256 lua_pushvalue( L, _i); // ... {_i} {}
257 lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1
258 lua_rawset( L, cache); // ... {_i}
259 STACK_MID( L, 0)
260
261 // this table is at breadth_first_cache index
262 lua_newtable( L); // ... {_i} {bfc}
263 ASSERT_L( lua_gettop( L) == breadth_first_cache);
264 // iterate over all entries in the processed table
265 lua_pushnil( L); // ... {_i} {bfc} nil
266 while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v
267 {
268 // just for debug, not actually needed
269 //char const * key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string";
270 // subtable: process it recursively
271 if( lua_istable( L, -1)) // ... {_i} {bfc} k {}
272 {
273 // increment visit count to make sure we will actually scan it at this recursive level
274 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
275 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {}
276 lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n?
277 visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1
278 lua_pop( L, 1); // ... {_i} {bfc} k {} {}
279 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
280 lua_rawset( L, cache); // ... {_i} {bfc} k {}
281 // store the table in the breadth-first cache
282 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
283 lua_insert( L, -2); // ... {_i} {bfc} k k {}
284 lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k
285 STACK_MID( L, 2)
286 }
287 else if( lua_isfunction( L, -1)) // ... {_i} {bfc} k func
288 {
289 if( luaG_getfuncsubtype( L, -1) != FST_Bytecode)
290 {
291 //char const *fqnString; for debugging
292 bool_t not_registered;
293 // first, skip everything if the function is already known
294 lua_pushvalue( L, -1); // ... {_i} {bfc} k func func
295 lua_rawget( L, dest); // ... {_i} {bfc} k func name?
296 not_registered = lua_isnil( L, -1);
297 lua_pop( L, 1); // ... {_i} {bfc} k func
298 if( not_registered)
299 {
300 ++ _depth;
301 // push function name in fqn stack (note that concatenation will crash if name is a not string!)
302 lua_pushvalue( L, -2); // ... {_i} {bfc} k func k
303 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k func
304 // generate name
305 /*fqnString =*/ (void) luaG_pushFQN( L, fqn, _depth); // ... {_i} {bfc} k func "f.q.n"
306 //puts( fqnString);
307 // prepare the stack for database feed
308 lua_pushvalue( L, -1); // ... {_i} {bfc} k func "f.q.n" "f.q.n"
309 lua_pushvalue( L, -3); // ... {_i} {bfc} k func "f.q.n" "f.q.n" func
310 // t["f.q.n"] = func
311 lua_rawset( L, dest); // ... {_i} {bfc} k func "f.q.n"
312 // t[func] = "f.q.n"
313 lua_rawset( L, dest); // ... {_i} {bfc} k
314 // remove table name from fqn stack
315 lua_pushnil( L); // ... {_i} {bfc} k nil
316 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k
317 -- _depth;
318 }
319 else
320 {
321 lua_pop( L, 1); // ... {_i} {bfc} k
322 }
323 }
324 else
325 {
326 lua_pop( L, 1); // ... {_i} {bfc} k
327 }
328 }
329 else
330 {
331 lua_pop( L, 1); // ... {_i} {bfc} k
332 }
333 STACK_MID( L, 2)
334 }
335 // now process the tables we encountered at that depth
336 ++ _depth;
337 lua_pushnil( L); // ... {_i} {bfc} nil
338 while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {}
339 {
340 // un-visit this table in case we do need to process it
341 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
342 lua_rawget( L, cache); // ... {_i} {bfc} k {} n
343 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER);
344 visit_count = lua_tointeger( L, -1) - 1;
345 lua_pop( L, 1); // ... {_i} {bfc} k {}
346 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {}
347 if( visit_count > 0)
348 {
349 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n
350 }
351 else
352 {
353 lua_pushnil( L); // ... {_i} {bfc} k {} {} nil
354 }
355 lua_rawset( L, cache); // ... {_i} {bfc} k {}
356 // push table name in fqn stack (note that concatenation will crash if name is a not string!)
357 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k
358 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {}
359 populate_func_lookup_table_recur( L, _ctx_base, lua_gettop( L), _depth); // ... {_i} {bfc} k {}
360 lua_pop( L, 1); // ... {_i} {bfc} k
361 STACK_MID( L, 2)
362 }
363 // remove table name from fqn stack
364 lua_pushnil( L); // ... {_i} {bfc} nil
365 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc}
366 -- _depth;
367 // we are done with our cache
368 lua_pop( L, 1); // ... {_i}
369 STACK_END( L, 0)
370 // we are done // ... {_i} {bfc}
371}
372
373/*
374 * create a "fully.qualified.name" <-> function equivalence database
375 */
376void populate_func_lookup_table( lua_State *L, int _i, char const *_name)
377{
378 int const ctx_base = lua_gettop( L) + 1;
379 int const in_base = STACK_ABS( L, _i);
380 int const start_depth = _name ? 1 : 0;
381 //printf( "%p: populate_func_lookup_table('%s')\n", L, _name ? _name : "NULL");
382 STACK_GROW( L, 3);
383 STACK_CHECK( L)
384 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}?
385 if( lua_isnil( L, -1)) // nil
386 {
387 lua_pop( L, 1); //
388 lua_newtable( L); // {}
389 lua_pushvalue( L, -1); // {} {}
390 lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
391 }
392 lua_newtable( L); // {} {fqn}
393 if( _name)
394 {
395 lua_pushstring( L, _name); // {} {fqn} "name"
396 lua_rawseti( L, -2, start_depth); // {} {fqn}
397 }
398 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}?
399 if( lua_isnil( L, -1))
400 {
401 lua_pop( L, 1); // {} {fqn}
402 lua_newtable( L); // {} {fqn} {cache}
403 lua_pushvalue( L, -1); // {} {fqn} {cache} {cache}
404 lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY_CACHE); // {} {fqn} {cache}
405 }
406 populate_func_lookup_table_recur( L, ctx_base, in_base, start_depth); // {...} {fqn} {cache}
407 lua_pop( L, 3);
408 STACK_END( L, 0)
126} 409}
127 410
128/* 411/*
@@ -139,33 +422,52 @@ static bool_t openlib( lua_State *L, const char *name, size_t len ) {
139*/ 422*/
140#define is_name_char(c) (isalpha(c) || (c)=='*') 423#define is_name_char(c) (isalpha(c) || (c)=='*')
141 424
142const char *luaG_openlibs( lua_State *L, const char *libs ) { 425lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create)
143 const char *p; 426{
144 unsigned len; 427 char const* p;
145 428 unsigned int len;
146 if (!libs) return NULL; // no libs, not even 'base' 429 lua_State* const L = luaL_newstate();
147
148 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
149 //
150 lua_gc(L, LUA_GCSTOP, 0);
151 430
152 // Anything causes 'base' to be taken in 431 // no libs, or special init func (not even 'base')
153 // 432 if (libs || _on_state_create)
154 STACK_GROW(L,2); 433 {
155 lua_pushcfunction( L, luaopen_base ); 434 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
156 lua_pushliteral( L, "" ); 435 //
157 lua_call( L, 1, 0 ); 436 lua_gc( L, LUA_GCSTOP, 0);
158
159 for( p= libs; *p; p+=len ) {
160 len=0;
161 while (*p && !is_name_char(*p)) p++; // bypass delimiters
162 while (is_name_char(p[len])) len++; // bypass name
163 if (len && (!openlib( L, p, len )))
164 break;
165 }
166 lua_gc(L, LUA_GCRESTART, 0);
167 437
168 return *p ? p : NULL; 438 // Anything causes 'base' to be taken in
439 //
440 STACK_GROW( L, 2);
441 STACK_CHECK( L)
442 if( _on_state_create)
443 {
444 lua_pushcfunction( L, _on_state_create);
445 lua_call( L, 0, 0);
446 }
447 if( libs)
448 {
449 lua_pushcfunction( L, luaopen_base);
450 lua_call( L, 0, 0);
451 }
452 // after opening base, register the functions it exported in our name<->function database
453 populate_func_lookup_table( L, LUA_GLOBALSINDEX, NULL);
454 STACK_MID( L, 0);
455 if( libs)
456 {
457 for( p = libs; *p; p += len)
458 {
459 len=0;
460 while (*p && !is_name_char(*p)) p++; // bypass delimiters
461 while (is_name_char(p[len])) len++; // bypass name
462 if (len && (!openlib( L, p, len )))
463 break;
464 }
465 serialize_require( L);
466 }
467 STACK_END(L,0)
468 lua_gc( L, LUA_GCRESTART, 0);
469 }
470 return L;
169} 471}
170 472
171 473
@@ -284,7 +586,7 @@ luaG_IdFunction get_idfunc( lua_State *L, int index )
284{ 586{
285 luaG_IdFunction ret; 587 luaG_IdFunction ret;
286 588
287 index= STACK_ABS(L,index); 589 index = STACK_ABS( L, index);
288 590
289 STACK_GROW(L,1); 591 STACK_GROW(L,1);
290 592
@@ -696,7 +998,7 @@ uint_t get_mt_id( lua_State *L, int i ) {
696 static uint_t last_id= 0; 998 static uint_t last_id= 0;
697 uint_t id; 999 uint_t id;
698 1000
699 i= STACK_ABS(L,i); 1001 i = STACK_ABS( L, i);
700 1002
701 STACK_GROW(L,3); 1003 STACK_GROW(L,3);
702 1004
@@ -819,8 +1121,8 @@ static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uin
819 1121
820static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) 1122static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
821{ 1123{
1124 void * const aspointer = (void*)lua_topointer( L, i );
822 // TBD: Merge this and same code for tables 1125 // TBD: Merge this and same code for tables
823
824 ASSERT_L( L2_cache_i != 0 ); 1126 ASSERT_L( L2_cache_i != 0 );
825 1127
826 STACK_GROW(L2,3); 1128 STACK_GROW(L2,3);
@@ -832,7 +1134,7 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui
832 // We don't need to use the from state ('L') in ID since the life span 1134 // We don't need to use the from state ('L') in ID since the life span
833 // is only for the duration of a copy (both states are locked). 1135 // is only for the duration of a copy (both states are locked).
834 // 1136 //
835 lua_pushlightuserdata( L2, (void*)lua_topointer( L, i )); // push a light userdata uniquely representing the function 1137 lua_pushlightuserdata( L2, aspointer); // push a light userdata uniquely representing the function
836 1138
837 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) ); 1139 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
838 1140
@@ -886,9 +1188,48 @@ static void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, ui
886 // 1188 //
887 // L2 [-1]: function 1189 // L2 [-1]: function
888 1190
889 ASSERT_L( lua_isfunction(L2,-1) ); 1191 ASSERT_L( lua_isfunction(L2,-1));
1192}
1193
1194/*
1195* Push a looked-up native/LuaJIT function.
1196*/
1197static void lookup_native_func( lua_State *L2, lua_State *L, uint_t i)
1198{
1199 char const *fqn;
1200 size_t len;
1201 _ASSERT_L( L, lua_isfunction( L, i));
1202 STACK_CHECK( L)
1203 STACK_CHECK( L2)
1204 // fetch the name from the source state's lookup table
1205 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
1206 _ASSERT_L( L, lua_istable( L, -1));
1207 lua_pushvalue( L, i); // {} f
1208 lua_rawget( L, -2); // {} "f.q.n"
1209 fqn = lua_tolstring( L, -1, &len);
1210 if( !fqn)
1211 {
1212 luaL_error( L, "function not found in origin transfer database.");
1213 }
1214 // push the equivalent function in the destination's stack, retrieved from the lookup table
1215 lua_getfield( L2, LUA_REGISTRYINDEX, LOOKUP_KEY); // {}
1216 _ASSERT_L( L2, lua_istable( L2, -1));
1217 lua_pushlstring( L2, fqn, len); // {} "f.q.n"
1218 lua_pop( L, 2); //
1219 lua_rawget( L2, -2); // {} f
1220 if( !lua_isfunction( L2, -1))
1221 {
1222 // yarglah: luaL_error formatting doesn't support string width modifier!
1223 char message[256];
1224 sprintf( message, "function %*s not found in destination transfer database.", len, fqn);
1225 luaL_error( L, message);
1226 }
1227 lua_remove( L2, -2); // f
1228 STACK_END( L2, 1)
1229 STACK_END( L, 0)
890} 1230}
891 1231
1232#define LOG_FUNC_INFO 0
892 1233
893/* 1234/*
894* Copy a function over, which has not been found in the cache. 1235* Copy a function over, which has not been found in the cache.
@@ -898,107 +1239,131 @@ enum e_vt {
898}; 1239};
899static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type ); 1240static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type );
900 1241
901static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) { 1242static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i )
902 1243{
903 lua_CFunction cfunc= lua_tocfunction( L,i ); 1244 FuncSubType funcSubType;
904 unsigned n; 1245 lua_CFunction cfunc = luaG_tocfunction( L, i, &funcSubType); // NULL for LuaJIT-fast && bytecode functions
905
906 ASSERT_L( L2_cache_i != 0 );
907
908 STACK_GROW(L,2);
909
910 STACK_CHECK(L)
911 if (!cfunc) { // Lua function
912 luaL_Buffer b;
913 const char *s;
914 size_t sz;
915 int tmp;
916 const char *name= NULL;
917
918#if 0
919 // "To get information about a function you push it onto the
920 // stack and start the what string with the character '>'."
921 //
922 { lua_Debug ar;
923 lua_pushvalue( L, i );
924 lua_getinfo(L, ">n", &ar); // fills 'name' and 'namewhat', pops function
925 name= ar.namewhat;
926
927 fprintf( stderr, "NAME: %s\n", name ); // just gives NULL
928 }
929#endif
930 // 'lua_dump()' needs the function at top of stack
931 //
932 if (i!=-1) lua_pushvalue( L, i );
933
934 luaL_buffinit(L,&b);
935 tmp= lua_dump(L, buf_writer, &b);
936 ASSERT_L(tmp==0);
937 //
938 // "value returned is the error code returned by the last call
939 // to the writer" (and we only return 0)
940
941 luaL_pushresult(&b); // pushes dumped string on 'L'
942 s= lua_tolstring(L,-1,&sz);
943 ASSERT_L( s && sz );
944
945 if (i!=-1) lua_remove( L, -2 );
946 1246
947 // Note: Line numbers seem to be taken precisely from the 1247 ASSERT_L( L2_cache_i != 0 );
948 // original function. 'name' is not used since the chunk 1248 STACK_GROW(L,2);
949 // is precompiled (it seems...). 1249 STACK_CHECK(L)
950 //
951 // TBD: Can we get the function's original name through, as well?
952 //
953 if (luaL_loadbuffer(L2, s, sz, name) != 0) {
954 // chunk is precompiled so only LUA_ERRMEM can happen
955 // "Otherwise, it pushes an error message"
956 //
957 STACK_GROW( L,1 );
958 luaL_error( L, "%s", lua_tostring(L2,-1) );
959 }
960 lua_pop(L,1); // remove the dumped string
961 STACK_MID(L,0)
962 }
963 1250
964 /* push over any upvalues; references to this function will come from 1251 if( funcSubType == FST_Bytecode)
965 * cache so we don't end up in eternal loop. 1252 {
966 */ 1253 unsigned n;
967 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) { 1254 luaL_Buffer b;
968 if ((!cfunc) && lua_equal(L,i,-1)) { 1255 // 'lua_dump()' needs the function at top of stack
969 /* Lua closure that has a (recursive) upvalue to itself 1256 // if already on top of the stack, no need to push again
970 */ 1257 int needToPush = (i != (uint_t)lua_gettop( L));
971 lua_pushvalue( L2, -((int)n)-1 ); 1258 if( needToPush)
972 } else { 1259 lua_pushvalue( L, i);
973 if (!inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL )) 1260
974 luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename(L,-1) ); 1261 luaL_buffinit( L, &b);
975 } 1262 //
976 lua_pop(L,1); 1263 // "value returned is the error code returned by the last call
977 } 1264 // to the writer" (and we only return 0)
978 // L2: function + 'n' upvalues (>=0) 1265 // not sure this could ever fail but for memory shortage reasons
1266 if( lua_dump( L, buf_writer, &b) != 0)
1267 {
1268 luaL_error( L, "internal error: function dump failed.");
1269 }
979 1270
980 STACK_MID(L,0) 1271 luaL_pushresult( &b); // pushes dumped string on 'L'
981 1272
982 if (cfunc) { 1273 // if not pushed, no need to pop
983 lua_pushcclosure( L2, cfunc, n ); // eats up upvalues 1274 if( needToPush)
984 } else { 1275 {
985 // Set upvalues (originally set to 'nil' by 'lua_load') 1276 lua_remove( L, -2);
986 // 1277 }
987 int func_index= lua_gettop(L2)-n;
988 1278
989 for( ; n>0; n-- ) { 1279 // transfer the bytecode, then the upvalues, to create a similar closure
990 const char *rc= lua_setupvalue( L2, func_index, n ); 1280 {
991 // 1281 const char *name= NULL;
992 // "assigns the value at the top of the stack to the upvalue and returns its name. 1282
993 // It also pops the value from the stack." 1283 #if LOG_FUNC_INFO
994 1284 // "To get information about a function you push it onto the
995 ASSERT_L(rc); // not having enough slots? 1285 // stack and start the what string with the character '>'."
996 } 1286 //
997 } 1287 {
998 STACK_END(L,0) 1288 lua_Debug ar;
1289 lua_pushvalue( L, i );
1290 lua_getinfo(L, ">nS", &ar); // fills 'name' 'namewhat' and 'linedefined', pops function
1291 name= ar.namewhat;
1292 fprintf( stderr, "NAME: %s @ %d\n", ar.short_src, ar.linedefined); // just gives NULL
1293 }
1294 #endif // LOG_FUNC_INFO
1295 {
1296 const char *s;
1297 size_t sz;
1298 s = lua_tolstring( L, -1, &sz);
1299 ASSERT_L( s && sz);
1300
1301 // Note: Line numbers seem to be taken precisely from the
1302 // original function. 'name' is not used since the chunk
1303 // is precompiled (it seems...).
1304 //
1305 // TBD: Can we get the function's original name through, as well?
1306 //
1307 if (luaL_loadbuffer(L2, s, sz, name) != 0)
1308 {
1309 // chunk is precompiled so only LUA_ERRMEM can happen
1310 // "Otherwise, it pushes an error message"
1311 //
1312 STACK_GROW( L,1);
1313 luaL_error( L, "%s", lua_tostring(L2,-1));
1314 }
1315 lua_pop( L, 1); // remove the dumped string
1316 }
1317 STACK_MID( L, 0)
1318
1319 /* push over any upvalues; references to this function will come from
1320 * cache so we don't end up in eternal loop.
1321 */
1322 for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ )
1323 {
1324 if ((!cfunc) && lua_equal(L,i,-1))
1325 {
1326 /* Lua closure that has a (recursive) upvalue to itself
1327 */
1328 lua_pushvalue( L2, -((int)n)-1 );
1329 }
1330 else
1331 {
1332 if( !inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL))
1333 luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1));
1334 }
1335 lua_pop( L, 1);
1336 }
1337 // L2: function + 'n' upvalues (>=0)
1338
1339 STACK_MID(L,0)
1340
1341 // Set upvalues (originally set to 'nil' by 'lua_load')
1342 {
1343 int func_index = lua_gettop( L2) - n;
1344 for( ; n > 0; -- n)
1345 {
1346 char const *rc = lua_setupvalue( L2, func_index, n);
1347 //
1348 // "assigns the value at the top of the stack to the upvalue and returns its name.
1349 // It also pops the value from the stack."
1350
1351 ASSERT_L(rc); // not having enough slots?
1352 }
1353 }
1354 }
1355 }
1356 else // C function OR LuaJIT fast function!!!
1357 {
1358#if LOG_FUNC_INFO
1359 fprintf( stderr, "NAME: [C] function %p \n", cfunc);
1360#endif // LOG_FUNC_INFO
1361 // No need to transfer upvalues for C/JIT functions since they weren't actually copied, only looked up
1362 lookup_native_func( L2, L, i);
1363 }
1364 STACK_END(L,0)
999} 1365}
1000 1366
1001
1002/* 1367/*
1003* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove 1368* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove
1004* the original value. 1369* the original value.
@@ -1123,7 +1488,7 @@ static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, u
1123 lua_rawset( L2, -3 ); // add to table (pops key & val) 1488 lua_rawset( L2, -3 ); // add to table (pops key & val)
1124 } else { 1489 } else {
1125 luaL_error( L, "Unable to copy over type '%s' (in %s)", 1490 luaL_error( L, "Unable to copy over type '%s' (in %s)",
1126 luaG_typename(L,val_i), 1491 luaL_typename(L,val_i),
1127 vt==VT_NORMAL ? "table":"metatable" ); 1492 vt==VT_NORMAL ? "table":"metatable" );
1128 } 1493 }
1129 } 1494 }
@@ -1291,32 +1656,36 @@ MUTEX_T require_cs;
1291// 1656//
1292// Upvalues: [1]: original 'require' function 1657// Upvalues: [1]: original 'require' function
1293// 1658//
1294static int new_require( lua_State *L ) 1659static int new_require( lua_State *L)
1295{ 1660{
1296 int rc; 1661 int rc, i;
1297 int args= lua_gettop(L); 1662 int args = lua_gettop( L);
1663 //char const* modname = luaL_checkstring( L, 1);
1298 1664
1299 STACK_GROW(L,1); 1665 STACK_GROW( L, args + 1);
1300 STACK_CHECK(L) 1666 STACK_CHECK( L)
1667
1668 lua_pushvalue( L, lua_upvalueindex(1));
1669 for( i = 1; i <= args; ++ i)
1670 lua_pushvalue( L, i);
1301 1671
1302 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would 1672 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
1303 // leave us locked, blocking any future 'require' calls from other lanes. 1673 // leave us locked, blocking any future 'require' calls from other lanes.
1304 // 1674 //
1305 MUTEX_LOCK( &require_cs); 1675 MUTEX_LOCK( &require_cs);
1306 { 1676 {
1307 lua_pushvalue( L, lua_upvalueindex(1) ); 1677 rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
1308 lua_insert( L, 1 );
1309
1310 rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
1311 // 1678 //
1312 // LUA_ERRRUN / LUA_ERRMEM 1679 // LUA_ERRRUN / LUA_ERRMEM
1313 } 1680 }
1314 MUTEX_UNLOCK( &require_cs); 1681 MUTEX_UNLOCK( &require_cs);
1315 1682
1683 // the required module (or an error message) is left on the stack as returned value by original require function
1684 STACK_END( L, 1)
1685
1316 if (rc) 1686 if (rc)
1317 lua_error(L); // error message already at [-1] 1687 lua_error(L); // error message already at [-1]
1318 1688
1319 STACK_END(L,0)
1320 return 1; 1689 return 1;
1321} 1690}
1322 1691
diff --git a/src/tools.h b/src/tools.h
index a080257..67f9874 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -10,10 +10,10 @@
10 10
11#include <assert.h> 11#include <assert.h>
12 12
13// Note: The < -10000 test is to leave registry/global/upvalue indices untouched 13// Note: The < LUA_REGISTRYINDEX test is to leave registry/global/upvalue indices untouched
14// 14//
15#define /*int*/ STACK_ABS(L,n) \ 15#define /*int*/ STACK_ABS(L,n) \
16 ( ((n) >= 0 || (n) <= -10000) ? (n) : lua_gettop(L) +(n) +1 ) 16 ( ((n) >= 0 || (n) <= LUA_REGISTRYINDEX) ? (n) : lua_gettop(L) +(n) +1 )
17 17
18#ifdef NDEBUG 18#ifdef NDEBUG
19 #define _ASSERT_L(lua,c) /*nothing*/ 19 #define _ASSERT_L(lua,c) /*nothing*/
@@ -24,7 +24,7 @@
24 #define DEBUG() /*nothing*/ 24 #define DEBUG() /*nothing*/
25 #define DEBUGEXEC(_code) {} /*nothing*/ 25 #define DEBUGEXEC(_code) {} /*nothing*/
26#else 26#else
27 #define _ASSERT_L(lua,c) { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } 27 #define _ASSERT_L(lua,c) do { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); } while( 0)
28 // 28 //
29 #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L); 29 #define STACK_CHECK(L) { int _oldtop_##L = lua_gettop(L);
30 #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \ 30 #define STACK_MID(L,change) { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \
@@ -37,7 +37,7 @@
37#endif 37#endif
38#define ASSERT_L(c) _ASSERT_L(L,c) 38#define ASSERT_L(c) _ASSERT_L(L,c)
39 39
40#define STACK_GROW(L,n) { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } 40#define STACK_GROW(L,n) do { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); } while( 0)
41 41
42#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L ) 42#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L )
43 43
@@ -46,13 +46,11 @@
46 46
47#define luaG_isany(L,i) (!lua_isnil(L,i)) 47#define luaG_isany(L,i) (!lua_isnil(L,i))
48 48
49#define luaG_typename( L, index ) lua_typename( L, lua_type(L,index) )
50
51typedef void (*luaG_IdFunction)( lua_State *L, char const * const which); 49typedef void (*luaG_IdFunction)( lua_State *L, char const * const which);
52 50
53void luaG_dump( lua_State* L ); 51void luaG_dump( lua_State* L );
54 52
55const char *luaG_openlibs( lua_State *L, const char *libs ); 53lua_State* luaG_newstate( char const* libs, lua_CFunction _on_state_create);
56 54
57int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc); 55int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc);
58void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ); 56void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index );
@@ -72,6 +70,7 @@ int luaG_inter_move( lua_State *L, lua_State *L2, uint_t n);
72extern MUTEX_T deep_lock; 70extern MUTEX_T deep_lock;
73extern MUTEX_T mtid_lock; 71extern MUTEX_T mtid_lock;
74 72
73void populate_func_lookup_table( lua_State *L, int _i, char const *_name);
75void serialize_require( lua_State *L); 74void serialize_require( lua_State *L);
76extern MUTEX_T require_cs; 75extern MUTEX_T require_cs;
77 76