diff options
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 215 |
1 files changed, 2 insertions, 213 deletions
diff --git a/src/lanes.c b/src/lanes.c index 0a89959..a8aba71 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -102,6 +102,7 @@ THE SOFTWARE. | |||
102 | 102 | ||
103 | #include "threading.h" | 103 | #include "threading.h" |
104 | #include "tools.h" | 104 | #include "tools.h" |
105 | #include "keeper.h" | ||
105 | 106 | ||
106 | #if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) | 107 | #if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)) |
107 | # include <sys/time.h> | 108 | # include <sys/time.h> |
@@ -113,12 +114,6 @@ THE SOFTWARE. | |||
113 | # include <sys/types.h> | 114 | # include <sys/types.h> |
114 | #endif | 115 | #endif |
115 | 116 | ||
116 | /* The selected number is not optimal; needs to be tested. Even using just | ||
117 | * one keeper state may be good enough (depends on the number of Lindas used | ||
118 | * in the applications). | ||
119 | */ | ||
120 | #define KEEPER_STATES_N 1 // 6 | ||
121 | |||
122 | /* Do you want full call stacks, or just the line where the error happened? | 117 | /* Do you want full call stacks, or just the line where the error happened? |
123 | * | 118 | * |
124 | * TBD: The full stack feature does not seem to work (try 'make error'). | 119 | * TBD: The full stack feature does not seem to work (try 'make error'). |
@@ -129,12 +124,6 @@ THE SOFTWARE. | |||
129 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key | 124 | # define STACK_TRACE_KEY ((void*)lane_error) // used as registry key |
130 | #endif | 125 | #endif |
131 | 126 | ||
132 | /* | ||
133 | * Lua code for the keeper states (baked in) | ||
134 | */ | ||
135 | static char keeper_chunk[]= | ||
136 | #include "keeper.lch" | ||
137 | |||
138 | // NOTE: values to be changed by either thread, during execution, without | 127 | // NOTE: values to be changed by either thread, during execution, without |
139 | // locking, are marked "volatile" | 128 | // locking, are marked "volatile" |
140 | // | 129 | // |
@@ -254,199 +243,6 @@ static bool_t push_registry_table( lua_State *L, void *key, bool_t create ) { | |||
254 | } | 243 | } |
255 | 244 | ||
256 | 245 | ||
257 | /*---=== Serialize require ===--- | ||
258 | */ | ||
259 | |||
260 | static MUTEX_T require_cs; | ||
261 | |||
262 | //--- | ||
263 | // [val]= new_require( ... ) | ||
264 | // | ||
265 | // Call 'old_require' but only one lane at a time. | ||
266 | // | ||
267 | // Upvalues: [1]: original 'require' function | ||
268 | // | ||
269 | static int new_require( lua_State *L ) { | ||
270 | int rc; | ||
271 | int args= lua_gettop(L); | ||
272 | |||
273 | STACK_GROW(L,1); | ||
274 | STACK_CHECK(L) | ||
275 | |||
276 | // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would | ||
277 | // leave us locked, blocking any future 'require' calls from other lanes. | ||
278 | // | ||
279 | MUTEX_LOCK( &require_cs ); | ||
280 | { | ||
281 | lua_pushvalue( L, lua_upvalueindex(1) ); | ||
282 | lua_insert( L, 1 ); | ||
283 | |||
284 | rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ ); | ||
285 | // | ||
286 | // LUA_ERRRUN / LUA_ERRMEM | ||
287 | } | ||
288 | MUTEX_UNLOCK( &require_cs ); | ||
289 | |||
290 | if (rc) lua_error(L); // error message already at [-1] | ||
291 | |||
292 | STACK_END(L,0) | ||
293 | return 1; | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * Serialize calls to 'require', if it exists | ||
298 | */ | ||
299 | static | ||
300 | void serialize_require( lua_State *L ) { | ||
301 | |||
302 | STACK_GROW(L,1); | ||
303 | STACK_CHECK(L) | ||
304 | |||
305 | // Check 'require' is there; if not, do nothing | ||
306 | // | ||
307 | lua_getglobal( L, "require" ); | ||
308 | if (lua_isfunction( L, -1 )) { | ||
309 | // [-1]: original 'require' function | ||
310 | |||
311 | lua_pushcclosure( L, new_require, 1 /*upvalues*/ ); | ||
312 | lua_setglobal( L, "require" ); | ||
313 | |||
314 | } else { | ||
315 | // [-1]: nil | ||
316 | lua_pop(L,1); | ||
317 | } | ||
318 | |||
319 | STACK_END(L,0) | ||
320 | } | ||
321 | |||
322 | |||
323 | /*---=== Keeper states ===--- | ||
324 | */ | ||
325 | |||
326 | /* | ||
327 | * Pool of keeper states | ||
328 | * | ||
329 | * Access to keeper states is locked (only one OS thread at a time) so the | ||
330 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | ||
331 | * keepers randomly, by a hash. | ||
332 | */ | ||
333 | struct s_Keeper | ||
334 | { | ||
335 | MUTEX_T lock_; | ||
336 | lua_State *L; | ||
337 | //int count; | ||
338 | } GKeepers[KEEPER_STATES_N]; | ||
339 | |||
340 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe | ||
341 | * checking for a lightuserdata is faster. | ||
342 | */ | ||
343 | static bool_t nil_sentinel; | ||
344 | |||
345 | /* | ||
346 | * Initialize keeper states | ||
347 | * | ||
348 | * If there is a problem, return an error message (NULL for okay). | ||
349 | * | ||
350 | * Note: Any problems would be design flaws; the created Lua state is left | ||
351 | * unclosed, because it does not really matter. In production code, this | ||
352 | * function never fails. | ||
353 | */ | ||
354 | static const char *init_keepers(void) { | ||
355 | unsigned int i; | ||
356 | for( i=0; i<KEEPER_STATES_N; i++ ) { | ||
357 | |||
358 | // Initialize Keeper states with bare minimum of libs (those required | ||
359 | // by 'keeper.lua') | ||
360 | // | ||
361 | lua_State *L= luaL_newstate(); | ||
362 | if (!L) return "out of memory"; | ||
363 | |||
364 | luaG_openlibs( L, "io,table,package" ); // 'io' for debugging messages, package because we need to require modules exporting idfuncs | ||
365 | serialize_require( L); | ||
366 | |||
367 | lua_pushlightuserdata( L, &nil_sentinel ); | ||
368 | lua_setglobal( L, "nil_sentinel" ); | ||
369 | |||
370 | // Read in the preloaded chunk (and run it) | ||
371 | // | ||
372 | if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" )) | ||
373 | return "luaL_loadbuffer() failed"; // LUA_ERRMEM | ||
374 | |||
375 | if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) { | ||
376 | // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR | ||
377 | // | ||
378 | const char *err= lua_tostring(L,-1); | ||
379 | assert(err); | ||
380 | return err; | ||
381 | } | ||
382 | |||
383 | MUTEX_INIT( &GKeepers[i].lock_ ); | ||
384 | GKeepers[i].L= L; | ||
385 | //GKeepers[i].count = 0; | ||
386 | } | ||
387 | return NULL; // ok | ||
388 | } | ||
389 | |||
390 | static struct s_Keeper *keeper_acquire( const void *ptr) | ||
391 | { | ||
392 | /* | ||
393 | * Any hashing will do that maps pointers to 0..KEEPER_STATES_N-1 | ||
394 | * consistently. | ||
395 | * | ||
396 | * Pointers are often aligned by 8 or so - ignore the low order bits | ||
397 | */ | ||
398 | unsigned int i= ((unsigned long)(ptr) >> 3) % KEEPER_STATES_N; | ||
399 | struct s_Keeper *K= &GKeepers[i]; | ||
400 | |||
401 | MUTEX_LOCK( &K->lock_); | ||
402 | //++ K->count; | ||
403 | return K; | ||
404 | } | ||
405 | |||
406 | static void keeper_release( struct s_Keeper *K) | ||
407 | { | ||
408 | //-- K->count; | ||
409 | MUTEX_UNLOCK( &K->lock_); | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * Call a function ('func_name') in the keeper state, and pass on the returned | ||
414 | * values to 'L'. | ||
415 | * | ||
416 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | ||
417 | * 'starting_index': first of the rest of parameters (none if 0) | ||
418 | * | ||
419 | * Returns: number of return values (pushed to 'L') or -1 in case of error | ||
420 | */ | ||
421 | static int keeper_call( lua_State *K, char const *func_name, lua_State *L, struct s_Linda *linda, uint_t starting_index) | ||
422 | { | ||
423 | int const args = starting_index ? (lua_gettop(L) - starting_index +1) : 0; | ||
424 | int const Ktos = lua_gettop(K); | ||
425 | int retvals = -1; | ||
426 | |||
427 | STACK_GROW( K, 2); | ||
428 | |||
429 | lua_getglobal( K, func_name); | ||
430 | ASSERT_L( lua_isfunction(K, -1)); | ||
431 | |||
432 | lua_pushlightuserdata( K, linda); | ||
433 | |||
434 | if( (args == 0) || luaG_inter_copy( L, K, args) == 0) // L->K | ||
435 | { | ||
436 | lua_call( K, 1 + args, LUA_MULTRET); | ||
437 | |||
438 | retvals = lua_gettop( K) - Ktos; | ||
439 | if( (retvals > 0) && luaG_inter_move( K, L, retvals) != 0) // K->L | ||
440 | { | ||
441 | retvals = -1; | ||
442 | } | ||
443 | } | ||
444 | // whatever happens, restore the stack to where it was at the origin | ||
445 | lua_settop( K, Ktos); | ||
446 | return retvals; | ||
447 | } | ||
448 | |||
449 | |||
450 | /*---=== Linda ===--- | 246 | /*---=== Linda ===--- |
451 | */ | 247 | */ |
452 | 248 | ||
@@ -1189,14 +985,7 @@ static void selfdestruct_atexit( void ) { | |||
1189 | DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); | 985 | DEBUGEXEC(fprintf( stderr, "Killed %d lane(s) at process end.\n", n )); |
1190 | #endif | 986 | #endif |
1191 | } | 987 | } |
1192 | { | 988 | close_keepers(); |
1193 | int i; | ||
1194 | for(i=0;i<KEEPER_STATES_N;i++){ | ||
1195 | lua_close( GKeepers[i].L); | ||
1196 | GKeepers[i].L = 0; | ||
1197 | //assert( GKeepers[i].count == 0); | ||
1198 | } | ||
1199 | } | ||
1200 | } | 989 | } |
1201 | 990 | ||
1202 | 991 | ||