diff --git a/CONTRIBUTING b/CONTRIBUTING
new file mode 100644
index 0000000000000000000000000000000000000000..7c29859c8a7a64652532e031e28a3bc3b6cbae75
--- /dev/null
+++ b/CONTRIBUTING
@@ -0,0 +1,4 @@
+I (Nicolas Casalini), gladly accepts contributions to the engine code either
+bugfixes or new features (although I feel no obligations to accept them all).
+
+To contribute
diff --git a/game/thirdparty/lanes.lua b/game/thirdparty/lanes.lua
new file mode 100644
index 0000000000000000000000000000000000000000..28b19cf046b93f9a66c3381ad4d511f5c7a89fe0
--- /dev/null
+++ b/game/thirdparty/lanes.lua
@@ -0,0 +1,611 @@
+--
+-- LANES.LUA
+--
+-- Multithreading and -core support for Lua
+--
+-- Author: Asko Kauppi <akauppi@gmail.com>
+--
+-- History:
+--    Jun-08 AKa: major revise
+--    15-May-07 AKa: pthread_join():less version, some speedup & ability to
+--                   handle more threads (~ 8000-9000, up from ~ 5000)
+--    26-Feb-07 AKa: serialization working (C side)
+--    17-Sep-06 AKa: started the module (serialization)
+--
+--[[
+===============================================================================
+
+Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+]]--
+
+module( "lanes", package.seeall )
+
+--require "lua51-lanes"
+assert( type(lanes)=="table" )
+
+local mm= lanes
+
+local linda_id=    assert( mm.linda_id )
+
+local thread_new=   assert(mm.thread_new)
+local thread_status= assert(mm.thread_status)
+local thread_join=  assert(mm.thread_join)
+local thread_cancel= assert(mm.thread_cancel)
+
+local _single= assert(mm._single)
+local _version= assert(mm._version)
+
+local _deep_userdata= assert(mm._deep_userdata)
+
+local now_secs= assert( mm.now_secs )
+local wakeup_conv= assert( mm.wakeup_conv )
+local timer_gateway= assert( mm.timer_gateway )
+
+local max_prio= assert( mm.max_prio )
+
+-- This check is for sublanes requiring Lanes
+--
+-- TBD: We could also have the C level expose 'string.gmatch' for us. But this is simpler.
+--
+if not string then
+    error( "To use 'lanes', you will also need to have 'string' available.", 2 )
+end
+
+--
+-- Cache globals for code that might run under sandboxing
+--
+local assert= assert
+local string_gmatch= assert( string.gmatch )
+local select= assert( select )
+local type= assert( type )
+local pairs= assert( pairs )
+local tostring= assert( tostring )
+local error= assert( error )
+local setmetatable= assert( setmetatable )
+local rawget= assert( rawget )
+
+ABOUT=
+{
+    author= "Asko Kauppi <akauppi@gmail.com>",
+    description= "Running multiple Lua states in parallel",
+    license= "MIT/X11",
+    copyright= "Copyright (c) 2007-08, Asko Kauppi",
+    version= _version,
+}
+
+
+-- Making copies of necessary system libs will pass them on as upvalues;
+-- only the first state doing "require 'lanes'" will need to have 'string'
+-- and 'table' visible.
+--
+local function WR(str)
+    io.stderr:write( str.."\n" )
+end
+
+local function DUMP( tbl )
+    if not tbl then return end
+    local str=""
+    for k,v in pairs(tbl) do
+        str= str..k.."="..tostring(v).."\n"
+    end
+    WR(str)
+end
+
+
+---=== Laning ===---
+
+-- lane_h[1..n]: lane results, same as via 'lane_h:join()'
+-- lane_h[0]:    can be read to make sure a thread has finished (always gives 'true')
+-- lane_h[-1]:   error message, without propagating the error
+--
+--      Reading a Lane result (or [0]) propagates a possible error in the lane
+--      (and execution does not return). Cancelled lanes give 'nil' values.
+--
+-- lane_h.state: "pending"/"running"/"waiting"/"done"/"error"/"cancelled"
+--
+local lane_mt= {
+    __index= function( me, k )
+                if type(k) == "number" then
+                    -- 'me[0]=true' marks we've already taken in the results
+                    --
+                    if not rawget( me, 0 ) then
+                        -- Wait indefinately; either propagates an error or
+                        -- returns the return values
+                        --
+                        me[0]= true  -- marker, even on errors
+
+                        local t= { thread_join(me._ud) }   -- wait indefinate
+                            --
+                            -- { ... }      "done": regular return, 0..N results
+                            -- { }          "cancelled"
+                            -- { nil, err_str, stack_tbl } "error"
+
+                        local st= thread_status(me._ud)
+                        if st=="done" then
+                            -- Use 'pairs' and not 'ipairs' so that nil holes in
+                            -- the returned values are tolerated.
+                            --
+                            for i,v in pairs(t) do
+                                me[i]= v
+                            end
+                        elseif st=="error" then
+                            assert( t[1]==nil and t[2] and type(t[3])=="table" )
+                            me[-1]= t[2]
+                            -- me[-2] could carry the stack table, but even
+                            -- me[-1] is rather unnecessary (and undocumented);
+                            -- use ':join()' instead.   --AKa 22-Jan-2009
+                        elseif st=="cancelled" then
+                            -- do nothing
+                        else
+                            error( "Unexpected status: "..st )
+                        end
+                    end
+
+                    -- Check errors even if we'd first peeked them via [-1]
+                    -- and then came for the actual results.
+                    --
+                    local err= rawget(me, -1)
+                    if err~=nil and k~=-1 then
+                        -- Note: Lua 5.1 interpreter is not prepared to show
+                        --       non-string errors, so we use 'tostring()' here
+                        --       to get meaningful output.  --AKa 22-Jan-2009
+                        --
+                        --       Also, the stack dump we get is no good; it only
+                        --       lists our internal Lanes functions. There seems
+                        --       to be no way to switch it off, though.
+
+                        -- Level 3 should show the line where 'h[x]' was read
+                        -- but this only seems to work for string messages
+                        -- (Lua 5.1.4). No idea, why.   --AKa 22-Jan-2009
+                        --
+                        error( tostring(err), 3 )   -- level 3 should show the line where 'h[x]' was read
+                    end
+                    return rawget( me, k )
+                    --
+                elseif k=="status" then     -- me.status
+                    return thread_status(me._ud)
+                    --
+                else
+                    error( "Unknown key: "..k )
+                end
+             end
+    }
+
+-----
+-- h= lanes.gen( [libs_str|opt_tbl [, ...],] lane_func ) ( [...] )
+--
+-- 'libs': nil:     no libraries available (default)
+--         "":      only base library ('assert', 'print', 'unpack' etc.)
+--         "math,os": math + os + base libraries (named ones + base)
+--         "*":     all standard libraries available
+--
+-- 'opt': .priority:  int (-2..+2) smaller is lower priority (0 = default)
+--
+--	      .cancelstep: bool | uint
+--            false: cancellation check only at pending Linda operations
+--                   (send/receive) so no runtime performance penalty (default)
+--            true:  adequate cancellation check (same as 100)
+--            >0:    cancellation check every x Lua lines (small number= faster
+--                   reaction but more performance overhead)
+--
+--        .globals:  table of globals to set for a new thread (passed by value)
+--
+--        ... (more options may be introduced later) ...
+--
+-- Calling with a function parameter ('lane_func') ends the string/table
+-- modifiers, and prepares a lane generator. One can either finish here,
+-- and call the generator later (maybe multiple times, with different parameters)
+-- or add on actual thread arguments to also ignite the thread on the same call.
+--
+local lane_proxy
+
+local valid_libs= {
+    ["package"]= true,
+    ["table"]= true,
+    ["io"]= true,
+    ["os"]= true,
+    ["string"]= true,
+    ["math"]= true,
+    ["debug"]= true,
+    --
+    ["base"]= true,
+    ["coroutine"]= true,
+    ["*"]= true
+}
+
+function gen( ... )
+    local opt= {}
+    local libs= nil
+    local lev= 2  -- level for errors
+
+    local n= select('#',...)
+
+    if n==0 then
+        error( "No parameters!" )
+    end
+
+    for i=1,n-1 do
+        local v= select(i,...)
+        if type(v)=="string" then
+            libs= libs and libs..","..v or v
+        elseif type(v)=="table" then
+            for k,vv in pairs(v) do
+                opt[k]= vv
+            end
+        elseif v==nil then
+            -- skip
+        else
+            error( "Bad parameter: "..tostring(v) )
+        end
+    end
+
+    local func= select(n,...)
+    if type(func)~="function" then
+        error( "Last parameter not function: "..tostring(func) )
+    end
+
+    -- Check 'libs' already here, so the error goes in the right place
+    -- (otherwise will be noticed only once the generator is called)
+    --
+    if libs then
+        for s in string_gmatch(libs, "[%a*]+") do
+            if not valid_libs[s] then
+                error( "Bad library name: "..s )
+            end
+        end
+    end
+
+    local prio, cs, g_tbl
+
+    for k,v in pairs(opt) do
+            if k=="priority" then prio= v
+        elseif k=="cancelstep" then cs= (v==true) and 100 or
+                                        (v==false) and 0 or
+                                        type(v)=="number" and v or
+                                        error( "Bad cancelstep: "..tostring(v), lev )
+        elseif k=="globals" then g_tbl= v
+        --..
+        elseif k==1 then error( "unkeyed option: ".. tostring(v), lev )
+        else error( "Bad option: ".. tostring(k), lev )
+        end
+    end
+
+    -- Lane generator
+    --
+    return function(...)
+              return lane_proxy( thread_new( func, libs, cs, prio, g_tbl,
+                                             ... ) )     -- args
+           end
+end
+
+lane_proxy= function( ud )
+    local proxy= {
+        _ud= ud,
+
+        -- void= me:cancel()
+        --
+        cancel= function(me) thread_cancel(me._ud) end,
+
+        -- [...] | [nil,err,stack_tbl]= me:join( [wait_secs=-1] )
+        --
+        join= function( me, wait )
+                return thread_join( me._ud, wait )
+            end,
+        }
+    assert( proxy._ud )
+    setmetatable( proxy, lane_mt )
+
+    return proxy
+end
+
+
+---=== Lindas ===---
+
+-- We let the C code attach methods to userdata directly
+
+-----
+-- linda_ud= lanes.linda()
+--
+function linda()
+    local proxy= _deep_userdata( linda_id )
+    assert( (type(proxy) == "userdata") and getmetatable(proxy) )
+    return proxy
+end
+
+
+---=== Timers ===---
+
+--
+-- On first 'require "lanes"', a timer lane is spawned that will maintain
+-- timer tables and sleep in between the timer events. All interaction with
+-- the timer lane happens via a 'timer_gateway' Linda, which is common to
+-- all that 'require "lanes"'.
+--
+-- Linda protocol to timer lane:
+--
+--  TGW_KEY: linda_h, key, [wakeup_at_secs], [repeat_secs]
+--
+local TGW_KEY= "(timer control)"    -- the key does not matter, a 'weird' key may help debugging
+local first_time_key= "first time"
+
+local first_time= timer_gateway:get(first_time_key) == nil
+timer_gateway:set(first_time_key,true)
+
+--
+-- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally
+-- has 'table' always declared)
+--
+if first_time then
+    local table_remove= assert( table.remove )
+    local table_insert= assert( table.insert )
+
+    --
+    -- { [deep_linda_lightuserdata]= { [deep_linda_lightuserdata]=linda_h,
+    --                                 [key]= { wakeup_secs [,period_secs] } [, ...] },
+    -- }
+    --
+    -- Collection of all running timers, indexed with linda's & key.
+    --
+    -- Note that we need to use the deep lightuserdata identifiers, instead
+    -- of 'linda_h' themselves as table indices. Otherwise, we'd get multiple
+    -- entries for the same timer.
+    --
+    -- The 'hidden' reference to Linda proxy is used in 'check_timers()' but
+    -- also important to keep the Linda alive, even if all outside world threw
+    -- away pointers to it (which would ruin uniqueness of the deep pointer).
+    -- Now we're safe.
+    --
+    local collection= {}
+
+    --
+    -- set_timer( linda_h, key [,wakeup_at_secs [,period_secs]] )
+    --
+    local function set_timer( linda, key, wakeup_at, period )
+
+        assert( wakeup_at==nil or wakeup_at>0.0 )
+        assert( period==nil or period>0.0 )
+
+        local linda_deep= linda:deep()
+        assert( linda_deep )
+
+        -- Find or make a lookup for this timer
+        --
+        local t1= collection[linda_deep]
+        if not t1 then
+            t1= { [linda_deep]= linda }     -- proxy to use the Linda
+            collection[linda_deep]= t1
+        end
+
+        if wakeup_at==nil then
+            -- Clear the timer
+            --
+            t1[key]= nil
+
+            -- Remove empty tables from collection; speeds timer checks and
+            -- lets our 'safety reference' proxy be gc:ed as well.
+            --
+            local empty= true
+            for k,_ in pairs(t1) do
+                if k~= linda_deep then
+                    empty= false; break
+                end
+            end
+            if empty then
+                collection[linda_deep]= nil
+            end
+
+            -- Note: any unread timer value is left at 'linda[key]' intensionally;
+            --       clearing a timer just stops it.
+        else
+            -- New timer or changing the timings
+            --
+            local t2= t1[key]
+            if not t2 then
+                t2= {}; t1[key]= t2
+            end
+
+            t2[1]= wakeup_at
+            t2[2]= period   -- can be 'nil'
+        end
+    end
+
+    -----
+    -- [next_wakeup_at]= check_timers()
+    --
+    -- Check timers, and wake up the ones expired (if any)
+    --
+    -- Returns the closest upcoming (remaining) wakeup time (or 'nil' if none).
+    --
+    local function check_timers()
+
+        local now= now_secs()
+        local next_wakeup
+
+        for linda_deep,t1 in pairs(collection) do
+            for key,t2 in pairs(t1) do
+                --
+                if key==linda_deep then
+                    -- no 'continue' in Lua :/
+                else
+                    -- 't2': { wakeup_at_secs [,period_secs] }
+                    --
+                    local wakeup_at= t2[1]
+                    local period= t2[2]     -- may be 'nil'
+
+                    if wakeup_at <= now then
+                        local linda= t1[linda_deep]
+                        assert(linda)
+
+                        linda:set( key, now )
+
+                        -- 'pairs()' allows the values to be modified (and even
+                        -- removed) as far as keys are not touched
+
+                        if not period then
+                            -- one-time timer; gone
+                            --
+                            t1[key]= nil
+                            wakeup_at= nil   -- no 'continue' in Lua :/
+                        else
+                            -- repeating timer; find next wakeup (may jump multiple repeats)
+                            --
+                            repeat
+                                wakeup_at= wakeup_at+period
+                            until wakeup_at > now
+
+                            t2[1]= wakeup_at
+                        end
+                    end
+
+                    if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then
+                        next_wakeup= wakeup_at
+                    end
+                end
+            end -- t2 loop
+        end -- t1 loop
+
+        return next_wakeup  -- may be 'nil'
+    end
+
+    -----
+    -- Snore loop (run as a lane on the background)
+    --
+    -- High priority, to get trustworthy timings.
+    --
+    -- We let the timer lane be a "free running" thread; no handle to it
+    -- remains.
+    --
+    gen( "io", { priority=max_prio }, function()
+
+        while true do
+            local next_wakeup= check_timers()
+
+            -- Sleep until next timer to wake up, or a set/clear command
+            --
+            local secs= next_wakeup and (next_wakeup - now_secs()) or nil
+            local linda= timer_gateway:receive( secs, TGW_KEY )
+
+            if linda then
+                local key= timer_gateway:receive( 0.0, TGW_KEY )
+                local wakeup_at= timer_gateway:receive( 0.0, TGW_KEY )
+                local period= timer_gateway:receive( 0.0, TGW_KEY )
+                assert( key and wakeup_at and period )
+
+                set_timer( linda, key, wakeup_at, period>0 and period or nil )
+            end
+        end
+    end )()
+end
+
+-----
+-- = timer( linda_h, key_val, date_tbl|first_secs [,period_secs] )
+--
+function timer( linda, key, a, period )
+
+    if a==0.0 then
+        -- Caller expects to get current time stamp in Linda, on return
+        -- (like the timer had expired instantly); it would be good to set this
+        -- as late as possible (to give most current time) but also we want it
+        -- to precede any possible timers that might start striking.
+        --
+        linda:set( key, now_secs() )
+
+        if not period or period==0.0 then
+            timer_gateway:send( TGW_KEY, linda, key, nil, nil )   -- clear the timer
+            return  -- nothing more to do
+        end
+        a= period
+    end
+
+    local wakeup_at= type(a)=="table" and wakeup_conv(a)    -- given point of time
+                                       or now_secs()+a
+    -- queue to timer
+    --
+    timer_gateway:send( TGW_KEY, linda, key, wakeup_at, period )
+end
+
+
+---=== Lock & atomic generators ===---
+
+-- These functions are just surface sugar, but make solutions easier to read.
+-- Not many applications should even need explicit locks or atomic counters.
+
+--
+-- lock_f= lanes.genlock( linda_h, key [,N_uint=1] )
+--
+-- = lock_f( +M )   -- acquire M
+--      ...locked...
+-- = lock_f( -M )   -- release M
+--
+-- Returns an access function that allows 'N' simultaneous entries between
+-- acquire (+M) and release (-M). For binary locks, use M==1.
+--
+function genlock( linda, key, N )
+    linda:limit(key,N)
+    linda:set(key,nil)  -- clears existing data
+
+    --
+    -- [true [, ...]= trues(uint)
+    --
+    local function trues(n)
+        if n>0 then return true,trues(n-1) end
+    end
+
+    return
+    function(M)
+        if M>0 then
+            -- 'nil' timeout allows 'key' to be numeric
+            linda:send( nil, key, trues(M) )    -- suspends until been able to push them
+        else
+            for i=1,-M do
+                linda:receive( key )
+            end
+        end
+    end
+end
+
+
+--
+-- atomic_f= lanes.genatomic( linda_h, key [,initial_num=0.0] )
+--
+-- int= atomic_f( [diff_num=1.0] )
+--
+-- Returns an access function that allows atomic increment/decrement of the
+-- number in 'key'.
+--
+function genatomic( linda, key, initial_val )
+    linda:limit(key,2)          -- value [,true]
+    linda:set(key,initial_val or 0.0)   -- clears existing data (also queue)
+
+    return
+    function(diff)
+        -- 'nil' allows 'key' to be numeric
+        linda:send( nil, key, true )    -- suspends until our 'true' is in
+        local val= linda:get(key) + (diff or 1.0)
+        linda:set( key, val )   -- releases the lock, by emptying queue
+        return val
+    end
+end
+
+
+--the end
diff --git a/src/lualanes/keeper.lch b/src/lualanes/keeper.lch
new file mode 100644
index 0000000000000000000000000000000000000000..6d5f2fc482828dfce29998a05bbfc423878606bb
--- /dev/null
+++ b/src/lualanes/keeper.lch
@@ -0,0 +1,214 @@
+/* bin2c.lua generated code -- DO NOT EDIT
+ *
+ * To use from C source: 
+ *    char my_chunk[]=
+ *    #include "my.lch"
+ */
+{ 
+   27, 76,117, 97, 81,  0,  1,  4,  4,  4,  8,  0, 12,  0,  0,  0, 64,107,101,101,
+  112,101,114, 46,108,117, 97,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  9,
+   45,  0,  0,  0,  5,  0,  0,  0, 69, 64,  0,  0, 28, 64,  0,  1,  5,  0,  0,  0,
+   69,128,  0,  0, 70,192,192,  0, 28,128,  0,  1, 69,  0,  0,  0,133,128,  0,  0,
+  134,  0, 65,  1, 92,128,  0,  1,164,  0,  0,  0,  0,  0,128,  0,202,  0,  0,  0,
+   10,  1,  0,  0, 74,  1,  0,  0,164, 65,  0,  0,  0,  0,128,  1,  0,  0,  0,  2,
+    0,  0,128,  2,228,129,  0,  0,  0,  0,  0,  3,  0,  0,  0,  1, 36,194,  0,  0,
+    0,  0,  0,  3,  7, 66,  1,  0, 36,  2,  1,  0,  0,  0,  0,  3,  0,  0,  0,  0,
+    7,130,  1,  0, 36, 66,  1,  0,  0,  0,  0,  3,  7,194,  1,  0, 36,130,  1,  0,
+    0,  0,  0,  3,  7,  2,  2,  0, 36,194,  1,  0,  0,  0,  0,  3,  7, 66,  2,  0,
+   36,  2,  2,  0,  0,  0,128,  1,  0,  0,  0,  2,  0,  0,128,  2,  7,130,  2,  0,
+   30,  0,128,  0, 11,  0,  0,  0,  4,  7,  0,  0,  0, 97,115,115,101,114,116,  0,
+    4, 13,  0,  0,  0,110,105,108, 95,115,101,110,116,105,110,101,108,  0,  4,  6,
+    0,  0,  0,116, 97, 98,108,101,  0,  4,  7,  0,  0,  0,114,101,109,111,118,101,
+    0,  4,  7,  0,  0,  0, 99,111,110, 99, 97,116,  0,  4,  5,  0,  0,  0,115,101,
+  110,100,  0,  4,  8,  0,  0,  0,114,101, 99,101,105,118,101,  0,  4,  6,  0,  0,
+    0,108,105,109,105,116,  0,  4,  4,  0,  0,  0,115,101,116,  0,  4,  4,  0,  0,
+    0,103,101,116,  0,  4,  6,  0,  0,  0, 99,108,101, 97,114,  0,  9,  0,  0,  0,
+    0,  0,  0,  0, 46,  0,  0,  0, 50,  0,  0,  0,  1,  0,  3,  6, 16,  0,  0,  0,
+   69,  0,  0,  0, 90,  0,  0,  0, 22,192,  2,128, 69,  0,  0,  0, 70, 64,192,  0,
+   75,128,192,  0,196,  0,  0,  0, 10,  1,  0,  0,101,  1,  0,  0, 34, 65,  0,  0,
+   65,193,  0,  0,220,128,128,  1,  1,  1,  1,  0,213,  0,129,  1, 92, 64,128,  1,
+   30,  0,128,  0,  5,  0,  0,  0,  4,  3,  0,  0,  0,105,111,  0,  4,  7,  0,  0,
+    0,115,116,100,101,114,114,  0,  4,  6,  0,  0,  0,119,114,105,116,101,  0,  4,
+    2,  0,  0,  0,  9,  0,  4,  2,  0,  0,  0, 10,  0,  0,  0,  0,  0, 16,  0,  0,
+    0, 47,  0,  0,  0, 47,  0,  0,  0, 47,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,
+    0, 48,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,
+    0, 48,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,  0, 48,  0,  0,
+    0, 50,  0,  0,  0,  1,  0,  0,  0,  4,  0,  0,  0, 97,114,103,  0,  0,  0,  0,
+    0, 15,  0,  0,  0,  1,  0,  0,  0, 13,  0,  0,  0,116, 97, 98,108,101, 95, 99,
+  111,110, 99, 97,116,  0,  0,  0,  0,  0, 84,  0,  0,  0, 93,  0,  0,  0,  3,  1,
+    0,  4, 21,  0,  0,  0, 68,  0,  0,  0, 70,  0,128,  0, 90, 64,  0,  0, 22,  0,
+    2,128, 68,  0,  0,  0,138,  0,  0,  0, 73,128,  0,  0, 68,  0,128,  0,138,  0,
+    0,  0, 73,128,  0,  0, 68,  0,  0,  1,138,  0,  0,  0, 73,128,  0,  0, 68,  0,
+    0,  0, 70,  0,128,  0,132,  0,128,  0,134,  0,  0,  1,196,  0,  0,  1,198,  0,
+  128,  1, 94,  0,  0,  2, 30,  0,128,  0,  0,  0,  0,  0,  0,  0,  0,  0, 21,  0,
+    0,  0, 87,  0,  0,  0, 87,  0,  0,  0, 87,  0,  0,  0, 87,  0,  0,  0, 88,  0,
+    0,  0, 88,  0,  0,  0, 88,  0,  0,  0, 89,  0,  0,  0, 89,  0,  0,  0, 89,  0,
+    0,  0, 90,  0,  0,  0, 90,  0,  0,  0, 90,  0,  0,  0, 92,  0,  0,  0, 92,  0,
+    0,  0, 92,  0,  0,  0, 92,  0,  0,  0, 92,  0,  0,  0, 92,  0,  0,  0, 92,  0,
+    0,  0, 93,  0,  0,  0,  1,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,  0,  0,
+    0, 20,  0,  0,  0,  3,  0,  0,  0,  6,  0,  0,  0, 95,100, 97,116, 97,  0, 10,
+    0,  0,  0, 95,105,110, 99,111,109,105,110,103,  0,  8,  0,  0,  0, 95,108,105,
+  109,105,116,115,  0,  0,  0,  0,  0, 96,  0,  0,  0,106,  0,  0,  0,  2,  3,  0,
+   16, 40,  0,  0,  0,197,  0,  0,  0, 27,  1,  0,  0, 22,128,  0,128, 27,  1,128,
+    0, 22,  0,  0,128,  0,  1,  0,  1,220, 64,  0,  1,196,  0,  0,  0,  0,  1,128,
+    0,220,  0,  1,  1,133, 65,  0,  0,198,129,128,  1,156,129,  0,  1,197,129,  0,
+    0,  6,130,  0,  2, 26, 66,  0,  0, 22,  0,  0,128, 10,  2,  0,  0,220,  1,  1,
+    1, 22, 64,  1,128,  0,  3,  0,  3, 65,195,  0,  0,133, 67,  0,  0,192,  3,128,
+    5,156,131,  0,  1,149,129,  3,  6,225,129,  0,  0, 22,192,253,127,196,  1,128,
+    0,  1,  2,  1,  0, 64,  2,  0,  0,129, 66,  1,  0,197, 66,  0,  0,  0,  3,  0,
+    1,220,130,  0,  1,  1,131,  1,  0, 21,  2,  3,  4, 64,  2,  0,  3,220, 65,128,
+    1, 30,  0,128,  0,  7,  0,  0,  0,  4,  7,  0,  0,  0, 97,115,115,101,114,116,
+    0,  4,  9,  0,  0,  0,116,111,115,116,114,105,110,103,  0,  4,  7,  0,  0,  0,
+  105,112, 97,105,114,115,  0,  4,  3,  0,  0,  0, 44, 32,  0,  4,  5,  0,  0,  0,
+   42, 42, 42, 32,  0,  4,  3,  0,  0,  0, 32, 40,  0,  4,  4,  0,  0,  0, 41, 58,
+   32,  0,  0,  0,  0,  0, 40,  0,  0,  0, 97,  0,  0,  0, 97,  0,  0,  0, 97,  0,
+    0,  0, 97,  0,  0,  0, 97,  0,  0,  0, 97,  0,  0,  0, 97,  0,  0,  0, 99,  0,
+    0,  0, 99,  0,  0,  0, 99,  0,  0,  0,101,  0,  0,  0,101,  0,  0,  0,101,  0,
+    0,  0,102,  0,  0,  0,102,  0,  0,  0,102,  0,  0,  0,102,  0,  0,  0,102,  0,
+    0,  0,102,  0,  0,  0,102,  0,  0,  0,103,  0,  0,  0,103,  0,  0,  0,103,  0,
+    0,  0,103,  0,  0,  0,103,  0,  0,  0,103,  0,  0,  0,102,  0,  0,  0,103,  0,
+    0,  0,105,  0,  0,  0,105,  0,  0,  0,105,  0,  0,  0,105,  0,  0,  0,105,  0,
+    0,  0,105,  0,  0,  0,105,  0,  0,  0,105,  0,  0,  0,105,  0,  0,  0,105,  0,
+    0,  0,105,  0,  0,  0,106,  0,  0,  0, 12,  0,  0,  0,  6,  0,  0,  0,116,105,
+  116,108,101,  0,  0,  0,  0,  0, 39,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,
+    0,  0,  0, 39,  0,  0,  0,  4,  0,  0,  0,107,101,121,  0,  0,  0,  0,  0, 39,
+    0,  0,  0,  5,  0,  0,  0,100, 97,116, 97,  0, 10,  0,  0,  0, 39,  0,  0,  0,
+    9,  0,  0,  0,105,110, 99,111,109,105,110,103,  0, 10,  0,  0,  0, 39,  0,  0,
+    0,  2,  0,  0,  0, 95,  0, 10,  0,  0,  0, 39,  0,  0,  0,  2,  0,  0,  0,115,
+    0, 13,  0,  0,  0, 39,  0,  0,  0, 16,  0,  0,  0, 40,102,111,114, 32,103,101,
+  110,101,114, 97,116,111,114, 41,  0, 19,  0,  0,  0, 28,  0,  0,  0, 12,  0,  0,
+    0, 40,102,111,114, 32,115,116, 97,116,101, 41,  0, 19,  0,  0,  0, 28,  0,  0,
+    0, 14,  0,  0,  0, 40,102,111,114, 32, 99,111,110,116,114,111,108, 41,  0, 19,
+    0,  0,  0, 28,  0,  0,  0,  2,  0,  0,  0, 95,  0, 20,  0,  0,  0, 26,  0,  0,
+    0,  2,  0,  0,  0,118,  0, 20,  0,  0,  0, 26,  0,  0,  0,  2,  0,  0,  0,  7,
+    0,  0,  0,116, 97, 98,108,101,115,  0,  3,  0,  0,  0, 87, 82,  0,  0,  0,  0,
+    0,121,  0,  0,  0,158,  0,  0,  0,  1,  2,  3, 16, 56,  0,  0,  0,196,  0,  0,
+    0,  0,  1,  0,  0,220,  0,  1,  1,133,  1,  0,  0,193, 65,  0,  0, 37,  2,  0,
+    0,156,129,  0,  0, 23,128, 64,  3, 22, 64,  0,128,194,  1,128,  0,222,  1,  0,
+    1,198, 65,  0,  2, 23,192,192,  3, 22, 64,  0,128,202,  1,  0,  0,  9,193,129,
+    0,198, 65,128,  1,218,  1,  0,  0, 22,  0,  1,128,198, 65,  0,  2,212,  1,128,
+    3,204,193,  1,130,218, 65,  0,  0, 22,  0,  0,128,193,129,  0,  0,  6, 66,128,
+    2, 26,  2,  0,  0, 22,  0,  1,128, 76,130,129,  3, 24, 64,  2,  4, 22, 64,  0,
+  128, 66,  2,  0,  0, 94,  2,  0,  1, 65,  2,  1,  0,128,  2,  0,  3,193,  2,  1,
+    0, 96,130,  3,128, 69,  3,  0,  0,128,  3,  0,  6,229,  3,  0,  0, 92,131,  0,
+    0, 23,192,192,  6, 22,  0,  0,128, 69, 67,  1,  0, 23,128,192,  3, 22,128,  0,
+  128,201, 64,131,  0,193,  1,  1,  0, 22,128,  0,128,134, 67,  0,  2,137, 67,131,
+    3,204,  1,193,  3, 95,194,251,127, 66,  2,128,  0, 94,  2,  0,  1, 30,  0,128,
+    0,  6,  0,  0,  0,  4,  7,  0,  0,  0,115,101,108,101, 99,116,  0,  4,  2,  0,
+    0,  0, 35,  0,  3,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  0,  0,  0,  0,  0,
+    0,240, 63,  4, 13,  0,  0,  0,110,105,108, 95,115,101,110,116,105,110,101,108,
+    0,  0,  0,  0,  0, 56,  0,  0,  0,123,  0,  0,  0,123,  0,  0,  0,123,  0,  0,
+    0,125,  0,  0,  0,125,  0,  0,  0,125,  0,  0,  0,125,  0,  0,  0,126,  0,  0,
+    0,126,  0,  0,  0,126,  0,  0,  0,126,  0,  0,  0,130,  0,  0,  0,130,  0,  0,
+    0,130,  0,  0,  0,131,  0,  0,  0,131,  0,  0,  0,134,  0,  0,  0,134,  0,  0,
+    0,134,  0,  0,  0,134,  0,  0,  0,134,  0,  0,  0,134,  0,  0,  0,134,  0,  0,
+    0,134,  0,  0,  0,134,  0,  0,  0,135,  0,  0,  0,137,  0,  0,  0,137,  0,  0,
+    0,137,  0,  0,  0,137,  0,  0,  0,137,  0,  0,  0,138,  0,  0,  0,138,  0,  0,
+    0,141,  0,  0,  0,141,  0,  0,  0,141,  0,  0,  0,141,  0,  0,  0,142,  0,  0,
+    0,142,  0,  0,  0,142,  0,  0,  0,142,  0,  0,  0,145,  0,  0,  0,145,  0,  0,
+    0,146,  0,  0,  0,149,  0,  0,  0,149,  0,  0,  0,150,  0,  0,  0,151,  0,  0,
+    0,151,  0,  0,  0,153,  0,  0,  0,153,  0,  0,  0,154,  0,  0,  0,141,  0,  0,
+    0,157,  0,  0,  0,157,  0,  0,  0,158,  0,  0,  0, 14,  0,  0,  0,  3,  0,  0,
+    0,117,100,  0,  0,  0,  0,  0, 55,  0,  0,  0,  4,  0,  0,  0,107,101,121,  0,
+    0,  0,  0,  0, 55,  0,  0,  0,  4,  0,  0,  0, 97,114,103,  0,  0,  0,  0,  0,
+   55,  0,  0,  0,  5,  0,  0,  0,100, 97,116, 97,  0,  3,  0,  0,  0, 55,  0,  0,
+    0,  9,  0,  0,  0,105,110, 99,111,109,105,110,103,  0,  3,  0,  0,  0, 55,  0,
+    0,  0,  7,  0,  0,  0,108,105,109,105,116,115,  0,  3,  0,  0,  0, 55,  0,  0,
+    0,  2,  0,  0,  0,110,  0,  7,  0,  0,  0, 55,  0,  0,  0,  4,  0,  0,  0,108,
+  101,110,  0, 25,  0,  0,  0, 55,  0,  0,  0,  2,  0,  0,  0,109,  0, 26,  0,  0,
+    0, 55,  0,  0,  0, 12,  0,  0,  0, 40,102,111,114, 32,105,110,100,101,120, 41,
+    0, 36,  0,  0,  0, 53,  0,  0,  0, 12,  0,  0,  0, 40,102,111,114, 32,108,105,
+  109,105,116, 41,  0, 36,  0,  0,  0, 53,  0,  0,  0, 11,  0,  0,  0, 40,102,111,
+  114, 32,115,116,101,112, 41,  0, 36,  0,  0,  0, 53,  0,  0,  0,  2,  0,  0,  0,
+  105,  0, 37,  0,  0,  0, 52,  0,  0,  0,  4,  0,  0,  0,118, 97,108,  0, 41,  0,
+    0,  0, 52,  0,  0,  0,  1,  0,  0,  0,  7,  0,  0,  0,116, 97, 98,108,101,115,
+    0,  0,  0,  0,  0,167,  0,  0,  0,189,  0,  0,  0,  2,  1,  3, 14, 40,  0,  0,
+    0,132,  0,  0,  0,192,  0,  0,  0,156,  0,  1,  1, 65,  1,  0,  0,133, 65,  0,
+    0,193,129,  0,  0, 37,  2,  0,  0,156,129,  0,  0,193,  1,  0,  0, 96,193,  6,
+  128, 69, 66,  0,  0,128,  2,  0,  4,229,  2,  0,  0, 92,130,  0,  0,134, 66,  2,
+    1, 87,192, 64,  5, 22,  0,  5,128,198, 66,130,  1,218,  2,  0,  0, 22, 64,  2,
+  128,198, 66,130,  1,198,  2,192,  5, 87,192,192,  5, 22, 64,  1,128,196,  2,128,
+    0,  6, 67,130,  1, 65,  3,  0,  0,220,130,128,  1,137,192,130,  4, 22,  0,  0,
+  128,137,192,192,  4,197,  2,  1,  0, 23,192,  2,  5, 22,  0,  0,128,131,  2,  0,
+    5,192,  2,  0,  5,  0,  3,128,  4,222,  2,128,  1, 95,129,248,127, 30,  0,128,
+    0,  5,  0,  0,  0,  3,  0,  0,  0,  0,  0,  0,240, 63,  4,  7,  0,  0,  0,115,
+  101,108,101, 99,116,  0,  4,  2,  0,  0,  0, 35,  0,  0,  4, 13,  0,  0,  0,110,
+  105,108, 95,115,101,110,116,105,110,101,108,  0,  0,  0,  0,  0, 40,  0,  0,  0,
+  169,  0,  0,  0,169,  0,  0,  0,169,  0,  0,  0,171,  0,  0,  0,171,  0,  0,  0,
+  171,  0,  0,  0,171,  0,  0,  0,171,  0,  0,  0,171,  0,  0,  0,171,  0,  0,  0,
+  172,  0,  0,  0,172,  0,  0,  0,172,  0,  0,  0,172,  0,  0,  0,173,  0,  0,  0,
+  175,  0,  0,  0,175,  0,  0,  0,176,  0,  0,  0,176,  0,  0,  0,176,  0,  0,  0,
+  176,  0,  0,  0,176,  0,  0,  0,176,  0,  0,  0,176,  0,  0,  0,178,  0,  0,  0,
+  178,  0,  0,  0,178,  0,  0,  0,178,  0,  0,  0,178,  0,  0,  0,178,  0,  0,  0,
+  180,  0,  0,  0,182,  0,  0,  0,182,  0,  0,  0,182,  0,  0,  0,183,  0,  0,  0,
+  185,  0,  0,  0,185,  0,  0,  0,185,  0,  0,  0,171,  0,  0,  0,189,  0,  0,  0,
+   11,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,  0,  0,  0, 39,  0,  0,  0,  4,
+    0,  0,  0, 97,114,103,  0,  0,  0,  0,  0, 39,  0,  0,  0,  5,  0,  0,  0,100,
+   97,116, 97,  0,  3,  0,  0,  0, 39,  0,  0,  0,  9,  0,  0,  0,105,110, 99,111,
+  109,105,110,103,  0,  3,  0,  0,  0, 39,  0,  0,  0,  2,  0,  0,  0, 95,  0,  3,
+    0,  0,  0, 39,  0,  0,  0, 12,  0,  0,  0, 40,102,111,114, 32,105,110,100,101,
+  120, 41,  0,  9,  0,  0,  0, 39,  0,  0,  0, 12,  0,  0,  0, 40,102,111,114, 32,
+  108,105,109,105,116, 41,  0,  9,  0,  0,  0, 39,  0,  0,  0, 11,  0,  0,  0, 40,
+  102,111,114, 32,115,116,101,112, 41,  0,  9,  0,  0,  0, 39,  0,  0,  0,  2,  0,
+    0,  0,105,  0, 10,  0,  0,  0, 38,  0,  0,  0,  4,  0,  0,  0,107,101,121,  0,
+   14,  0,  0,  0, 38,  0,  0,  0,  4,  0,  0,  0,118, 97,108,  0, 15,  0,  0,  0,
+   38,  0,  0,  0,  2,  0,  0,  0,  7,  0,  0,  0,116, 97, 98,108,101,115,  0, 13,
+    0,  0,  0,116, 97, 98,108,101, 95,114,101,109,111,118,101,  0,  0,  0,  0,  0,
+  195,  0,  0,  0,200,  0,  0,  0,  1,  3,  0,  6,  5,  0,  0,  0,196,  0,  0,  0,
+    0,  1,  0,  0,220,  0,  1,  1, 73,129,128,  0, 30,  0,128,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  5,  0,  0,  0,197,  0,  0,  0,197,  0,  0,  0,197,  0,  0,  0,
+  199,  0,  0,  0,200,  0,  0,  0,  6,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,
+    0,  0,  0,  4,  0,  0,  0,  4,  0,  0,  0,107,101,121,  0,  0,  0,  0,  0,  4,
+    0,  0,  0,  2,  0,  0,  0,110,  0,  0,  0,  0,  0,  4,  0,  0,  0,  2,  0,  0,
+    0, 95,  0,  3,  0,  0,  0,  4,  0,  0,  0,  2,  0,  0,  0, 95,  0,  3,  0,  0,
+    0,  4,  0,  0,  0,  7,  0,  0,  0,108,105,109,105,116,115,  0,  3,  0,  0,  0,
+    4,  0,  0,  0,  1,  0,  0,  0,  7,  0,  0,  0,116, 97, 98,108,101,115,  0,  0,
+    0,  0,  0,206,  0,  0,  0,214,  0,  0,  0,  1,  3,  0,  6,  6,  0,  0,  0,196,
+    0,  0,  0,  0,  1,  0,  0,220,  0,  1,  1,201,128,128,  0,  9,  1,192,  0, 30,
+    0,128,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  6,  0,  0,  0,208,  0,  0,  0,
+  208,  0,  0,  0,208,  0,  0,  0,212,  0,  0,  0,213,  0,  0,  0,214,  0,  0,  0,
+    6,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,  0,  0,  0,  5,  0,  0,  0,  4,
+    0,  0,  0,107,101,121,  0,  0,  0,  0,  0,  5,  0,  0,  0,  4,  0,  0,  0,118,
+   97,108,  0,  0,  0,  0,  0,  5,  0,  0,  0,  5,  0,  0,  0,100, 97,116, 97,  0,
+    3,  0,  0,  0,  5,  0,  0,  0,  9,  0,  0,  0,105,110, 99,111,109,105,110,103,
+    0,  3,  0,  0,  0,  5,  0,  0,  0,  2,  0,  0,  0, 95,  0,  3,  0,  0,  0,  5,
+    0,  0,  0,  1,  0,  0,  0,  7,  0,  0,  0,116, 97, 98,108,101,115,  0,  0,  0,
+    0,  0,220,  0,  0,  0,229,  0,  0,  0,  1,  2,  0,  7, 10,  0,  0,  0,132,  0,
+    0,  0,192,  0,  0,  0,156,  0,  1,  1, 70, 65,  0,  1,133,  1,  0,  0, 23,128,
+  129,  2, 22,  0,  0,128, 67,  1,128,  2, 94,  1,  0,  1, 30,  0,128,  0,  1,  0,
+    0,  0,  4, 13,  0,  0,  0,110,105,108, 95,115,101,110,116,105,110,101,108,  0,
+    0,  0,  0,  0, 10,  0,  0,  0,222,  0,  0,  0,222,  0,  0,  0,222,  0,  0,  0,
+  224,  0,  0,  0,225,  0,  0,  0,225,  0,  0,  0,225,  0,  0,  0,226,  0,  0,  0,
+  228,  0,  0,  0,229,  0,  0,  0,  6,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,
+    0,  0,  0,  9,  0,  0,  0,  4,  0,  0,  0,107,101,121,  0,  0,  0,  0,  0,  9,
+    0,  0,  0,  5,  0,  0,  0,100, 97,116, 97,  0,  3,  0,  0,  0,  9,  0,  0,  0,
+    2,  0,  0,  0, 95,  0,  3,  0,  0,  0,  9,  0,  0,  0,  2,  0,  0,  0, 95,  0,
+    3,  0,  0,  0,  9,  0,  0,  0,  4,  0,  0,  0,118, 97,108,  0,  4,  0,  0,  0,
+    9,  0,  0,  0,  1,  0,  0,  0,  7,  0,  0,  0,116, 97, 98,108,101,115,  0,  0,
+    0,  0,  0,237,  0,  0,  0,242,  0,  0,  0,  3,  1,  0,  2,  7,  0,  0,  0, 68,
+    0,  0,  0, 73,  0, 64,  0, 68,  0,128,  0, 73,  0, 64,  0, 68,  0,  0,  1, 73,
+    0, 64,  0, 30,  0,128,  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  7,  0,  0,  0,
+  239,  0,  0,  0,239,  0,  0,  0,240,  0,  0,  0,240,  0,  0,  0,241,  0,  0,  0,
+  241,  0,  0,  0,242,  0,  0,  0,  1,  0,  0,  0,  3,  0,  0,  0,117,100,  0,  0,
+    0,  0,  0,  6,  0,  0,  0,  3,  0,  0,  0,  6,  0,  0,  0, 95,100, 97,116, 97,
+    0, 10,  0,  0,  0, 95,105,110, 99,111,109,105,110,103,  0,  8,  0,  0,  0, 95,
+  108,105,109,105,116,115,  0, 45,  0,  0,  0, 39,  0,  0,  0, 39,  0,  0,  0, 39,
+    0,  0,  0, 43,  0,  0,  0, 43,  0,  0,  0, 43,  0,  0,  0, 43,  0,  0,  0, 44,
+    0,  0,  0, 44,  0,  0,  0, 44,  0,  0,  0, 44,  0,  0,  0, 50,  0,  0,  0, 50,
+    0,  0,  0, 59,  0,  0,  0, 68,  0,  0,  0, 77,  0,  0,  0, 93,  0,  0,  0, 93,
+    0,  0,  0, 93,  0,  0,  0, 93,  0,  0,  0,106,  0,  0,  0,106,  0,  0,  0,106,
+    0,  0,  0,158,  0,  0,  0,158,  0,  0,  0,121,  0,  0,  0,189,  0,  0,  0,189,
+    0,  0,  0,189,  0,  0,  0,167,  0,  0,  0,200,  0,  0,  0,200,  0,  0,  0,195,
+    0,  0,  0,214,  0,  0,  0,214,  0,  0,  0,206,  0,  0,  0,229,  0,  0,  0,229,
+    0,  0,  0,220,  0,  0,  0,242,  0,  0,  0,242,  0,  0,  0,242,  0,  0,  0,242,
+    0,  0,  0,237,  0,  0,  0,242,  0,  0,  0,  8,  0,  0,  0, 13,  0,  0,  0,116,
+   97, 98,108,101, 95,114,101,109,111,118,101,  0,  7,  0,  0,  0, 44,  0,  0,  0,
+   13,  0,  0,  0,116, 97, 98,108,101, 95, 99,111,110, 99, 97,116,  0, 11,  0,  0,
+    0, 44,  0,  0,  0,  3,  0,  0,  0, 87, 82,  0, 13,  0,  0,  0, 44,  0,  0,  0,
+    6,  0,  0,  0, 95,100, 97,116, 97,  0, 14,  0,  0,  0, 44,  0,  0,  0, 10,  0,
+    0,  0, 95,105,110, 99,111,109,105,110,103,  0, 15,  0,  0,  0, 44,  0,  0,  0,
+    8,  0,  0,  0, 95,108,105,109,105,116,115,  0, 16,  0,  0,  0, 44,  0,  0,  0,
+    7,  0,  0,  0,116, 97, 98,108,101,115,  0, 20,  0,  0,  0, 44,  0,  0,  0,  6,
+    0,  0,  0, 68, 69, 66, 85, 71,  0, 23,  0,  0,  0, 44,  0,  0,  0,  0,  0,  0,
+    0,};
+
diff --git a/src/lualanes/lanes.c b/src/lualanes/lanes.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b36e4d0c7a650e3d51f265be3068d2085d3c18f
--- /dev/null
+++ b/src/lualanes/lanes.c
@@ -0,0 +1,1849 @@
+/*
+ * LANES.C   	                          Copyright (c) 2007-08, Asko Kauppi
+ *
+ * Multithreading in Lua.
+ * 
+ * History:
+ *      20-Oct-08 (2.0.2): Added closing of free-running threads, but it does
+ *                  not seem to eliminate the occasional segfaults at process
+ *                  exit.
+ *          ...
+ *      24-Jun-08 .. 14-Aug-08 AKa: Major revise, Lanes 2008 version (2.0 rc1)
+ *          ...
+ *      18-Sep-06 AKa: Started the module.
+ *
+ * Platforms (tested internally):
+ *      OS X (10.5.4 PowerPC/Intel)
+ *      Linux x86 (Ubuntu 8.04)
+ *      Win32 (Windows XP Home SP2, Visual C++ 2005/2008 Express)
+ *      PocketPC (TBD)
+ *
+ * Platforms (tested externally):
+ *      Win32 (MSYS) by Ross Berteig.
+ *
+ * Platforms (testers appreciated):
+ *      Win64 - should work???
+ *      Linux x64 - should work
+ *      FreeBSD - should work
+ *      QNX - porting shouldn't be hard
+ *      Sun Solaris - porting shouldn't be hard
+ *
+ * References:
+ *      "Porting multithreaded applications from Win32 to Mac OS X":
+ *      <http://developer.apple.com/macosx/multithreadedprogramming.html>
+ *
+ *      Pthreads:
+ *      <http://vergil.chemistry.gatech.edu/resources/programming/threads.html>
+ *
+ *      MSDN: <http://msdn2.microsoft.com/en-us/library/ms686679.aspx>
+ *
+ *      <http://ridiculousfish.com/blog/archives/2007/02/17/barrier>
+ *
+ * Defines:
+ *      -DLINUX_SCHED_RR: all threads are lifted to SCHED_RR category, to
+ *          allow negative priorities (-2,-1) be used. Even without this,
+ *          using priorities will require 'sudo' privileges on Linux.
+ *
+ *		-DUSE_PTHREAD_TIMEDJOIN: use 'pthread_timedjoin_np()' for waiting
+ *          for threads with a timeout. This changes the thread cleanup
+ *          mechanism slightly (cleans up at the join, not once the thread
+ *          has finished). May or may not be a good idea to use it.
+ *          Available only in selected operating systems (Linux).
+ *
+ * Bugs:
+ *
+ * To-do:
+ *
+ *      ...
+ */
+
+const char *VERSION= "2.0.3";
+
+/*
+===============================================================================
+
+Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+*/
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "threading.h"
+#include "tools.h"
+
+#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
+# include <sys/time.h>
+#endif
+
+/* geteuid() */
+#ifdef PLATFORM_LINUX
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+
+/* The selected number is not optimal; needs to be tested. Even using just
+* one keeper state may be good enough (depends on the number of Lindas used
+* in the applications).
+*/
+#define KEEPER_STATES_N 1   // 6
+
+/* Do you want full call stacks, or just the line where the error happened?
+*
+* TBD: The full stack feature does not seem to work (try 'make error').
+*/
+#define ERROR_FULL_STACK
+
+#ifdef ERROR_FULL_STACK
+# define STACK_TRACE_KEY ((void*)lane_error)     // used as registry key
+#endif
+
+/*
+* Lua code for the keeper states (baked in)
+*/
+static char keeper_chunk[]= 
+#include "keeper.lch"
+
+struct s_lane;
+static bool_t cancel_test( lua_State *L );
+static void cancel_error( lua_State *L );
+
+#define CANCEL_TEST_KEY ((void*)cancel_test)    // used as registry key
+#define CANCEL_ERROR ((void*)cancel_error)      // 'cancel_error' sentinel
+
+/*
+* registry[FINALIZER_REG_KEY] is either nil (no finalizers) or a table
+* of functions that Lanes will call after the executing 'pcall' has ended.
+*
+* We're NOT using the GC system for finalizer mainly because providing the
+* error (and maybe stack trace) parameters to the finalizer functions would
+* anyways complicate that approach.
+*/
+#define FINALIZER_REG_KEY ((void*)LG_set_finalizer)
+
+struct s_Linda;
+
+#if 1
+# define DEBUG_SIGNAL( msg, signal_ref ) /* */
+#else
+# define DEBUG_SIGNAL( msg, signal_ref ) \
+    { int i; unsigned char *ptr; char buf[999]; \
+      sprintf( buf, ">>> " msg ": %p\t", (signal_ref) ); \
+      ptr= (unsigned char *)signal_ref; \
+      for( i=0; i<sizeof(*signal_ref); i++ ) { \
+        sprintf( strchr(buf,'\0'), "%02x %c ", ptr[i], ptr[i] ); \
+      } \
+      fprintf( stderr, "%s\n", buf ); \
+    }
+#endif
+
+static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force );
+
+
+/*
+* Push a table stored in registry onto Lua stack.
+*
+* If there is no existing table, create one if 'create' is TRUE.
+* 
+* Returns: TRUE if a table was pushed
+*          FALSE if no table found, not created, and nothing pushed
+*/
+static bool_t push_registry_table( lua_State *L, void *key, bool_t create ) {
+
+    STACK_GROW(L,3);
+    
+    lua_pushlightuserdata( L, key );
+    lua_gettable( L, LUA_REGISTRYINDEX );
+    
+    if (lua_isnil(L,-1)) {
+        lua_pop(L,1);
+
+        if (!create) return FALSE;  // nothing pushed
+
+        lua_newtable(L);
+        lua_pushlightuserdata( L, key );
+        lua_pushvalue(L,-2);    // duplicate of the table
+        lua_settable( L, LUA_REGISTRYINDEX );
+        
+        // [-1]: table that's also bound in registry
+    }
+    return TRUE;    // table pushed
+}
+
+
+/*---=== Serialize require ===---
+*/
+
+static MUTEX_T require_cs;
+
+//---
+// [val]= new_require( ... )
+//
+// Call 'old_require' but only one lane at a time.
+//
+// Upvalues: [1]: original 'require' function
+//
+static int new_require( lua_State *L ) {
+    int rc;
+    int args= lua_gettop(L);
+
+  STACK_GROW(L,1);
+  STACK_CHECK(L)
+    
+    // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
+    // leave us locked, blocking any future 'require' calls from other lanes.
+    //
+    MUTEX_LOCK( &require_cs );
+    {
+        lua_pushvalue( L, lua_upvalueindex(1) );
+        lua_insert( L, 1 );
+
+        rc= lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
+            //
+            // LUA_ERRRUN / LUA_ERRMEM
+    }
+    MUTEX_UNLOCK( &require_cs );
+
+    if (rc) lua_error(L);   // error message already at [-1]
+
+  STACK_END(L,0)
+    return 1;
+}
+
+/*
+* Serialize calls to 'require', if it exists
+*/
+static 
+void serialize_require( lua_State *L ) {
+
+  STACK_GROW(L,1);  
+  STACK_CHECK(L)
+    
+    // Check 'require' is there; if not, do nothing
+    //
+    lua_getglobal( L, "require" );
+    if (lua_isfunction( L, -1 )) {
+        // [-1]: original 'require' function
+
+        lua_pushcclosure( L, new_require, 1 /*upvalues*/ );
+        lua_setglobal( L, "require" );
+
+    } else {
+        // [-1]: nil
+        lua_pop(L,1);
+    }
+
+  STACK_END(L,0)
+}
+
+
+/*---=== Keeper states ===---
+*/
+
+/*
+* Pool of keeper states
+*
+* Access to keeper states is locked (only one OS thread at a time) so the 
+* bigger the pool, the less chances of unnecessary waits. Lindas map to the
+* keepers randomly, by a hash.
+*/
+struct s_Keeper {
+    MUTEX_T lock_;
+    lua_State *L;
+} keeper[ KEEPER_STATES_N ];
+
+/* We could use an empty table in 'keeper.lua' as the sentinel, but maybe
+* checking for a lightuserdata is faster.
+*/
+static bool_t nil_sentinel;
+
+/*
+* Initialize keeper states
+*
+* If there is a problem, return an error message (NULL for okay).
+*
+* Note: Any problems would be design flaws; the created Lua state is left
+*       unclosed, because it does not really matter. In production code, this
+*       function never fails.
+*/
+static const char *init_keepers(void) {
+    unsigned int i;
+    for( i=0; i<KEEPER_STATES_N; i++ ) {
+        
+        // Initialize Keeper states with bare minimum of libs (those required
+        // by 'keeper.lua')
+        //
+        lua_State *L= luaL_newstate();
+        if (!L) return "out of memory";
+
+        luaG_openlibs( L, "io,table" );     // 'io' for debugging messages
+
+        lua_pushlightuserdata( L, &nil_sentinel );
+        lua_setglobal( L, "nil_sentinel" );
+
+        // Read in the preloaded chunk (and run it)
+        //
+        if (luaL_loadbuffer( L, keeper_chunk, sizeof(keeper_chunk), "=lanes_keeper" ))
+            return "luaL_loadbuffer() failed";   // LUA_ERRMEM
+
+        if (lua_pcall( L, 0 /*args*/, 0 /*results*/, 0 /*errfunc*/ )) {
+            // LUA_ERRRUN / LUA_ERRMEM / LUA_ERRERR
+            //
+            const char *err= lua_tostring(L,-1);
+            assert(err);
+            return err;
+        }
+
+        MUTEX_INIT( &keeper[i].lock_ );
+        keeper[i].L= L;
+    }
+    return NULL;    // ok
+}
+
+static 
+struct s_Keeper *keeper_acquire( const void *ptr ) {
+    /*
+    * Any hashing will do that maps pointers to 0..KEEPER_STATES_N-1 
+    * consistently.
+    *
+    * Pointers are often aligned by 8 or so - ignore the low order bits
+    */
+    unsigned int i= ((unsigned long)(ptr) >> 3) % KEEPER_STATES_N;
+    struct s_Keeper *K= &keeper[i];
+
+    MUTEX_LOCK( &K->lock_ );
+    return K;
+}
+
+static 
+void keeper_release( struct s_Keeper *K ) {
+    MUTEX_UNLOCK( &K->lock_ );
+}
+
+/*
+* Call a function ('func_name') in the keeper state, and pass on the returned
+* values to 'L'.
+*
+* 'linda':          deep Linda pointer (used only as a unique table key, first parameter)
+* 'starting_index': first of the rest of parameters (none if 0)
+*
+* Returns:  number of return values (pushed to 'L')
+*/
+static
+int keeper_call( lua_State* K, const char *func_name, 
+                  lua_State *L, struct s_Linda *linda, uint_t starting_index ) {
+
+    int args= starting_index ? (lua_gettop(L) - starting_index +1) : 0;
+    int Ktos= lua_gettop(K);
+    int retvals;
+
+    lua_getglobal( K, func_name );
+    ASSERT_L( lua_isfunction(K,-1) );
+
+    STACK_GROW( K, 1 );
+    lua_pushlightuserdata( K, linda );
+
+    luaG_inter_copy( L,K, args );   // L->K
+    lua_call( K, 1+args, LUA_MULTRET );
+
+    retvals= lua_gettop(K) - Ktos;
+
+    luaG_inter_move( K,L, retvals );    // K->L
+    return retvals;
+}
+
+
+/*---=== Linda ===---
+*/
+
+/*
+* Actual data is kept within a keeper state, which is hashed by the 's_Linda'
+* pointer (which is same to all userdatas pointing to it).
+*/
+struct s_Linda {
+    SIGNAL_T read_happened;
+    SIGNAL_T write_happened;
+};
+
+static int LG_linda_id( lua_State* );
+
+#define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, LG_linda_id, n ))
+
+
+/*
+* bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... )
+*
+* Send one or more values to a Linda. If there is a limit, all values must fit.
+*
+* Returns:  'true' if the value was queued
+*           'false' for timeout (only happens when the queue size is limited)
+*/
+LUAG_FUNC( linda_send ) {
+    struct s_Linda *linda= lua_toLinda( L, 1 );
+    bool_t ret;
+    bool_t cancel= FALSE;
+    struct s_Keeper *K;
+    time_d timeout= -1.0;
+    uint_t key_i= 2;    // index of first key, if timeout not there
+
+    if (lua_isnumber(L,2)) {
+        timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
+        key_i++;
+    } else if (lua_isnil(L,2))
+        key_i++;
+
+    if (lua_isnil(L,key_i))
+        luaL_error( L, "nil key" );
+
+    STACK_GROW(L,1);
+
+    K= keeper_acquire( linda );
+    {
+        lua_State *KL= K->L;    // need to do this for 'STACK_CHECK'
+STACK_CHECK(KL)
+        while(TRUE) {
+            int pushed;
+        
+STACK_MID(KL,0)
+            pushed= keeper_call( K->L, "send", L, linda, key_i );
+            ASSERT_L( pushed==1 );
+        
+            ret= lua_toboolean(L,-1);
+            lua_pop(L,1);
+        
+            if (ret) {
+                // Wake up ALL waiting threads
+                //
+                SIGNAL_ALL( &linda->write_happened );
+                break;
+
+            } else if (timeout==0.0) {
+                break;  /* no wait; instant timeout */
+
+            } else {
+                /* limit faced; push until timeout */
+                    
+                cancel= cancel_test( L );   // testing here causes no delays
+                if (cancel) break;
+
+                // K lock will be released for the duration of wait and re-acquired
+                //
+                if (!SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout ))
+                    break;  // timeout
+            }
+        }
+STACK_END(KL,0)
+    }
+    keeper_release(K);
+
+    if (cancel)
+        cancel_error(L);
+    
+    lua_pushboolean( L, ret );
+    return 1;
+}
+
+
+/*
+* [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] )
+*
+* Receive a value from Linda, consuming it.
+*
+* Returns:  value received (which is consumed from the slot)
+*           key which had it
+*/
+LUAG_FUNC( linda_receive ) {
+    struct s_Linda *linda= lua_toLinda( L, 1 );
+    int pushed;
+    bool_t cancel= FALSE;
+    struct s_Keeper *K;
+    time_d timeout= -1.0;
+    uint_t key_i= 2;
+
+    if (lua_isnumber(L,2)) {
+        timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) );
+        key_i++;
+    } else if (lua_isnil(L,2))
+        key_i++;
+
+    K= keeper_acquire( linda );
+    {
+        while(TRUE) {
+            pushed= keeper_call( K->L, "receive", L, linda, key_i );
+            if (pushed) {
+                ASSERT_L( pushed==2 );
+
+                // To be done from within the 'K' locking area
+                //
+                SIGNAL_ALL( &linda->read_happened );
+                break;
+
+            } else if (timeout==0.0) {
+                break;  /* instant timeout */
+
+            } else {    /* nothing received; wait until timeout */
+    
+                cancel= cancel_test( L );   // testing here causes no delays
+                if (cancel) break;
+
+                // Release the K lock for the duration of wait, and re-acquire
+                //
+                if (!SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout ))
+                    break;
+            }
+        }
+    }
+    keeper_release(K);
+
+    if (cancel)
+        cancel_error(L);
+
+    return pushed;
+}
+
+
+/*
+* = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] )
+*
+* Set a value to Linda.
+*
+* Existing slot value is replaced, and possible queue entries removed.
+*/
+LUAG_FUNC( linda_set ) {
+    struct s_Linda *linda= lua_toLinda( L, 1 );
+    bool_t has_value= !lua_isnil(L,3);
+
+    struct s_Keeper *K= keeper_acquire( linda );
+    {
+        int pushed= keeper_call( K->L, "set", L, linda, 2 );
+        ASSERT_L( pushed==0 );
+
+        /* Set the signal from within 'K' locking.
+        */
+        if (has_value) {
+            SIGNAL_ALL( &linda->write_happened );
+        }
+    }
+    keeper_release(K);
+
+    return 0;
+}
+
+
+/*
+* [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata )
+*
+* Get a value from Linda.
+*/
+LUAG_FUNC( linda_get ) {
+    struct s_Linda *linda= lua_toLinda( L, 1 );
+    int pushed;
+
+    struct s_Keeper *K= keeper_acquire( linda );
+    {
+        pushed= keeper_call( K->L, "get", L, linda, 2 );
+        ASSERT_L( pushed==0 || pushed==1 );
+    }
+    keeper_release(K);
+
+    return pushed;
+}
+
+
+/*
+* = linda_limit( linda_ud, key_num|str|bool|lightuserdata, uint [, ...] )
+*
+* Set limits to 1 or more Linda keys.
+*/
+LUAG_FUNC( linda_limit ) {
+    struct s_Linda *linda= lua_toLinda( L, 1 );
+
+    struct s_Keeper *K= keeper_acquire( linda );
+    {
+        int pushed= keeper_call( K->L, "limit", L, linda, 2 );
+        ASSERT_L( pushed==0 );
+    }
+    keeper_release(K);
+
+    return 0;
+}
+
+
+/*
+* lightuserdata= linda_deep( linda_ud )
+*
+* Return the 'deep' userdata pointer, identifying the Linda.
+*
+* This is needed for using Lindas as key indices (timer system needs it);
+* separately created proxies of the same underlying deep object will have
+* different userdata and won't be known to be essentially the same deep one
+* without this.
+*/
+LUAG_FUNC( linda_deep ) {
+    struct s_Linda *linda= lua_toLinda( L, 1 );
+    lua_pushlightuserdata( L, linda );      // just the address
+    return 1;
+}
+
+
+/*
+* Identity function of a shared userdata object.
+* 
+*   lightuserdata= linda_id( "new" [, ...] )
+*   = linda_id( "delete", lightuserdata )
+*
+* Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into
+* regular userdata proxies, per each state using the deep data.
+*
+*   tbl= linda_id( "metatable" )
+*
+* Returns a metatable for the proxy objects ('__gc' method not needed; will
+* be added by 'luaG_...')
+*
+*   = linda_id( str, ... )
+*
+* For any other strings, the ID function must not react at all. This allows
+* future extensions of the system. 
+*/
+LUAG_FUNC( linda_id ) {
+    const char *which= lua_tostring(L,1);
+
+    if (strcmp( which, "new" )==0) {
+        struct s_Linda *s;
+
+        /* We don't use any parameters, but one could (they're at [2..TOS])
+        */
+        ASSERT_L( lua_gettop(L)==1 );
+
+        /* The deep data is allocated separately of Lua stack; we might no
+        * longer be around when last reference to it is being released.
+        * One can use any memory allocation scheme.
+        */
+        s= (struct s_Linda *) malloc( sizeof(struct s_Linda) );
+        ASSERT_L(s);
+
+        SIGNAL_INIT( &s->read_happened );
+        SIGNAL_INIT( &s->write_happened );
+
+        lua_pushlightuserdata( L, s );
+        return 1;
+
+    } else if (strcmp( which, "delete" )==0) {
+        struct s_Keeper *K;
+        struct s_Linda *s= lua_touserdata(L,2);
+        ASSERT_L(s);
+
+        /* Clean associated structures in the keeper state.
+        */
+        K= keeper_acquire(s);
+        {
+            keeper_call( K->L, "clear", L, s, 0 );
+        }
+        keeper_release(K);
+
+        /* There aren't any lanes waiting on these lindas, since all proxies
+        * have been gc'ed. Right?
+        */
+        SIGNAL_FREE( &s->read_happened );
+        SIGNAL_FREE( &s->write_happened );
+        free(s);
+
+        return 0;
+
+    } else if (strcmp( which, "metatable" )==0) {
+
+      STACK_CHECK(L)
+        lua_newtable(L);
+        lua_newtable(L);
+            //
+            // [-2]: linda metatable
+            // [-1]: metatable's to-be .__index table
+    
+        lua_pushcfunction( L, LG_linda_send );
+        lua_setfield( L, -2, "send" );
+    
+        lua_pushcfunction( L, LG_linda_receive );
+        lua_setfield( L, -2, "receive" );
+    
+        lua_pushcfunction( L, LG_linda_limit );
+        lua_setfield( L, -2, "limit" );
+
+        lua_pushcfunction( L, LG_linda_set );
+        lua_setfield( L, -2, "set" );
+    
+        lua_pushcfunction( L, LG_linda_get );
+        lua_setfield( L, -2, "get" );
+
+        lua_pushcfunction( L, LG_linda_deep );
+        lua_setfield( L, -2, "deep" );
+
+        lua_setfield( L, -2, "__index" );
+      STACK_END(L,1)
+    
+        return 1;
+    }
+    
+    return 0;   // unknown request, be quiet
+}
+
+
+/*---=== Finalizer ===---
+*/
+
+//---
+// void= finalizer( finalizer_func )
+//
+// finalizer_func( [err, stack_tbl] )
+//
+// Add a function that will be called when exiting the lane, either via
+// normal return or an error.
+//
+LUAG_FUNC( set_finalizer )
+{
+    STACK_GROW(L,3);
+    
+    // Get the current finalizer table (if any)
+    //
+    push_registry_table( L, FINALIZER_REG_KEY, TRUE /*do create if none*/ );
+
+    lua_pushinteger( L, lua_objlen(L,-1)+1 );
+    lua_pushvalue( L, 1 );  // copy of the function
+    lua_settable( L, -3 );
+    
+    lua_pop(L,1);
+    return 0;
+}
+
+
+//---
+// Run finalizers - if any - with the given parameters
+//
+// If 'rc' is nonzero, error message and stack index are available as:
+//      [-1]: stack trace (table)
+//      [-2]: error message (any type)
+//
+// Returns:
+//      0 if finalizers were run without error (or there were none)
+//      LUA_ERRxxx return code if any of the finalizers failed
+//
+// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
+//
+static int run_finalizers( lua_State *L, int lua_rc )
+{
+    unsigned error_index, tbl_index;
+    unsigned n;
+    int rc= 0;
+    
+    if (!push_registry_table(L, FINALIZER_REG_KEY, FALSE /*don't create one*/))
+        return 0;   // no finalizers
+
+    tbl_index= lua_gettop(L);
+    error_index= (lua_rc!=0) ? tbl_index-1 : 0;   // absolute indices
+
+    STACK_GROW(L,4);
+
+    // [-1]: { func [, ...] }
+    //
+    for( n= lua_objlen(L,-1); n>0; n-- ) {
+        unsigned args= 0;
+        lua_pushinteger( L,n );
+        lua_gettable( L, -2 );
+        
+        // [-1]: function
+        // [-2]: finalizers table
+
+        if (error_index) {
+            lua_pushvalue( L, error_index );
+            lua_pushvalue( L, error_index+1 );  // stack trace
+            args= 2;
+        }
+
+        rc= lua_pcall( L, args, 0 /*retvals*/, 0 /*no errfunc*/ );
+            //
+            // LUA_ERRRUN / LUA_ERRMEM
+    
+        if (rc!=0) {
+            // [-1]: error message
+            //
+            // If one finalizer fails, don't run the others. Return this
+            // as the 'real' error, preceding that we could have had (or not)
+            // from the actual code.
+            //
+            break;
+        }
+    }
+    
+    lua_remove(L,tbl_index);   // take finalizer table out of stack
+
+    return rc;
+}
+
+
+/*---=== Threads ===---
+*/
+
+// NOTE: values to be changed by either thread, during execution, without
+//       locking, are marked "volatile"
+//
+struct s_lane {
+    THREAD_T thread;
+        //
+        // M: sub-thread OS thread
+        // S: not used
+
+    lua_State *L;
+        //
+        // M: prepares the state, and reads results
+        // S: while S is running, M must keep out of modifying the state
+
+    volatile enum e_status status;
+        // 
+        // M: sets to PENDING (before launching)
+        // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED
+    
+    volatile bool_t cancel_request;
+        //
+        // M: sets to FALSE, flags TRUE for cancel request
+        // S: reads to see if cancel is requested
+
+#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) )
+    SIGNAL_T done_signal_;
+        //
+        // M: Waited upon at lane ending  (if Posix with no PTHREAD_TIMEDJOIN)
+        // S: sets the signal once cancellation is noticed (avoids a kill)
+
+    MUTEX_T done_lock_;
+        // 
+        // Lock required by 'done_signal' condition variable, protecting
+        // lane status changes to DONE/ERROR_ST/CANCELLED.
+#endif
+
+    volatile enum { 
+        NORMAL,         // normal master side state
+        KILLED          // issued an OS kill
+    } mstatus;
+        //
+        // M: sets to NORMAL, if issued a kill changes to KILLED
+        // S: not used
+        
+    struct s_lane * volatile selfdestruct_next;
+        //
+        // M: sets to non-NULL if facing lane handle '__gc' cycle but the lane
+        //    is still running
+        // S: cleans up after itself if non-NULL at lane exit
+};
+
+static MUTEX_T selfdestruct_cs;
+    //
+    // Protects modifying the selfdestruct chain
+
+#define SELFDESTRUCT_END ((struct s_lane *)(-1))
+    //
+    // The chain is ended by '(struct s_lane*)(-1)', not NULL:
+    //      'selfdestruct_first -> ... -> ... -> (-1)'
+
+struct s_lane * volatile selfdestruct_first= SELFDESTRUCT_END;
+
+/*
+* Add the lane to selfdestruct chain; the ones still running at the end of the
+* whole process will be cancelled.
+*/
+static void selfdestruct_add( struct s_lane *s ) {
+
+    MUTEX_LOCK( &selfdestruct_cs );
+    {
+        assert( s->selfdestruct_next == NULL );
+
+        s->selfdestruct_next= selfdestruct_first;
+        selfdestruct_first= s;
+    }
+    MUTEX_UNLOCK( &selfdestruct_cs );
+}
+
+/*
+* A free-running lane has ended; remove it from selfdestruct chain
+*/
+static void selfdestruct_remove( struct s_lane *s ) {
+
+    MUTEX_LOCK( &selfdestruct_cs );
+    {
+        // Make sure (within the MUTEX) that we actually are in the chain
+        // still (at process exit they will remove us from chain and then
+        // cancel/kill).
+        //
+        if (s->selfdestruct_next != NULL) {
+            struct s_lane **ref= (struct s_lane **) &selfdestruct_first;
+            bool_t found= FALSE;
+    
+            while( *ref != SELFDESTRUCT_END ) {
+                if (*ref == s) {
+                    *ref= s->selfdestruct_next;
+                    s->selfdestruct_next= NULL;
+                    found= TRUE;
+                    break;
+                }
+                ref= (struct s_lane **) &((*ref)->selfdestruct_next);
+            }
+            assert( found );
+        }
+    }
+    MUTEX_UNLOCK( &selfdestruct_cs );
+}
+
+/*
+* Process end; cancel any still free-running threads
+*/
+static void selfdestruct_atexit( void ) {
+
+    if (selfdestruct_first == SELFDESTRUCT_END) return;    // no free-running threads
+
+    // Signal _all_ still running threads to exit
+    //
+    MUTEX_LOCK( &selfdestruct_cs );
+    {
+        struct s_lane *s= selfdestruct_first;
+        while( s != SELFDESTRUCT_END ) {
+            s->cancel_request= TRUE;
+            s= s->selfdestruct_next;
+        }
+    }
+    MUTEX_UNLOCK( &selfdestruct_cs );
+
+    // When noticing their cancel, the lanes will remove themselves from
+    // the selfdestruct chain.
+
+    // TBD: Not sure if Windows (multi core) will require the timed approach,
+    //      or single Yield. I don't have machine to test that (so leaving
+    //      for timed approach).    -- AKa 25-Oct-2008
+ 
+#ifdef PLATFORM_LINUX
+    // It seems enough for Linux to have a single yield here, which allows
+    // other threads (timer lane) to proceed. Without the yield, there is
+    // segfault.
+    //
+    YIELD();
+#else
+    // OS X 10.5 (Intel) needs more to avoid segfaults.
+    //
+    // "make test" is okay. 100's of "make require" are okay.
+    //
+    // Tested on MacBook Core Duo 2GHz and 10.5.5:
+    //  -- AKa 25-Oct-2008
+    //
+    #ifndef ATEXIT_WAIT_SECS
+    # define ATEXIT_WAIT_SECS (0.1)
+    #endif
+    {
+        double t_until= now_secs() + ATEXIT_WAIT_SECS;
+    
+        while( selfdestruct_first != SELFDESTRUCT_END ) {
+            YIELD();    // give threads time to act on their cancel
+            
+            if (now_secs() >= t_until) break;
+        }
+    }
+#endif
+
+    //---
+    // Kill the still free running threads
+    //
+    if ( selfdestruct_first != SELFDESTRUCT_END ) {
+        unsigned n=0;
+        MUTEX_LOCK( &selfdestruct_cs );
+        {
+            struct s_lane *s= selfdestruct_first;
+            while( s != SELFDESTRUCT_END ) {
+                n++;
+                s= s->selfdestruct_next;
+            }
+        }
+        MUTEX_UNLOCK( &selfdestruct_cs );
+
+    // Linux (at least 64-bit): CAUSES A SEGFAULT IF THIS BLOCK IS ENABLED
+    //       and works without the block (so let's leave those lanes running)
+    //
+#if 1
+        // 2.0.2: at least timer lane is still here
+        //
+        //fprintf( stderr, "Left %d lane(s) with cancel request at process end.\n", n );
+#else
+        MUTEX_LOCK( &selfdestruct_cs );
+        {
+            struct s_lane *s= selfdestruct_first;
+            while( s != SELFDESTRUCT_END ) {
+                struct s_lane *next_s= s->selfdestruct_next;
+                s->selfdestruct_next= NULL;     // detach from selfdestruct chain
+
+                THREAD_KILL( &s->thread );
+                s= next_s;
+                n++;
+            }
+            selfdestruct_first= SELFDESTRUCT_END;
+        }
+        MUTEX_UNLOCK( &selfdestruct_cs );
+
+        fprintf( stderr, "Killed %d lane(s) at process end.\n", n );
+#endif
+    }
+}
+
+
+// To allow free-running threads (longer lifespan than the handle's)
+// 'struct s_lane' are malloc/free'd and the handle only carries a pointer.
+// This is not deep userdata since the handle's not portable among lanes.
+//
+#define lua_toLane(L,i)  (* ((struct s_lane**) lua_touserdata(L,i)))
+
+
+/*
+* Check if the thread in question ('L') has been signalled for cancel.
+*
+* Called by cancellation hooks and/or pending Linda operations (because then
+* the check won't affect performance).
+*
+* Returns TRUE if any locks are to be exited, and 'cancel_error()' called,
+* to make execution of the lane end.
+*/
+static bool_t cancel_test( lua_State *L ) {
+    struct s_lane *s;
+
+    STACK_GROW(L,1);
+
+  STACK_CHECK(L)
+    lua_pushlightuserdata( L, CANCEL_TEST_KEY );
+    lua_rawget( L, LUA_REGISTRYINDEX );
+    s= lua_touserdata( L, -1 );     // lightuserdata (true 's_lane' pointer) / nil
+    lua_pop(L,1);
+  STACK_END(L,0)
+
+    // 's' is NULL for the original main state (no-one can cancel that)
+    //
+    return s && s->cancel_request;
+}
+
+static void cancel_error( lua_State *L ) {
+    STACK_GROW(L,1);
+    lua_pushlightuserdata( L, CANCEL_ERROR );    // special error value
+    lua_error(L);   // no return
+}
+
+static void cancel_hook( lua_State *L, lua_Debug *ar ) {
+    (void)ar;
+    if (cancel_test(L)) cancel_error(L);
+}
+
+
+//---
+// = _single( [cores_uint=1] )
+//
+// Limits the process to use only 'cores' CPU cores. To be used for performance
+// testing on multicore devices. DEBUGGING ONLY!
+//
+LUAG_FUNC( _single ) {
+	uint_t cores= luaG_optunsigned(L,1,1);
+
+#ifdef PLATFORM_OSX
+  #ifdef _UTILBINDTHREADTOCPU
+	if (cores > 1) luaL_error( L, "Limiting to N>1 cores not possible." );
+    // requires 'chudInitialize()'
+    utilBindThreadToCPU(0);     // # of CPU to run on (we cannot limit to 2..N CPUs?)
+  #else
+    luaL_error( L, "Not available: compile with _UTILBINDTHREADTOCPU" );
+  #endif
+#else
+    luaL_error( L, "not implemented!" );
+#endif
+	(void)cores;
+	
+	return 0;
+}
+
+
+/*
+* str= lane_error( error_val|str )
+*
+* Called if there's an error in some lane; add call stack to error message 
+* just like 'lua.c' normally does.
+*
+* ".. will be called with the error message and its return value will be the 
+*     message returned on the stack by lua_pcall."
+*
+* Note: Rather than modifying the error message itself, it would be better
+*     to provide the call stack (as string) completely separated. This would
+*     work great with non-string error values as well (current system does not).
+*     (This is NOT possible with the Lua 5.1 'lua_pcall()'; we could of course
+*     implement a Lanes-specific 'pcall' of our own that does this). TBD!!! :)
+*       --AKa 22-Jan-2009
+*/
+#ifdef ERROR_FULL_STACK
+
+static int lane_error( lua_State *L ) {
+    lua_Debug ar;
+    unsigned lev,n;
+
+    // [1]: error message (any type)
+
+    assert( lua_gettop(L)==1 );
+
+    // Don't do stack survey for cancelled lanes.
+    //
+#if 1
+    if (lua_touserdata(L,1) == CANCEL_ERROR)
+        return 1;   // just pass on
+#endif
+
+    // Place stack trace at 'registry[lane_error]' for the 'luc_pcall()'
+    // caller to fetch. This bypasses the Lua 5.1 limitation of only one
+    // return value from error handler to 'lua_pcall()' caller.
+
+    // It's adequate to push stack trace as a table. This gives the receiver
+    // of the stack best means to format it to their liking. Also, it allows
+    // us to add more stack info later, if needed.
+    //
+    // table of { "sourcefile.lua:<line>", ... }
+    //
+    STACK_GROW(L,3);
+    lua_newtable(L);
+
+    // Best to start from level 1, but in some cases it might be a C function
+    // and we don't get '.currentline' for that. It's okay - just keep level
+    // and table index growing separate.    --AKa 22-Jan-2009
+    //
+    lev= 0;
+    n=1;
+    while( lua_getstack(L, ++lev, &ar ) ) {
+        lua_getinfo(L, "Sl", &ar);
+        if (ar.currentline > 0) {
+            lua_pushinteger( L, n++ );
+            lua_pushfstring( L, "%s:%d", ar.short_src, ar.currentline );
+            lua_settable( L, -3 );
+        }
+    }
+
+    lua_pushlightuserdata( L, STACK_TRACE_KEY );
+    lua_insert(L,-2);
+    lua_settable( L, LUA_REGISTRYINDEX );
+
+    assert( lua_gettop(L)== 1 );
+
+    return 1;   // the untouched error value
+}
+#endif
+
+
+//---
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+  static THREAD_RETURN_T __stdcall lane_main( void *vs )
+#else
+  static THREAD_RETURN_T lane_main( void *vs )
+#endif
+{
+    struct s_lane *s= (struct s_lane *)vs;
+    int rc, rc2;
+    lua_State *L= s->L;
+
+    s->status= RUNNING;  // PENDING -> RUNNING
+
+    // Tie "set_finalizer()" to the state
+    //
+    lua_pushcfunction( L, LG_set_finalizer );
+    lua_setglobal( L, "set_finalizer" );
+
+#ifdef ERROR_FULL_STACK
+    STACK_GROW( L, 1 );
+    lua_pushcfunction( L, lane_error );
+    lua_insert( L, 1 );
+
+    // [1]: error handler
+    // [2]: function to run
+    // [3..top]: parameters
+    //
+    rc= lua_pcall( L, lua_gettop(L)-2, LUA_MULTRET, 1 /*error handler*/ );
+        // 0: no error
+        // LUA_ERRRUN: a runtime error (error pushed on stack)
+        // LUA_ERRMEM: memory allocation error
+        // LUA_ERRERR: error while running the error handler (if any)
+
+    assert( rc!=LUA_ERRERR );   // since we've authored it
+
+    lua_remove(L,1);    // remove error handler
+
+    // Lua 5.1 error handler is limited to one return value; taking stack trace
+    // via registry
+    //
+    if (rc!=0) {    
+        STACK_GROW(L,1);
+        lua_pushlightuserdata( L, STACK_TRACE_KEY );
+        lua_gettable(L, LUA_REGISTRYINDEX);
+
+        // For cancellation, a stack trace isn't placed
+        //
+        assert( lua_istable(L,2) || (lua_touserdata(L,1)==CANCEL_ERROR) );
+        
+        // Just leaving the stack trace table on the stack is enough to get
+        // it through to the master.
+    }
+
+#else
+    // This code does not use 'lane_error'
+    //
+    // [1]: function to run
+    // [2..top]: parameters
+    //
+    rc= lua_pcall( L, lua_gettop(L)-1, LUA_MULTRET, 0 /*no error handler*/ );
+        // 0: no error
+        // LUA_ERRRUN: a runtime error (error pushed on stack)
+        // LUA_ERRMEM: memory allocation error
+#endif
+
+//STACK_DUMP(L);
+    // Call finalizers, if the script has set them up.
+    //
+    rc2= run_finalizers(L,rc);
+    if (rc2!=0) {
+        // Error within a finalizer!  
+        // 
+        // [-1]: error message
+
+        rc= rc2;    // we're overruling the earlier script error or normal return
+
+        lua_insert( L,1 );  // make error message [1]
+        lua_settop( L,1 );  // remove all rest
+
+        // Place an empty stack table just to keep the API simple (always when
+        // there's an error, there's also stack table - though it may be empty).
+        //
+        lua_newtable(L);
+    }
+
+    if (s->selfdestruct_next != NULL) {
+        // We're a free-running thread and no-one's there to clean us up.
+        //
+        lua_close( s->L );
+        L= 0;
+
+    #if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) )
+        SIGNAL_FREE( &s->done_signal_ );
+        MUTEX_FREE( &s->done_lock_ );
+    #endif
+        selfdestruct_remove(s);     // away from selfdestruct chain
+        free(s);
+
+    } else {
+        // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them
+
+        enum e_status st= 
+            (rc==0) ? DONE 
+                    : (lua_touserdata(L,1)==CANCEL_ERROR) ? CANCELLED 
+                    : ERROR_ST;
+
+        // Posix no PTHREAD_TIMEDJOIN:
+        // 		'done_lock' protects the -> DONE|ERROR_ST|CANCELLED state change
+        //
+    #if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
+        s->status= st;
+    #else
+        MUTEX_LOCK( &s->done_lock_ );
+        {
+            s->status= st;
+            SIGNAL_ONE( &s->done_signal_ );   // wake up master (while 's->done_lock' is on)
+        }
+        MUTEX_UNLOCK( &s->done_lock_ );
+    #endif
+    }
+
+    return 0;   // ignored
+}
+
+
+//---
+// lane_ud= thread_new( function, [libs_str], 
+//                          [cancelstep_uint=0], 
+//                          [prio_int=0],
+//                          [globals_tbl],
+//                          [... args ...] )
+//
+// Upvalues: metatable to use for 'lane_ud'
+//
+LUAG_FUNC( thread_new )
+{
+    lua_State *L2;
+    struct s_lane *s;
+    struct s_lane **ud;
+
+    const char *libs= lua_tostring( L, 2 );
+    uint_t cs= luaG_optunsigned( L, 3,0);
+    int prio= luaL_optinteger( L, 4,0);
+    uint_t glob= luaG_isany(L,5) ? 5:0;
+
+    #define FIXED_ARGS (5)
+    uint_t args= lua_gettop(L) - FIXED_ARGS;
+
+    if (prio < THREAD_PRIO_MIN || prio > THREAD_PRIO_MAX) {
+        luaL_error( L, "Priority out of range: %d..+%d (%d)", 
+                            THREAD_PRIO_MIN, THREAD_PRIO_MAX, prio );
+    }
+
+    /* --- Create and prepare the sub state --- */
+
+    L2 = luaL_newstate();   // uses standard 'realloc()'-based allocator,
+                            // sets the panic callback
+
+    if (!L2) luaL_error( L, "'luaL_newstate()' failed; out of memory" );
+
+    STACK_GROW( L,2 );
+
+    // Setting the globals table (needs to be done before loading stdlibs,
+    // and the lane function)
+    //
+    if (glob!=0) {
+STACK_CHECK(L)
+        if (!lua_istable(L,glob)) 
+            luaL_error( L, "Expected table, got %s", luaG_typename(L,glob) );
+
+        lua_pushvalue( L, glob );
+        luaG_inter_move( L,L2, 1 );     // moves the table to L2
+
+        // L2 [-1]: table of globals
+
+        // "You can change the global environment of a Lua thread using lua_replace"
+        // (refman-5.0.pdf p. 30) 
+        //
+        lua_replace( L2, LUA_GLOBALSINDEX );
+STACK_END(L,0)
+    }
+
+    // Selected libraries
+    //
+    if (libs) {
+        const char *err= luaG_openlibs( L2, libs );
+        ASSERT_L( !err );   // bad libs should have been noticed by 'lanes.lua'
+
+        serialize_require( L2 );
+    }
+
+    // Lane main function
+    //
+STACK_CHECK(L)
+    lua_pushvalue( L, 1 );
+    luaG_inter_move( L,L2, 1 );    // L->L2
+STACK_MID(L,0)
+
+    ASSERT_L( lua_gettop(L2) == 1 );
+    ASSERT_L( lua_isfunction(L2,1) );
+
+    // revive arguments
+    //
+    if (args) luaG_inter_copy( L,L2, args );    // L->L2
+STACK_MID(L,0)
+
+ASSERT_L( (uint_t)lua_gettop(L2) == 1+args );
+ASSERT_L( lua_isfunction(L2,1) );
+
+    // 's' is allocated from heap, not Lua, since its life span may surpass 
+    // the handle's (if free running thread)
+    //
+    ud= lua_newuserdata( L, sizeof(struct s_lane*) );
+    ASSERT_L(ud);
+
+    s= *ud= malloc( sizeof(struct s_lane) );
+    ASSERT_L(s);
+
+    //memset( s, 0, sizeof(struct s_lane) );
+    s->L= L2;
+    s->status= PENDING;
+    s->cancel_request= FALSE;
+
+#if !( (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN) )
+    MUTEX_INIT( &s->done_lock_ );
+    SIGNAL_INIT( &s->done_signal_ );
+#endif
+    s->mstatus= NORMAL;
+    s->selfdestruct_next= NULL;
+
+    // Set metatable for the userdata
+    //
+    lua_pushvalue( L, lua_upvalueindex(1) );
+    lua_setmetatable( L, -2 );
+STACK_MID(L,1)
+
+    // Place 's' to registry, for 'cancel_test()' (even if 'cs'==0 we still
+    // do cancel tests at pending send/receive).
+    //
+    lua_pushlightuserdata( L2, CANCEL_TEST_KEY );
+    lua_pushlightuserdata( L2, s );
+    lua_rawset( L2, LUA_REGISTRYINDEX );
+
+    if (cs) {
+        lua_sethook( L2, cancel_hook, LUA_MASKCOUNT, cs );
+    }
+
+    THREAD_CREATE( &s->thread, lane_main, s, prio );
+STACK_END(L,1)
+
+    return 1;
+}
+
+
+//---
+// = thread_gc( lane_ud )
+//
+// Cleanup for a thread userdata. If the thread is still executing, leave it
+// alive as a free-running thread (will clean up itself).
+//
+// * Why NOT cancel/kill a loose thread: 
+//
+// At least timer system uses a free-running thread, they should be handy
+// and the issue of cancelling/killing threads at gc is not very nice, either
+// (would easily cause waits at gc cycle, which we don't want).
+//
+// * Why YES kill a loose thread:
+//
+// Current way causes segfaults at program exit, if free-running threads are
+// in certain stages. Details are not clear, but this is the core reason.
+// If gc would kill threads then at process exit only one thread would remain.
+//
+// Todo: Maybe we should have a clear #define for selecting either behaviour.
+//
+LUAG_FUNC( thread_gc ) {
+    struct s_lane *s= lua_toLane(L,1);
+
+    // We can read 's->status' without locks, but not wait for it
+    //
+    if (s->status < DONE) {
+        //
+        selfdestruct_add(s);
+        assert( s->selfdestruct_next );
+        return 0;
+
+    } else if (s->mstatus==KILLED) {
+        // Make sure a kill has proceeded, before cleaning up the data structure.
+        //
+        // If not doing 'THREAD_WAIT()' we should close the Lua state here
+        // (can it be out of order, since we killed the lane abruptly?)
+        //
+#if 0
+        lua_close( s->L );
+#else
+fprintf( stderr, "** Joining with a killed thread (needs testing) **" );
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
+        THREAD_WAIT( &s->thread, -1 );
+#else
+        THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, -1 );
+#endif
+fprintf( stderr, "** Joined ok **" );
+#endif
+    }
+
+    // Clean up after a (finished) thread
+    //
+#if (! ((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)))
+    SIGNAL_FREE( &s->done_signal_ );
+    MUTEX_FREE( &s->done_lock_ );
+    free(s);
+#endif
+
+    return 0;
+}
+
+
+//---
+// = thread_cancel( lane_ud [,timeout_secs=0.0] [,force_kill_bool=false] )
+//
+// The originator thread asking us specifically to cancel the other thread.
+//
+// 'timeout': <0: wait forever, until the lane is finished
+//            0.0: just signal it to cancel, no time waited
+//            >0: time to wait for the lane to detect cancellation
+//
+// 'force_kill': if true, and lane does not detect cancellation within timeout,
+//            it is forcefully killed. Using this with 0.0 timeout means just kill
+//            (unless the lane is already finished).
+//
+// Returns: true if the lane was already finished (DONE/ERROR_ST/CANCELLED) or if we
+//          managed to cancel it.
+//          false if the cancellation timed out, or a kill was needed.
+//
+LUAG_FUNC( thread_cancel )
+{
+    struct s_lane *s= lua_toLane(L,1);
+    double secs= 0.0;
+    uint_t force_i=2;
+    bool_t force, done= TRUE;
+    
+    if (lua_isnumber(L,2)) {
+        secs= lua_tonumber(L,2);
+        force_i++;
+    } else if (lua_isnil(L,2))
+        force_i++;
+
+    force= lua_toboolean(L,force_i);     // FALSE if nothing there
+    
+    // We can read 's->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN)
+    //
+    if (s->status < DONE) {
+        s->cancel_request= TRUE;    // it's now signalled to stop
+
+        done= thread_cancel( s, secs, force );
+    }
+
+    lua_pushboolean( L, done );
+    return 1;
+}
+
+static bool_t thread_cancel( struct s_lane *s, double secs, bool_t force )
+{
+    bool_t done= 
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
+        THREAD_WAIT( &s->thread, secs );
+#else
+        THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, secs );
+#endif
+
+    if ((!done) && force) {
+        // Killing is asynchronous; we _will_ wait for it to be done at
+        // GC, to make sure the data structure can be released (alternative
+        // would be use of "cancellation cleanup handlers" that at least
+        // PThread seems to have).
+        //
+        THREAD_KILL( &s->thread );
+        s->mstatus= KILLED;     // mark 'gc' to wait for it
+    }
+    return done;
+}
+
+
+//---
+// str= thread_status( lane_ud )
+//
+// Returns: "pending"   not started yet
+//          -> "running"   started, doing its work..
+//             <-> "waiting"   blocked in a receive()
+//                -> "done"     finished, results are there
+//                   / "error"     finished at an error, error value is there
+//                   / "cancelled"   execution cancelled by M (state gone)
+//
+LUAG_FUNC( thread_status )
+{
+    struct s_lane *s= lua_toLane(L,1);
+    enum e_status st= s->status;    // read just once (volatile)
+    const char *str;
+    
+    if (s->mstatus == KILLED)
+        st= CANCELLED;
+
+    str= (st==PENDING) ? "pending" :
+         (st==RUNNING) ? "running" :    // like in 'co.status()'
+         (st==WAITING) ? "waiting" :
+         (st==DONE) ? "done" :
+         (st==ERROR_ST) ? "error" :
+         (st==CANCELLED) ? "cancelled" : NULL;
+    ASSERT_L(str);
+
+    lua_pushstring( L, str );
+    return 1;
+}
+
+
+//---
+// [...] | [nil, err_any, stack_tbl]= thread_join( lane_ud [, wait_secs=-1] )
+//
+//  timeout:   returns nil
+//  done:      returns return values (0..N)
+//  error:     returns nil + error value + stack table
+//  cancelled: returns nil
+//
+LUAG_FUNC( thread_join )
+{
+    struct s_lane *s= lua_toLane(L,1);
+    double wait_secs= luaL_optnumber(L,2,-1.0);
+    lua_State *L2= s->L;
+    int ret;
+
+    bool_t done= 
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
+        THREAD_WAIT( &s->thread, wait_secs );
+#else
+        THREAD_WAIT( &s->thread, &s->done_signal_, &s->done_lock_, &s->status, wait_secs );
+#endif
+    if (!done)
+        return 0;      // timeout: pushes none, leaves 'L2' alive
+
+    // Thread is DONE/ERROR_ST/CANCELLED; all ours now
+
+    STACK_GROW( L, 1 );
+
+    switch( s->status ) {
+        case DONE: {   
+            uint_t n= lua_gettop(L2);       // whole L2 stack
+            luaG_inter_move( L2,L, n );
+            ret= n;
+            } break;
+
+        case ERROR_ST:
+            lua_pushnil(L);
+            luaG_inter_move( L2,L, 2 );    // error message at [-2], stack trace at [-1]
+            ret= 3;
+            break;
+
+        case CANCELLED:
+            ret= 0;
+            break;
+        
+        default:
+            fprintf( stderr, "Status: %d\n", s->status );
+            ASSERT_L( FALSE ); ret= 0;
+    }
+    lua_close(L2);
+
+    return ret;
+}
+
+
+/*---=== Timer support ===---
+*/
+
+/*
+* Push a timer gateway Linda object; only one deep userdata is
+* created for this, each lane will get its own proxy.
+*
+* Note: this needs to be done on the C side; Lua wouldn't be able
+*       to even see, when we've been initialized for the very first
+*       time (with us, they will be).
+*/
+static
+void push_timer_gateway( lua_State *L ) {
+
+    /* No need to lock; 'static' is just fine
+    */
+    static DEEP_PRELUDE *p;  // = NULL
+
+  STACK_CHECK(L)
+    if (!p) {
+        // Create the Linda (only on first time)
+        //
+        // proxy_ud= deep_userdata( idfunc )
+        //
+        lua_pushcfunction( L, luaG_deep_userdata );
+        lua_pushcfunction( L, LG_linda_id );
+        lua_call( L, 1 /*args*/, 1 /*retvals*/ );
+
+        ASSERT_L( lua_isuserdata(L,-1) );
+        
+        // Proxy userdata contents is only a 'DEEP_PRELUDE*' pointer
+        //
+        p= * (DEEP_PRELUDE**) lua_touserdata( L, -1 );
+        ASSERT_L(p && p->refcount==1 && p->deep);
+
+        // [-1]: proxy for accessing the Linda
+
+    } else {
+        /* Push a proxy based on the deep userdata we stored. 
+        */
+        luaG_push_proxy( L, LG_linda_id, p );
+    }
+  STACK_END(L,1)
+}
+
+/*
+* secs= now_secs()
+*
+* Returns the current time, as seconds (millisecond resolution).
+*/
+LUAG_FUNC( now_secs )
+{
+    lua_pushnumber( L, now_secs() );
+    return 1;
+}
+
+/*
+* wakeup_at_secs= wakeup_conv( date_tbl )
+*/
+LUAG_FUNC( wakeup_conv )
+{
+    int year, month, day, hour, min, sec, isdst;
+    struct tm tm= {0};
+        //
+        // .year (four digits)
+        // .month (1..12)
+        // .day (1..31)
+        // .hour (0..23)
+        // .min (0..59)
+        // .sec (0..61)
+        // .yday (day of the year)
+        // .isdst (daylight saving on/off)
+
+  STACK_CHECK(L)    
+    lua_getfield( L, 1, "year" ); year= lua_tointeger(L,-1); lua_pop(L,1);
+    lua_getfield( L, 1, "month" ); month= lua_tointeger(L,-1); lua_pop(L,1);
+    lua_getfield( L, 1, "day" ); day= lua_tointeger(L,-1); lua_pop(L,1);
+    lua_getfield( L, 1, "hour" ); hour= lua_tointeger(L,-1); lua_pop(L,1);
+    lua_getfield( L, 1, "min" ); min= lua_tointeger(L,-1); lua_pop(L,1);
+    lua_getfield( L, 1, "sec" ); sec= lua_tointeger(L,-1); lua_pop(L,1);
+
+    // If Lua table has '.isdst' we trust that. If it does not, we'll let
+    // 'mktime' decide on whether the time is within DST or not (value -1).
+    //
+    lua_getfield( L, 1, "isdst" );
+    isdst= lua_isboolean(L,-1) ? lua_toboolean(L,-1) : -1;
+    lua_pop(L,1);
+  STACK_END(L,0)
+
+    tm.tm_year= year-1900;
+    tm.tm_mon= month-1;     // 0..11
+    tm.tm_mday= day;        // 1..31
+    tm.tm_hour= hour;       // 0..23
+    tm.tm_min= min;         // 0..59
+    tm.tm_sec= sec;         // 0..60
+    tm.tm_isdst= isdst;     // 0/1/negative
+
+    lua_pushnumber( L, (double) mktime( &tm ) );   // ms=0
+    return 1;
+}
+
+
+/*---=== Module linkage ===---
+*/
+
+#define REG_FUNC( name ) \
+    lua_pushcfunction( L, LG_##name ); \
+    lua_setglobal( L, #name )
+
+#define REG_FUNC2( name, val ) \
+    lua_pushcfunction( L, val ); \
+    lua_setglobal( L, #name )
+
+#define REG_STR2( name, val ) \
+    lua_pushstring( L, val ); \
+    lua_setglobal( L, #name )
+
+#define REG_INT2( name, val ) \
+    lua_pushinteger( L, val ); \
+    lua_setglobal( L, #name )
+
+
+int 
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+__declspec(dllexport)
+#endif
+	luaopen_lanes( lua_State *L ) {
+    const char *err;
+    static volatile char been_here;  // =0
+
+    // One time initializations:
+    //
+    if (!been_here) {
+        been_here= TRUE;
+
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+        now_secs();     // initialize 'now_secs()' internal offset
+#endif
+
+#if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU)
+        chudInitialize();
+#endif
+    
+        // Locks for 'tools.c' inc/dec counters
+        //
+        MUTEX_INIT( &deep_lock );
+        MUTEX_INIT( &mtid_lock );
+    
+        // Serialize calls to 'require' from now on, also in the primary state
+        //
+        MUTEX_RECURSIVE_INIT( &require_cs );
+
+        serialize_require( L );
+
+        // Selfdestruct chain handling
+        //
+        MUTEX_INIT( &selfdestruct_cs );
+        atexit( selfdestruct_atexit );
+
+        //---
+        // Linux needs SCHED_RR to change thread priorities, and that is only
+        // allowed for sudo'ers. SCHED_OTHER (default) has no priorities.
+        // SCHED_OTHER threads are always lower priority than SCHED_RR.
+        //
+        // ^-- those apply to 2.6 kernel.  IF **wishful thinking** these 
+        //     constraints will change in the future, non-sudo priorities can 
+        //     be enabled also for Linux.
+        //
+#ifdef PLATFORM_LINUX
+        sudo= geteuid()==0;     // we are root?
+
+        // If lower priorities (-2..-1) are wanted, we need to lift the main
+        // thread to SCHED_RR and 50 (medium) level. Otherwise, we're always below 
+        // the launched threads (even -2).
+	    //
+  #ifdef LINUX_SCHED_RR
+        if (sudo) {
+            struct sched_param sp= {0}; sp.sched_priority= _PRIO_0;
+            PT_CALL( pthread_setschedparam( pthread_self(), SCHED_RR, &sp) );
+        }
+  #endif
+#endif
+        err= init_keepers();
+        if (err) 
+            luaL_error( L, "Unable to initialize: %s", err );
+    }
+    
+    // Linda identity function
+    //
+    REG_FUNC( linda_id );
+
+    // metatable for threads
+    //
+    lua_newtable( L );
+    lua_pushcfunction( L, LG_thread_gc );
+    lua_setfield( L, -2, "__gc" );
+
+    lua_pushcclosure( L, LG_thread_new, 1 );    // metatable as closure param
+    lua_setglobal( L, "thread_new" );
+
+    REG_FUNC( thread_status );
+    REG_FUNC( thread_join );
+    REG_FUNC( thread_cancel );
+
+    REG_STR2( _version, VERSION );
+    REG_FUNC( _single );
+
+    REG_FUNC2( _deep_userdata, luaG_deep_userdata );
+
+    REG_FUNC( now_secs );
+    REG_FUNC( wakeup_conv );
+
+    push_timer_gateway(L);    
+    lua_setglobal( L, "timer_gateway" );
+
+    REG_INT2( max_prio, THREAD_PRIO_MAX );
+
+    lua_pushlightuserdata( L, CANCEL_ERROR );
+    lua_setglobal( L, "cancel_error" );
+
+    return 0;
+}
+
+
diff --git a/src/lualanes/threading.c b/src/lualanes/threading.c
new file mode 100644
index 0000000000000000000000000000000000000000..68d1e41f2f338ef7defbb9d508e69f715926cc46
--- /dev/null
+++ b/src/lualanes/threading.c
@@ -0,0 +1,721 @@
+/*
+ * THREADING.C   	                    Copyright (c) 2007-08, Asko Kauppi
+ *
+ * Lua Lanes OS threading specific code.
+ *
+ * References:
+ *      <http://www.cse.wustl.edu/~schmidt/win32-cv-1.html>
+*/
+
+/*
+===============================================================================
+
+Copyright (C) 2007-08 Asko Kauppi <akauppi@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+
+#include "threading.h"
+#include "lua.h"
+
+#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
+# include <sys/time.h>
+#endif
+
+
+#if defined(PLATFORM_LINUX) || defined(PLATFORM_CYGWIN)
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+/* Linux needs to check, whether it's been run as root
+*/
+#ifdef PLATFORM_LINUX
+  volatile bool_t sudo;
+#endif
+
+#ifdef _MSC_VER
+// ".. selected for automatic inline expansion" (/O2 option)
+# pragma warning( disable : 4711 )
+// ".. type cast from function pointer ... to data pointer"
+# pragma warning( disable : 4054 )
+#endif
+
+//#define THREAD_CREATE_RETRIES_MAX 20
+    // loops (maybe retry forever?)
+
+/* 
+* FAIL is for unexpected API return values - essentially programming 
+* error in _this_ code. 
+*/
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+static void FAIL( const char *funcname, int rc ) {
+    fprintf( stderr, "%s() failed! (%d)\n", funcname, rc );
+    abort();
+}
+#endif
+
+
+/*
+* Returns millisecond timing (in seconds) for the current time.
+*
+* Note: This function should be called once in single-threaded mode in Win32,
+*       to get it initialized.
+*/
+time_d now_secs(void) {
+
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+    /*
+    * Windows FILETIME values are "100-nanosecond intervals since 
+    * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as
+    * the offset and it seems, so would they:
+    *
+    * <http://msdn.microsoft.com/en-us/library/ms724928(VS.85).aspx>
+    */
+    SYSTEMTIME st;
+    FILETIME ft;
+    ULARGE_INTEGER uli;
+    static ULARGE_INTEGER uli_epoch;   // Jan 1st 1970 0:0:0
+
+    if (uli_epoch.HighPart==0) {
+        st.wYear= 1970;
+        st.wMonth= 1;   // Jan
+        st.wDay= 1;
+        st.wHour= st.wMinute= st.wSecond= st.wMilliseconds= 0;
+
+        if (!SystemTimeToFileTime( &st, &ft ))
+            FAIL( "SystemTimeToFileTime", GetLastError() );
+
+        uli_epoch.LowPart= ft.dwLowDateTime;
+        uli_epoch.HighPart= ft.dwHighDateTime;
+    }
+
+    GetSystemTime( &st );	// current system date/time in UTC
+    if (!SystemTimeToFileTime( &st, &ft ))
+        FAIL( "SystemTimeToFileTime", GetLastError() );
+
+    uli.LowPart= ft.dwLowDateTime;
+    uli.HighPart= ft.dwHighDateTime;
+
+    /* 'double' has less accuracy than 64-bit int, but if it were to degrade,
+     * it would do so gracefully. In practise, the integer accuracy is not
+     * of the 100ns class but just 1ms (Windows XP).
+     */
+# if 1
+    // >= 2.0.3 code
+    return (double) ((uli.QuadPart - uli_epoch.QuadPart)/10000) / 1000.0;
+# elif 0
+    // fix from Kriss Daniels, see: 
+    // <http://luaforge.net/forum/forum.php?thread_id=22704&forum_id=1781>
+    //
+    // "seem to be getting negative numbers from the old version, probably number
+    // conversion clipping, this fixes it and maintains ms resolution"
+    //
+    // This was a bad fix, and caused timer test 5 sec timers to disappear.
+    // --AKa 25-Jan-2009
+    //
+    return ((double)((signed)((uli.QuadPart/10000) - (uli_epoch.QuadPart/10000)))) / 1000.0;
+# else
+    // <= 2.0.2 code
+    return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0;
+# endif
+#else
+    struct timeval tv;
+        // {
+        //   time_t       tv_sec;   /* seconds since Jan. 1, 1970 */
+        //   suseconds_t  tv_usec;  /* and microseconds */
+        // };
+
+    int rc= gettimeofday( &tv, NULL /*time zone not used any more (in Linux)*/ );
+    assert( rc==0 );
+
+    return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0;
+#endif
+}
+
+
+/*
+*/
+time_d SIGNAL_TIMEOUT_PREPARE( double secs ) {
+    if (secs<=0.0) return secs;
+    else return now_secs() + secs;
+}
+
+
+#if !((defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC))
+/*
+* Prepare 'abs_secs' kind of timeout to 'timespec' format
+*/
+static void prepare_timeout( struct timespec *ts, time_d abs_secs ) {
+    assert(ts);
+    assert( abs_secs >= 0.0 );
+
+    if (abs_secs==0.0)
+        abs_secs= now_secs();
+
+    ts->tv_sec= floor( abs_secs );
+    ts->tv_nsec= ((long)((abs_secs - ts->tv_sec) * 1000.0 +0.5)) * 1000000UL;   // 1ms = 1000000ns
+}
+#endif
+
+
+/*---=== Threading ===---*/
+
+//---
+// It may be meaningful to explicitly limit the new threads' C stack size.
+// We should know how much Lua needs in the C stack, all Lua side allocations
+// are done in heap so they don't count.
+//
+// Consequence of _not_ limiting the stack is running out of virtual memory
+// with 1000-5000 threads on 32-bit systems.
+//
+// Note: using external C modules may be affected by the stack size check.
+//       if having problems, set back to '0' (default stack size of the system).
+// 
+// Win32:       64K (?)
+// Win64:       xxx
+//
+// Linux x86:   2MB     Ubuntu 7.04 via 'pthread_getstacksize()'
+// Linux x64:   xxx
+// Linux ARM:   xxx
+//
+// OS X 10.4.9: 512K    <http://developer.apple.com/qa/qa2005/qa1419.html>
+//                      valid values N * 4KB
+//
+#ifndef _THREAD_STACK_SIZE
+# if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PLATFORM_CYGWIN)
+#  define _THREAD_STACK_SIZE 0
+      // Win32: does it work with less?
+# elif (defined PLATFORM_OSX)
+#  define _THREAD_STACK_SIZE (524288/2)   // 262144
+      // OS X: "make test" works on 65536 and even below
+      //       "make perftest" works on >= 4*65536 == 262144 (not 3*65536)
+# elif (defined PLATFORM_LINUX) && (defined __i386)
+#  define _THREAD_STACK_SIZE (2097152/16)  // 131072
+      // Linux x86 (Ubuntu 7.04): "make perftest" works on /16 (not on /32)
+# elif (defined PLATFORM_BSD) && (defined __i386)
+#  define _THREAD_STACK_SIZE (1048576/8)  // 131072
+      // FreeBSD 6.2 SMP i386: ("gmake perftest" works on /8 (not on /16)
+# endif
+#endif
+
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+  //
+  void MUTEX_INIT( MUTEX_T *ref ) {
+     *ref= CreateMutex( NULL /*security attr*/, FALSE /*not locked*/, NULL );
+     if (!ref) FAIL( "CreateMutex", GetLastError() );
+  }
+  void MUTEX_FREE( MUTEX_T *ref ) {
+     if (!CloseHandle(*ref)) FAIL( "CloseHandle (mutex)", GetLastError() );
+     *ref= NULL;
+  }
+  void MUTEX_LOCK( MUTEX_T *ref ) {
+    DWORD rc= WaitForSingleObject(*ref,INFINITE);
+    if (rc!=0) FAIL( "WaitForSingleObject", rc==WAIT_FAILED ? GetLastError() : rc );
+  }
+  void MUTEX_UNLOCK( MUTEX_T *ref ) {
+    if (!ReleaseMutex(*ref))
+        FAIL( "ReleaseMutex", GetLastError() );
+  }
+    /* MSDN: "If you would like to use the CRT in ThreadProc, use the
+              _beginthreadex function instead (of CreateThread)."
+       MSDN: "you can create at most 2028 threads"
+    */
+  void
+  THREAD_CREATE( THREAD_T *ref,
+                 THREAD_RETURN_T (__stdcall *func)( void * ),
+                     // Note: Visual C++ requires '__stdcall' where it is
+                 void *data, int prio /* -3..+3 */ ) {
+
+    HANDLE h= (HANDLE)_beginthreadex( NULL, // security
+                              _THREAD_STACK_SIZE,
+                              func,
+                              data,
+                              0,    // flags (0/CREATE_SUSPENDED)
+                              NULL  // thread id (not used)
+                            );    
+
+    if (h == INVALID_HANDLE_VALUE) FAIL( "CreateThread", GetLastError() );
+
+    if (prio!= 0) {
+        int win_prio= (prio == +3) ? THREAD_PRIORITY_TIME_CRITICAL :
+                      (prio == +2) ? THREAD_PRIORITY_HIGHEST :
+                      (prio == +1) ? THREAD_PRIORITY_ABOVE_NORMAL :
+                      (prio == -1) ? THREAD_PRIORITY_BELOW_NORMAL :
+                      (prio == -2) ? THREAD_PRIORITY_LOWEST :
+                                     THREAD_PRIORITY_IDLE;  // -3
+
+        if (!SetThreadPriority( h, win_prio )) 
+            FAIL( "SetThreadPriority", GetLastError() );
+    }
+    *ref= h;
+  }
+  //
+  bool_t THREAD_WAIT( THREAD_T *ref, double secs ) {
+    long ms= (long)((secs*1000.0)+0.5);
+
+    DWORD rc= WaitForSingleObject( *ref, ms<0 ? INFINITE:ms /*timeout*/ );
+        //
+        // (WAIT_ABANDONED)
+        // WAIT_OBJECT_0    success (0)
+        // WAIT_TIMEOUT
+        // WAIT_FAILED      more info via GetLastError()
+
+    if (rc == WAIT_TIMEOUT) return FALSE;
+    if (rc != 0) FAIL( "WaitForSingleObject", rc );
+    *ref= NULL;     // thread no longer usable
+    return TRUE;
+  }
+  //
+  void THREAD_KILL( THREAD_T *ref ) {
+    if (!TerminateThread( *ref, 0 )) FAIL("TerminateThread", GetLastError());
+    *ref= NULL;
+  }
+  //
+  void SIGNAL_INIT( SIGNAL_T *ref ) {
+    // 'manual reset' event type selected, to be able to wake up all the
+    // waiting threads.
+    //
+    HANDLE h= CreateEvent( NULL,    // security attributes
+                           TRUE,    // TRUE: manual event
+                           FALSE,   // Initial state
+                           NULL );  // name
+
+    if (h == NULL) FAIL( "CreateEvent", GetLastError() );
+    *ref= h;
+  }
+  void SIGNAL_FREE( SIGNAL_T *ref ) {
+    if (!CloseHandle(*ref)) FAIL( "CloseHandle (event)", GetLastError() );
+    *ref= NULL;
+  }
+  //
+  bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu_ref, time_d abs_secs ) {
+    DWORD rc;
+    long ms;
+    
+    if (abs_secs<0.0)
+        ms= INFINITE;
+    else if (abs_secs==0.0)
+        ms= 0;
+    else {
+        ms= (long) ((abs_secs - now_secs())*1000.0 + 0.5);
+        
+        // If the time already passed, still try once (ms==0). A short timeout
+        // may have turned negative or 0 because of the two time samples done.
+        //
+        if (ms<0) ms= 0;
+    }
+
+    // Unlock and start a wait, atomically (like condition variables do)
+    //
+    rc= SignalObjectAndWait( *mu_ref,   // "object to signal" (unlock)
+                             *ref,      // "object to wait on"
+                             ms,
+                             FALSE );   // not alertable
+
+    // All waiting locks are woken here; each competes for the lock in turn.
+    //
+    // Note: We must get the lock even if we've timed out; it makes upper
+    //       level code equivalent to how PThread does it.
+    //
+    MUTEX_LOCK(mu_ref);
+
+    if (rc==WAIT_TIMEOUT) return FALSE;
+    if (rc!=0) FAIL( "SignalObjectAndWait", rc );
+    return TRUE;
+  }
+  void SIGNAL_ALL( SIGNAL_T *ref ) {
+/* 
+ * MSDN tries to scare that 'PulseEvent' is bad, unreliable and should not be
+ * used. Use condition variables instead (wow, they have that!?!); which will
+ * ONLY WORK on Vista and 2008 Server, it seems... so MS, isn't it.
+ * 
+ * I refuse to believe that; using 'PulseEvent' is probably just as good as
+ * using Windows (XP) in the first place. Just don't use APC's (asynchronous
+ * process calls) in your C side coding.
+ */
+    // PulseEvent on manual event:
+    //
+    // Release ALL threads waiting for it (and go instantly back to unsignalled
+    // status = future threads to start a wait will wait)
+    //
+    if (!PulseEvent( *ref ))
+        FAIL( "PulseEvent", GetLastError() );
+  }
+#else
+  // PThread (Linux, OS X, ...)
+  //
+  // On OS X, user processes seem to be able to change priorities.
+  // On Linux, SCHED_RR and su privileges are required..  !-(
+  //
+  #include <errno.h>
+  #include <sys/time.h>
+  //
+  static void _PT_FAIL( int rc, const char *name, const char *file, uint_t line ) {
+    const char *why= (rc==EINVAL) ? "EINVAL" : 
+                     (rc==EBUSY) ? "EBUSY" : 
+                     (rc==EPERM) ? "EPERM" :
+                     (rc==ENOMEM) ? "ENOMEM" :
+                     (rc==ESRCH) ? "ESRCH" :
+                     //...
+                     "";
+    fprintf( stderr, "%s %d: %s failed, %d %s\n", file, line, name, rc, why );
+    abort();
+  }
+  #define PT_CALL( call ) { int rc= call; if (rc!=0) _PT_FAIL( rc, #call, __FILE__, __LINE__ ); }
+  //
+  void SIGNAL_INIT( SIGNAL_T *ref ) {
+    PT_CALL( pthread_cond_init(ref,NULL /*attr*/) );
+    }
+  void SIGNAL_FREE( SIGNAL_T *ref ) {
+    PT_CALL( pthread_cond_destroy(ref) );
+  }
+  //
+  /*
+  * Timeout is given as absolute since we may have fake wakeups during
+  * a timed out sleep. A Linda with some other key read, or just because
+  * PThread cond vars can wake up unwantedly.
+  */
+  bool_t SIGNAL_WAIT( SIGNAL_T *ref, pthread_mutex_t *mu, time_d abs_secs ) {
+    if (abs_secs<0.0) {
+        PT_CALL( pthread_cond_wait( ref, mu ) );  // infinite
+    } else {
+        int rc;
+        struct timespec ts;
+
+        assert( abs_secs != 0.0 );
+        prepare_timeout( &ts, abs_secs );
+
+        rc= pthread_cond_timedwait( ref, mu, &ts );
+
+        if (rc==ETIMEDOUT) return FALSE;
+        if (rc) { _PT_FAIL( rc, "pthread_cond_timedwait()", __FILE__, __LINE__ ); }
+    }
+    return TRUE;
+  }
+  //
+  void SIGNAL_ONE( SIGNAL_T *ref ) {
+    PT_CALL( pthread_cond_signal(ref) );     // wake up ONE (or no) waiting thread
+  }
+  //
+  void SIGNAL_ALL( SIGNAL_T *ref ) {
+    PT_CALL( pthread_cond_broadcast(ref) );     // wake up ALL waiting threads
+  }
+  //
+  void THREAD_CREATE( THREAD_T* ref, 
+                      THREAD_RETURN_T (*func)( void * ),
+                      void *data, int prio /* -2..+2 */ ) {
+    pthread_attr_t _a;
+    pthread_attr_t *a= &_a;
+    struct sched_param sp;
+
+    PT_CALL( pthread_attr_init(a) );
+
+#ifndef PTHREAD_TIMEDJOIN
+    // We create a NON-JOINABLE thread. This is mainly due to the lack of
+    // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier 
+    // freeing of the thread's resources).
+    //
+    PT_CALL( pthread_attr_setdetachstate(a,PTHREAD_CREATE_DETACHED) );
+#endif
+
+    // Use this to find a system's default stack size (DEBUG)
+#if 0
+  { size_t n; pthread_attr_getstacksize( a, &n );
+    fprintf( stderr, "Getstack: %u\n", (unsigned int)n ); }
+    	//  524288 on OS X
+    	// 2097152 on Linux x86 (Ubuntu 7.04)
+    	// 1048576 on FreeBSD 6.2 SMP i386
+#endif
+
+#if (defined _THREAD_STACK_SIZE) && (_THREAD_STACK_SIZE > 0)
+    PT_CALL( pthread_attr_setstacksize( a, _THREAD_STACK_SIZE ) );
+#endif
+    
+    bool_t normal= 
+#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
+        !sudo;          // with sudo, even normal thread must use SCHED_RR
+#else
+        prio == 0;      // create a default thread if
+#endif
+    if (!normal) {
+        // NB: PThreads priority handling is about as twisty as one can get it
+        //     (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!!
+
+        // "The specified scheduling parameters are only used if the scheduling
+        //  parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED."
+        //
+        PT_CALL( pthread_attr_setinheritsched( a, PTHREAD_EXPLICIT_SCHED ) );
+
+        //---
+        // "Select the scheduling policy for the thread: one of SCHED_OTHER 
+        // (regular, non-real-time scheduling), SCHED_RR (real-time, 
+        // round-robin) or SCHED_FIFO (real-time, first-in first-out)."
+        //
+        // "Using the RR policy ensures that all threads having the same
+        // priority level will be scheduled equally, regardless of their activity."
+        //
+        // "For SCHED_FIFO and SCHED_RR, the only required member of the
+        // sched_param structure is the priority sched_priority. For SCHED_OTHER,
+        // the affected scheduling parameters are implementation-defined."
+        //
+        // "The priority of a thread is specified as a delta which is added to 
+        // the priority of the process."
+        //
+        // ".. priority is an integer value, in the range from 1 to 127. 
+        //  1 is the least-favored priority, 127 is the most-favored."
+        //
+        // "Priority level 0 cannot be used: it is reserved for the system."
+        //
+        // "When you use specify a priority of -99 in a call to 
+        // pthread_setschedparam(), the priority of the target thread is 
+        // lowered to the lowest possible value."
+        //
+        // ...
+
+        // ** CONCLUSION **
+        //
+        // PThread priorities are _hugely_ system specific, and we need at
+        // least OS specific settings. Hopefully, Linuxes and OS X versions
+        // are uniform enough, among each other...
+        //
+#ifdef PLATFORM_OSX
+        // AK 10-Apr-07 (OS X PowerPC 10.4.9):
+        //
+        // With SCHED_RR, 26 seems to be the "normal" priority, where setting
+        // it does not seem to affect the order of threads processed.
+        //
+        // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26,
+        // but the difference is not so clear with OTHER).
+        //
+        // 'sched_get_priority_min()' and '..max()' give 15, 47 as the 
+        // priority limits. This could imply, user mode applications won't
+        // be able to use values outside of that range.
+        //
+        #define _PRIO_MODE SCHED_OTHER
+        
+        // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope
+        //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
+
+        #define _PRIO_HI  32    // seems to work (_carefully_ picked!)
+        #define _PRIO_0   26    // detected
+        #define _PRIO_LO   1    // seems to work (tested)
+
+#elif defined(PLATFORM_LINUX)
+        // (based on Ubuntu Linux 2.6.15 kernel)
+        //
+        // SCHED_OTHER is the default policy, but does not allow for priorities.
+        // SCHED_RR allows priorities, all of which (1..99) are higher than
+        // a thread with SCHED_OTHER policy.
+        //
+        // <http://kerneltrap.org/node/6080>
+        // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library>
+        // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
+        //
+        // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING,
+        // but even Ubuntu does not seem to define it.
+        //
+        #define _PRIO_MODE SCHED_RR
+        
+        // NTLP 2.5: only system scope allowed (being the basic reason why
+        //           root privileges are required..)
+        //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
+
+        #define _PRIO_HI 99
+        #define _PRIO_0  50
+        #define _PRIO_LO 1
+
+#elif defined(PLATFORM_BSD)
+        //
+        // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html>
+        //
+        // "When control over the thread scheduling is desired, then FreeBSD
+        //  with the libpthread implementation is by far the best choice .."
+        //
+        #define _PRIO_MODE SCHED_OTHER
+        #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS
+        #define _PRIO_HI 31
+        #define _PRIO_0  15
+        #define _PRIO_LO 1
+
+#elif defined(PLATFORM_CYGWIN)
+	//
+	// TBD: Find right values for Cygwin
+	//
+#else
+        #error "Unknown OS: not implemented!"
+#endif
+
+#ifdef _PRIO_SCOPE
+        PT_CALL( pthread_attr_setscope( a, _PRIO_SCOPE ) );
+#endif
+        PT_CALL( pthread_attr_setschedpolicy( a, _PRIO_MODE ) );
+
+#define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2) )
+#define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2) )
+
+        sp.sched_priority= 
+            (prio == +2) ? _PRIO_HI :
+            (prio == +1) ? _PRIO_AN :
+#if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR)
+            (prio == 0) ? _PRIO_0 :
+#endif
+            (prio == -1) ? _PRIO_BN : _PRIO_LO;
+
+        PT_CALL( pthread_attr_setschedparam( a, &sp ) );
+    }
+
+    //---
+    // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system
+    // thread limit (not userland thread). Actual limit for us is way higher.
+    // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!)
+    //
+# ifndef THREAD_CREATE_RETRIES_MAX
+    // Don't bother with retries; a failure is a failure
+    //
+    { 
+      int rc= pthread_create( ref, a, func, data );
+      if (rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__-1 );
+    }
+# else
+# error "This code deprecated"
+/*
+    // Wait slightly if thread creation has exchausted the system
+    //
+    { uint_t retries;
+    for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) {
+
+        int rc= pthread_create( ref, a, func, data );
+            //
+            // OS X / Linux:
+            //    EAGAIN: ".. lacked the necessary resources to create
+            //             another thread, or the system-imposed limit on the
+            //             total number of threads in a process 
+            //             [PTHREAD_THREADS_MAX] would be exceeded."
+            //    EINVAL: attr is invalid
+            // Linux:
+            //    EPERM: no rights for given parameters or scheduling (no sudo)
+            //    ENOMEM: (known to fail with this code, too - not listed in man)
+            
+        if (rc==0) break;   // ok!
+
+        // In practise, exhaustion seems to be coming from memory, not a
+        // maximum number of threads. Keep tuning... ;)
+        //
+        if (rc==EAGAIN) {
+//fprintf( stderr, "Looping (retries=%d) ", retries );    // DEBUG
+
+            // Try again, later.
+
+            Yield();
+        } else {
+            _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ );
+        }
+    }
+    }
+*/
+# endif
+
+    if (a) {
+        PT_CALL( pthread_attr_destroy(a) );
+    }
+  }
+  //
+  /*
+  * Wait for a thread to finish.
+  *
+  * 'mu_ref' is a lock we should use for the waiting; initially unlocked.
+  * Same lock as passed to THREAD_EXIT.
+  *
+  * Returns TRUE for succesful wait, FALSE for timed out
+  */
+#ifdef PTHREAD_TIMEDJOIN
+  bool_t THREAD_WAIT( THREAD_T *ref, double secs )
+#else
+  bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs )
+#endif
+{
+    struct timespec ts_store;
+    const struct timespec *timeout= NULL;
+    bool_t done;
+
+    // Do timeout counting before the locks
+    //
+#ifdef PTHREAD_TIMEDJOIN
+    if (secs>=0.0) {
+#else
+    if (secs>0.0) {
+#endif
+        prepare_timeout( &ts_store, now_secs()+secs );
+        timeout= &ts_store;
+    }
+
+#ifdef PTHREAD_TIMEDJOIN
+    /* Thread is joinable
+    */
+    if (!timeout) {
+        PT_CALL( pthread_join( *ref, NULL /*ignore exit value*/ ));
+        done= TRUE;
+    } else {
+        int rc= PTHREAD_TIMEDJOIN( *ref, NULL, timeout );
+        if ((rc!=0) && (rc!=ETIMEDOUT)) {
+            _PT_FAIL( rc, "PTHREAD_TIMEDJOIN", __FILE__, __LINE__-2 );
+        }
+        done= rc==0;
+    }
+#else
+    /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot
+     * join with it. Use the cond.var.
+    */
+    MUTEX_LOCK( mu_ref );
+    
+        // 'secs'==0.0 does not need to wait, just take the current status
+        // within the 'mu_ref' locks
+        //
+        if (secs != 0.0) {
+            while( *st_ref < DONE ) {
+                if (!timeout) {
+                    PT_CALL( pthread_cond_wait( signal_ref, mu_ref ));
+                } else {
+                    int rc= pthread_cond_timedwait( signal_ref, mu_ref, timeout );
+                    if (rc==ETIMEDOUT) break;
+                    if (rc!=0) _PT_FAIL( rc, "pthread_cond_timedwait", __FILE__, __LINE__-2 );
+                }
+            }
+        }
+        done= *st_ref >= DONE;  // DONE|ERROR_ST|CANCELLED
+
+    MUTEX_UNLOCK( mu_ref );
+#endif
+    return done;
+  }    
+  //
+  void THREAD_KILL( THREAD_T *ref ) {
+    pthread_cancel( *ref );
+  }
+#endif
+
+static const lua_Alloc alloc_f= 0;
diff --git a/src/lualanes/threading.h b/src/lualanes/threading.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a83229b64f373fd9d253315ffe71a64438d60e5
--- /dev/null
+++ b/src/lualanes/threading.h
@@ -0,0 +1,196 @@
+/*
+* THREADING.H
+*/
+#ifndef THREADING_H
+#define THREADING_H
+
+/* Platform detection
+*/
+#ifdef _WIN32_WCE
+  #define PLATFORM_POCKETPC
+#elif (defined _WIN32)
+  #define PLATFORM_WIN32
+#elif (defined __linux__)
+  #define PLATFORM_LINUX
+#elif (defined __APPLE__) && (defined __MACH__)
+  #define PLATFORM_OSX
+#elif (defined __NetBSD__) || (defined __FreeBSD__) || (defined BSD)
+  #define PLATFORM_BSD
+#elif (defined __QNX__)
+  #define PLATFORM_QNX
+#elif (defined __CYGWIN__)
+  #define PLATFORM_CYGWIN
+#else
+  #error "Unknown platform!"
+#endif
+
+typedef int bool_t;
+#ifndef FALSE
+# define FALSE 0
+# define TRUE 1
+#endif
+
+typedef unsigned int uint_t;
+
+#if defined(PLATFORM_WIN32) && defined(__GNUC__)
+/* MinGW with MSVCR80.DLL */
+/* Do this BEFORE including time.h so that it is declaring _mktime32()
+ * as it would have declared mktime().
+ */
+# define mktime _mktime32
+#endif
+#include <time.h>
+
+/* Note: ERROR is a defined entity on Win32
+*/
+enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED };
+
+
+/*---=== Locks & Signals ===---
+*/
+
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+  #define WIN32_LEAN_AND_MEAN
+  // 'SignalObjectAndWait' needs this (targets Windows 2000 and above)
+  #define _WIN32_WINNT 0x0400
+  #include <windows.h>
+  #include <process.h>
+
+  // MSDN: http://msdn2.microsoft.com/en-us/library/ms684254.aspx
+  //
+  // CRITICAL_SECTION can be used for simple code protection. Mutexes are
+  // needed for use with the SIGNAL system.
+  //
+  #define MUTEX_T HANDLE
+  void MUTEX_INIT( MUTEX_T *ref );
+  #define MUTEX_RECURSIVE_INIT(ref)  MUTEX_INIT(ref)  /* always recursive in Win32 */
+  void MUTEX_FREE( MUTEX_T *ref );
+  void MUTEX_LOCK( MUTEX_T *ref );
+  void MUTEX_UNLOCK( MUTEX_T *ref );
+
+  typedef unsigned THREAD_RETURN_T;
+
+  #define SIGNAL_T HANDLE
+  
+  #define YIELD() Sleep(0)
+#else
+  // PThread (Linux, OS X, ...)
+  //
+  #include <pthread.h>
+
+  #ifdef PLATFORM_LINUX
+  # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP
+  #else
+    /* OS X, ... */
+  # define _MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE
+  #endif
+
+  #define MUTEX_T            pthread_mutex_t
+  #define MUTEX_INIT(ref)    pthread_mutex_init(ref,NULL)
+  #define MUTEX_RECURSIVE_INIT(ref) \
+      { pthread_mutexattr_t a; pthread_mutexattr_init( &a ); \
+        pthread_mutexattr_settype( &a, _MUTEX_RECURSIVE ); \
+        pthread_mutex_init(ref,&a); pthread_mutexattr_destroy( &a ); \
+      }
+  #define MUTEX_FREE(ref)    pthread_mutex_destroy(ref)
+  #define MUTEX_LOCK(ref)    pthread_mutex_lock(ref)
+  #define MUTEX_UNLOCK(ref)  pthread_mutex_unlock(ref)
+
+  typedef void * THREAD_RETURN_T;
+
+  typedef pthread_cond_t SIGNAL_T;
+
+  void SIGNAL_ONE( SIGNAL_T *ref );
+  
+  // Yield is non-portable:
+  //
+  //    OS X 10.4.8/9 has pthread_yield_np()
+  //    Linux 2.4   has pthread_yield() if _GNU_SOURCE is #defined
+  //    FreeBSD 6.2 has pthread_yield()
+  //    ...
+  //
+  #ifdef PLATFORM_OSX
+    #define YIELD() pthread_yield_np()
+  #else
+    #define YIELD() pthread_yield()
+  #endif
+#endif
+
+void SIGNAL_INIT( SIGNAL_T *ref );
+void SIGNAL_FREE( SIGNAL_T *ref );
+void SIGNAL_ALL( SIGNAL_T *ref );
+
+/*
+* 'time_d': <0.0 for no timeout
+*           0.0 for instant check
+*           >0.0 absolute timeout in secs + ms
+*/
+typedef double time_d;
+time_d now_secs(void);
+
+time_d SIGNAL_TIMEOUT_PREPARE( double rel_secs );
+
+bool_t SIGNAL_WAIT( SIGNAL_T *ref, MUTEX_T *mu, time_d timeout );
+
+
+/*---=== Threading ===---
+*/
+
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
+
+  typedef HANDLE THREAD_T;
+  //
+  void THREAD_CREATE( THREAD_T *ref,
+                      THREAD_RETURN_T (__stdcall *func)( void * ),
+                      void *data, int prio /* -3..+3 */ );
+                 
+# define THREAD_PRIO_MIN (-3)
+# define THREAD_PRIO_MAX (+3)
+
+#else
+    /* Platforms that have a timed 'pthread_join()' can get away with a simpler
+    * implementation. Others will use a condition variable.
+    */
+# ifdef USE_PTHREAD_TIMEDJOIN
+#  ifdef PLATFORM_OSX
+#   error "No 'pthread_timedjoin()' on this system"
+#  else
+    /* Linux, ... */
+#   define PTHREAD_TIMEDJOIN pthread_timedjoin_np
+#  endif
+# endif
+
+  typedef pthread_t THREAD_T;
+
+  void THREAD_CREATE( THREAD_T *ref, 
+                      THREAD_RETURN_T (*func)( void * ),
+                      void *data, int prio /* -2..+2 */ );
+                      
+# if defined(PLATFORM_LINUX)
+  volatile bool_t sudo;
+#  ifdef LINUX_SCHED_RR
+#   define THREAD_PRIO_MIN (sudo ? -2 : 0)
+#  else
+#   define THREAD_PRIO_MIN (0)
+#  endif
+# define THREAD_PRIO_MAX (sudo ? +2 : 0)
+# else
+#  define THREAD_PRIO_MIN (-2)
+#  define THREAD_PRIO_MAX (+2)
+# endif
+#endif
+
+/* 
+* Win32 and PTHREAD_TIMEDJOIN allow waiting for a thread with a timeout.
+* Posix without PTHREAD_TIMEDJOIN needs to use a condition variable approach.
+*/
+#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) || (defined PTHREAD_TIMEDJOIN)
+  bool_t THREAD_WAIT( THREAD_T *ref, double secs );
+#else
+  bool_t THREAD_WAIT( THREAD_T *ref, SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref, double secs );
+#endif
+
+void THREAD_KILL( THREAD_T *ref );
+
+#endif
+    // THREADING_H
diff --git a/src/lualanes/tools.c b/src/lualanes/tools.c
new file mode 100644
index 0000000000000000000000000000000000000000..9ff68c278dd691afe13f0d18f1e5c286debe1123
--- /dev/null
+++ b/src/lualanes/tools.c
@@ -0,0 +1,1208 @@
+/*
+ * TOOLS.C   	                    Copyright (c) 2002-08, Asko Kauppi
+ *
+ * Lua tools to support Lanes.
+*/
+
+/*
+===============================================================================
+
+Copyright (C) 2002-08 Asko Kauppi <akauppi@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+*/
+
+#include "tools.h"
+
+#include "lualib.h"
+#include "lauxlib.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+static volatile lua_CFunction hijacked_tostring;     // = NULL
+
+MUTEX_T deep_lock;
+MUTEX_T mtid_lock;
+
+/*---=== luaG_dump ===---*/
+
+void luaG_dump( lua_State* L ) {
+
+    int top= lua_gettop(L);
+    int i;
+
+	fprintf( stderr, "\n\tDEBUG STACK:\n" );
+
+	if (top==0)
+		fprintf( stderr, "\t(none)\n" );
+
+	for( i=1; i<=top; i++ ) {
+		int type= lua_type( L, i );
+
+		fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename(L,type) );
+
+		// Print item contents here...
+		//
+		// Note: this requires 'tostring()' to be defined. If it is NOT,
+		//       enable it for more debugging.
+		//
+    STACK_CHECK(L)
+        STACK_GROW( L, 2 )
+
+        lua_getglobal( L, "tostring" );
+            //
+            // [-1]: tostring function, or nil
+
+        if (!lua_isfunction(L,-1)) {
+             fprintf( stderr, "('tostring' not available)" );
+         } else {
+             lua_pushvalue( L, i );
+             lua_call( L, 1 /*args*/, 1 /*retvals*/ );
+
+             // Don't trust the string contents
+             //
+             fprintf( stderr, "%s", lua_tostring(L,-1) );
+         }
+         lua_pop(L,1);
+    STACK_END(L,0)
+		fprintf( stderr, "\n" );
+		}
+	fprintf( stderr, "\n" );
+}
+
+
+/*---=== luaG_openlibs ===---*/
+extern int luaopen_socket_core(lua_State *L);
+extern int luaopen_mime_core(lua_State *L);
+extern int luaopen_struct(lua_State *L);
+extern int luaopen_profiler(lua_State *L);
+
+static const luaL_Reg libs[] = {
+  { LUA_LOADLIBNAME, luaopen_package },
+  { LUA_TABLIBNAME, luaopen_table },
+  { LUA_IOLIBNAME, luaopen_io },
+  { LUA_OSLIBNAME, luaopen_os },
+  { LUA_STRLIBNAME, luaopen_string },
+  { LUA_MATHLIBNAME, luaopen_math },
+  { LUA_DBLIBNAME, luaopen_debug },
+  //
+  { "base", NULL },         // ignore "base" (already acquired it)
+  { "coroutine", NULL },    // part of Lua 5.1 base package
+
+  // T-Engine embeded libs
+  { "socket.core", luaopen_socket_core },
+  { "mime.core", luaopen_mime_core },
+  { "struct", luaopen_struct },
+  { "profiler", luaopen_profiler },
+  { NULL, NULL }
+};
+
+static bool_t openlib( lua_State *L, const char *name, size_t len ) {
+
+    unsigned i;
+    bool_t all= strncmp( name, "*", len ) == 0;
+
+    for( i=0; libs[i].name; i++ ) {
+        if (all || (strncmp(name, libs[i].name, len) ==0)) {
+            if (libs[i].func) {
+                STACK_GROW(L,2);
+                lua_pushcfunction( L, libs[i].func );
+                lua_pushstring( L, libs[i].name );
+                lua_call( L, 1, 0 );
+            }
+            if (!all) return TRUE;
+        }
+    }
+    return all;
+}
+
+/*
+* Like 'luaL_openlibs()' but allows the set of libraries be selected
+*
+*   NULL    no libraries, not even base
+*   ""      base library only
+*   "io,string"     named libraries
+*   "*"     all libraries
+*
+* Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL.
+*
+* Returns NULL for ok, position of error within 'libs' on failure.
+*/
+#define is_name_char(c) (isalpha(c) || (c)=='*')
+
+const char *luaG_openlibs( lua_State *L, const char *libs ) {
+    const char *p;
+    unsigned len;
+
+	if (!libs) return NULL;     // no libs, not even 'base'
+
+    // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
+    //
+    lua_gc(L, LUA_GCSTOP, 0);
+
+    // Anything causes 'base' to be taken in
+    //
+    STACK_GROW(L,2);
+    lua_pushcfunction( L, luaopen_base );
+    lua_pushliteral( L, "" );
+    lua_call( L, 1, 0 );
+
+    for( p= libs; *p; p+=len ) {
+        len=0;
+        while (*p && !is_name_char(*p)) p++;    // bypass delimiters
+        while (is_name_char(p[len])) len++;     // bypass name
+        if (len && (!openlib( L, p, len )))
+            break;
+    }
+    lua_gc(L, LUA_GCRESTART, 0);
+
+    return *p ? p : NULL;
+}
+
+
+
+/*---=== Deep userdata ===---*/
+
+/* The deep portion must be allocated separately of any Lua state's; it's
+* lifespan may be longer than that of the creating state.
+*/
+#define DEEP_MALLOC malloc
+#define DEEP_FREE   free
+
+/*
+* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's
+* metatables:
+*
+*   metatable   ->  idfunc
+*   idfunc      ->  metatable
+*/
+#define DEEP_LOOKUP_KEY ((void*)set_deep_lookup)
+    // any unique light userdata
+
+static void push_registry_subtable( lua_State *L, void *token );
+
+/*
+* Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists.
+* Pops the both values off the stack.
+*/
+void set_deep_lookup( lua_State *L ) {
+
+    STACK_GROW(L,3);
+
+  STACK_CHECK(L)
+#if 1
+    push_registry_subtable( L, DEEP_LOOKUP_KEY );
+#else
+    /* ..to be removed.. */
+    lua_pushlightuserdata( L, DEEP_LOOKUP_KEY );
+    lua_rawget( L, LUA_REGISTRYINDEX );
+
+    if (lua_isnil(L,-1)) {
+        // First time here; let's make the lookup
+        //
+        lua_pop(L,1);
+
+        lua_newtable(L);
+        lua_pushlightuserdata( L, DEEP_LOOKUP_KEY );
+        lua_pushvalue(L,-2);
+            //
+            // [-3]: {} (2nd ref)
+            // [-2]: DEEP_LOOKUP_KEY
+            // [-1]: {}
+
+        lua_rawset( L, LUA_REGISTRYINDEX );
+            //
+            // [-1]: lookup table (empty)
+    }
+#endif
+  STACK_MID(L,1)
+
+    lua_insert(L,-3);
+
+    // [-3]: lookup table
+    // [-2]: A
+    // [-1]: B
+
+    lua_pushvalue( L,-1 );  // B
+    lua_pushvalue( L,-3 );  // A
+    lua_rawset( L, -5 );    // B->A
+    lua_rawset( L, -3 );    // A->B
+    lua_pop( L,1 );
+
+  STACK_END(L,-2)
+}
+
+/*
+* Pops the key (metatable or idfunc) off the stack, and replaces with the
+* deep lookup value (idfunc/metatable/nil).
+*/
+void get_deep_lookup( lua_State *L ) {
+
+    STACK_GROW(L,1);
+
+  STACK_CHECK(L)
+    lua_pushlightuserdata( L, DEEP_LOOKUP_KEY );
+    lua_rawget( L, LUA_REGISTRYINDEX );
+
+    if (!lua_isnil(L,-1)) {
+        // [-2]: key (metatable or idfunc)
+        // [-1]: lookup table
+
+        lua_insert( L, -2 );
+        lua_rawget( L, -2 );
+
+        // [-2]: lookup table
+        // [-1]: value (metatable / idfunc / nil)
+    }
+    lua_remove(L,-2);
+        // remove lookup, or unused key
+  STACK_END(L,0)
+}
+
+/*
+* Return the registered ID function for 'index' (deep userdata proxy),
+* or NULL if 'index' is not a deep userdata proxy.
+*/
+static
+lua_CFunction get_idfunc( lua_State *L, int index ) {
+    lua_CFunction ret;
+
+    index= STACK_ABS(L,index);
+
+    STACK_GROW(L,1);
+
+  STACK_CHECK(L)
+    if (!lua_getmetatable( L, index ))
+        return NULL;    // no metatable
+
+    // [-1]: metatable of [index]
+
+    get_deep_lookup(L);
+        //
+        // [-1]: idfunc/nil
+
+    ret= lua_tocfunction(L,-1);
+    lua_pop(L,1);
+  STACK_END(L,0)
+    return ret;
+}
+
+
+/*
+* void= mt.__gc( proxy_ud )
+*
+* End of life for a proxy object; reduce the deep reference count and clean
+* it up if reaches 0.
+*/
+static
+int deep_userdata_gc( lua_State *L ) {
+    DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 );
+    DEEP_PRELUDE *p= *proxy;
+    int v;
+
+    *proxy= 0;  // make sure we don't use it any more
+
+    MUTEX_LOCK( &deep_lock );
+      v= --(p->refcount);
+    MUTEX_UNLOCK( &deep_lock );
+
+    if (v==0) {
+        int pushed;
+
+        // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup
+        //
+        lua_CFunction idfunc= get_idfunc(L,1);
+        ASSERT_L(idfunc);
+
+        lua_settop(L,0);    // clean stack so we can call 'idfunc' directly
+
+        // void= idfunc( "delete", lightuserdata )
+        //
+        lua_pushliteral( L, "delete" );
+        lua_pushlightuserdata( L, p->deep );
+        pushed= idfunc(L);
+
+        if (pushed)
+            luaL_error( L, "Bad idfunc on \"delete\": returned something" );
+
+        DEEP_FREE( (void*)p );
+    }
+    return 0;
+}
+
+
+/*
+* Push a proxy userdata on the stack.
+*
+* Initializes necessary structures if it's the first time 'idfunc' is being
+* used in this Lua state (metatable, registring it). Otherwise, increments the
+* reference count.
+*/
+void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *prelude ) {
+    DEEP_PRELUDE **proxy;
+
+    MUTEX_LOCK( &deep_lock );
+      ++(prelude->refcount);  // one more proxy pointing to this deep data
+    MUTEX_UNLOCK( &deep_lock );
+
+    STACK_GROW(L,4);
+
+  STACK_CHECK(L)
+
+    proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) );
+    ASSERT_L(proxy);
+    *proxy= prelude;
+
+    // Get/create metatable for 'idfunc' (in this state)
+    //
+    lua_pushcfunction( L, idfunc );    // key
+    get_deep_lookup(L);
+        //
+        // [-2]: proxy
+        // [-1]: metatable / nil
+
+    if (lua_isnil(L,-1)) {
+        // No metatable yet; make one and register it
+        //
+        lua_pop(L,1);
+
+        // tbl= idfunc( "metatable" )
+        //
+        lua_pushcfunction( L, idfunc );
+        lua_pushliteral( L, "metatable" );
+        lua_call( L, 1 /*args*/, 1 /*results*/ );
+            //
+            // [-2]: proxy
+            // [-1]: metatable (returned by 'idfunc')
+
+        if (!lua_istable(L,-1))
+            luaL_error( L, "Bad idfunc on \"metatable\": did not return one" );
+
+        // Add '__gc' method
+        //
+        lua_pushcfunction( L, deep_userdata_gc );
+        lua_setfield( L, -2, "__gc" );
+
+        // Memorize for later rounds
+        //
+        lua_pushvalue( L,-1 );
+        lua_pushcfunction( L, idfunc );
+            //
+            // [-4]: proxy
+            // [-3]: metatable (2nd ref)
+            // [-2]: metatable
+            // [-1]: idfunc
+
+        set_deep_lookup(L);
+    }
+  STACK_MID(L,2)
+    ASSERT_L( lua_isuserdata(L,-2) );
+    ASSERT_L( lua_istable(L,-1) );
+
+    // [-2]: proxy userdata
+    // [-1]: metatable to use
+
+    lua_setmetatable( L, -2 );
+
+  STACK_END(L,1)
+    // [-1]: proxy userdata
+}
+
+
+/*
+* Create a deep userdata
+*
+*   proxy_ud= deep_userdata( idfunc [, ...] )
+*
+* Creates a deep userdata entry of the type defined by 'idfunc'.
+* Other parameters are passed on to the 'idfunc' "new" invocation.
+*
+* 'idfunc' must fulfill the following features:
+*
+*   lightuserdata= idfunc( "new" [, ...] )      -- creates a new deep data instance
+*   void= idfunc( "delete", lightuserdata )     -- releases a deep data instance
+*   tbl= idfunc( "metatable" )          -- gives metatable for userdata proxies
+*
+* Reference counting and true userdata proxying are taken care of for the
+* actual data type.
+*
+* Types using the deep userdata system (and only those!) can be passed between
+* separate Lua states via 'luaG_inter_move()'.
+*
+* Returns:  'proxy' userdata for accessing the deep data via 'luaG_todeep()'
+*/
+int luaG_deep_userdata( lua_State *L ) {
+    lua_CFunction idfunc= lua_tocfunction( L,1 );
+    int pushed;
+
+    DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) );
+    ASSERT_L(prelude);
+
+    prelude->refcount= 0;   // 'luaG_push_proxy' will lift it to 1
+
+    STACK_GROW(L,1);
+  STACK_CHECK(L)
+
+    // Replace 'idfunc' with "new" in the stack (keep possible other params)
+    //
+    lua_remove(L,1);
+    lua_pushliteral( L, "new" );
+    lua_insert(L,1);
+
+    // lightuserdata= idfunc( "new" [, ...] )
+    //
+    pushed= idfunc(L);
+
+    if ((pushed!=1) || lua_type(L,-1) != LUA_TLIGHTUSERDATA)
+        luaL_error( L, "Bad idfunc on \"new\": did not return light userdata" );
+
+    prelude->deep= lua_touserdata(L,-1);
+    ASSERT_L(prelude->deep);
+
+    lua_pop(L,1);   // pop deep data
+
+    luaG_push_proxy( L, idfunc, prelude );
+        //
+        // [-1]: proxy userdata
+
+  STACK_END(L,1)
+    return 1;
+}
+
+
+/*
+* Access deep userdata through a proxy.
+*
+* Reference count is not changed, and access to the deep userdata is not
+* serialized. It is the module's responsibility to prevent conflicting usage.
+*/
+void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index ) {
+    DEEP_PRELUDE **proxy;
+
+  STACK_CHECK(L)
+    if (get_idfunc(L,index) != idfunc)
+        return NULL;    // no metatable, or wrong kind
+
+    proxy= (DEEP_PRELUDE**)lua_touserdata( L, index );
+  STACK_END(L,0)
+
+    return (*proxy)->deep;
+}
+
+
+/*
+* Copy deep userdata between two separate Lua states.
+*
+* Returns:
+*   the id function of the copied value, or NULL for non-deep userdata
+*   (not copied)
+*/
+static
+lua_CFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) {
+    DEEP_PRELUDE **proxy;
+    DEEP_PRELUDE *p;
+
+    lua_CFunction idfunc;
+
+    idfunc= get_idfunc( L, index );
+    if (!idfunc) return NULL;   // not a deep userdata
+
+    // Increment reference count
+    //
+    proxy= (DEEP_PRELUDE**)lua_touserdata( L, index );
+    p= *proxy;
+
+    luaG_push_proxy( L2, idfunc, p );
+        //
+        // L2 [-1]: proxy userdata
+
+    return idfunc;
+}
+
+
+
+/*---=== Inter-state copying ===---*/
+
+/*-- Metatable copying --*/
+
+/*
+ * 'reg[ REG_MT_KNOWN ]'= {
+ *      [ table ]= id_uint,
+ *          ...
+ *      [ id_uint ]= table,
+ *          ...
+ * }
+ */
+
+/*
+* Push a registry subtable (keyed by unique 'token') onto the stack.
+* If the subtable does not exist, it is created and chained.
+*/
+static
+void push_registry_subtable( lua_State *L, void *token ) {
+
+    STACK_GROW(L,3);
+
+  STACK_CHECK(L)
+
+    lua_pushlightuserdata( L, token );
+    lua_rawget( L, LUA_REGISTRYINDEX );
+        //
+        // [-1]: nil/subtable
+
+    if (lua_isnil(L,-1)) {
+        lua_pop(L,1);
+        lua_newtable(L);                    // value
+        lua_pushlightuserdata( L, token );  // key
+        lua_pushvalue(L,-2);
+            //
+            // [-3]: value (2nd ref)
+            // [-2]: key
+            // [-1]: value
+
+        lua_rawset( L, LUA_REGISTRYINDEX );
+    }
+  STACK_END(L,1)
+
+    ASSERT_L( lua_istable(L,-1) );
+}
+
+#define REG_MTID ( (void*) get_mt_id )
+
+/*
+* Get a unique ID for metatable at [i].
+*/
+static
+uint_t get_mt_id( lua_State *L, int i ) {
+    static uint_t last_id= 0;
+    uint_t id;
+
+    i= STACK_ABS(L,i);
+
+    STACK_GROW(L,3);
+
+  STACK_CHECK(L)
+    push_registry_subtable( L, REG_MTID );
+    lua_pushvalue(L, i);
+    lua_rawget( L, -2 );
+        //
+        // [-2]: reg[REG_MTID]
+        // [-1]: nil/uint
+
+    id= lua_tointeger(L,-1);    // 0 for nil
+    lua_pop(L,1);
+  STACK_MID(L,1)
+
+    if (id==0) {
+        MUTEX_LOCK( &mtid_lock );
+            id= ++last_id;
+        MUTEX_UNLOCK( &mtid_lock );
+
+        /* Create two-way references: id_uint <-> table
+        */
+        lua_pushvalue(L,i);
+        lua_pushinteger(L,id);
+        lua_rawset( L, -3 );
+
+        lua_pushinteger(L,id);
+        lua_pushvalue(L,i);
+        lua_rawset( L, -3 );
+    }
+    lua_pop(L,1);     // remove 'reg[REG_MTID]' reference
+
+  STACK_END(L,0)
+
+    return id;
+}
+
+
+static int buf_writer( lua_State *L, const void* b, size_t n, void* B ) {
+  (void)L;
+  luaL_addlstring((luaL_Buffer*) B, (const char *)b, n);
+  return 0;
+}
+
+
+/*
+ * Check if we've already copied the same table from 'L', and
+ * reuse the old copy. This allows table upvalues shared by multiple
+ * local functions to point to the same table, also in the target.
+ *
+ * Always pushes a table to 'L2'.
+ *
+ * Returns TRUE if the table was cached (no need to fill it!); FALSE if
+ * it's a virgin.
+ */
+static
+bool_t push_cached_table( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) {
+    bool_t ret;
+
+    ASSERT_L( hijacked_tostring );
+    ASSERT_L( L2_cache_i != 0 );
+
+    STACK_GROW(L,2);
+    STACK_GROW(L2,3);
+
+    // Create an identity string for table at [i]; it should stay unique at
+    // least during copying of the data (then we can clear the caches).
+    //
+  STACK_CHECK(L)
+    lua_pushcfunction( L, hijacked_tostring );
+    lua_pushvalue( L, i );
+    lua_call( L, 1 /*args*/, 1 /*retvals*/ );
+        //
+        // [-1]: "table: 0x...."
+
+  STACK_END(L,1)
+    ASSERT_L( lua_type(L,-1) == LUA_TSTRING );
+
+    // L2_cache[id_str]= [{...}]
+    //
+  STACK_CHECK(L2)
+
+    // We don't need to use the from state ('L') in ID since the life span
+    // is only for the duration of a copy (both states are locked).
+    //
+    lua_pushstring( L2, lua_tostring(L,-1) );
+    lua_pop(L,1);   // remove the 'tostring(tbl)' value (in L!)
+
+//fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
+
+    lua_pushvalue( L2, -1 );
+    lua_rawget( L2, L2_cache_i );
+        //
+        // [-2]: identity string ("table: 0x...")
+        // [-1]: table|nil
+
+    if (lua_isnil(L2,-1)) {
+        lua_pop(L2,1);
+        lua_newtable(L2);
+        lua_pushvalue(L2,-1);
+        lua_insert(L2,-3);
+            //
+            // [-3]: new table (2nd ref)
+            // [-2]: identity string
+            // [-1]: new table
+
+        lua_rawset(L2, L2_cache_i);
+            //
+            // [-1]: new table (tied to 'L2_cache' table')
+
+        ret= FALSE;     // brand new
+
+    } else {
+        lua_remove(L2,-2);
+        ret= TRUE;      // from cache
+    }
+  STACK_END(L2,1)
+    //
+    // L2 [-1]: table to use as destination
+
+    ASSERT_L( lua_istable(L2,-1) );
+    return ret;
+}
+
+
+/*
+ * Check if we've already copied the same function from 'L', and reuse the old
+ * copy.
+ *
+ * Always pushes a function to 'L2'.
+ */
+static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i );
+
+static
+void push_cached_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) {
+    // TBD: Merge this and same code for tables
+
+    ASSERT_L( hijacked_tostring );
+    ASSERT_L( L2_cache_i != 0 );
+
+    STACK_GROW(L,2);
+    STACK_GROW(L2,3);
+
+  STACK_CHECK(L)
+    lua_pushcfunction( L, hijacked_tostring );
+    lua_pushvalue( L, i );
+    lua_call( L, 1 /*args*/, 1 /*retvals*/ );
+        //
+        // [-1]: "function: 0x...."
+
+  STACK_END(L,1)
+    ASSERT_L( lua_type(L,-1) == LUA_TSTRING );
+
+    // L2_cache[id_str]= function
+    //
+  STACK_CHECK(L2)
+
+    // We don't need to use the from state ('L') in ID since the life span
+    // is only for the duration of a copy (both states are locked).
+    //
+    lua_pushstring( L2, lua_tostring(L,-1) );
+    lua_pop(L,1);   // remove the 'tostring(tbl)' value (in L!)
+
+//fprintf( stderr, "<< ID: %s >>\n", lua_tostring(L2,-1) );
+
+    lua_pushvalue( L2, -1 );
+    lua_rawget( L2, L2_cache_i );
+        //
+        // [-2]: identity string ("function: 0x...")
+        // [-1]: function|nil|true  (true means: we're working on it; recursive)
+
+    if (lua_isnil(L2,-1)) {
+        lua_pop(L2,1);
+
+        // Set to 'true' for the duration of creation; need to find self-references
+        // via upvalues
+        //
+        lua_pushboolean(L2,TRUE);
+        lua_setfield( L2, L2_cache_i, lua_tostring(L2,-2) );
+
+        inter_copy_func( L2, L2_cache_i, L, i );    // pushes a copy of the func
+
+        lua_pushvalue(L2,-1);
+        lua_insert(L2,-3);
+            //
+            // [-3]: function (2nd ref)
+            // [-2]: identity string
+            // [-1]: function
+
+        lua_rawset(L2,L2_cache_i);
+            //
+            // [-1]: function (tied to 'L2_cache' table')
+
+    } else if (lua_isboolean(L2,-1)) {
+        // Loop in preparing upvalues; either direct or via a table
+        //
+        // Note: This excludes the case where a function directly addresses
+        //       itself as an upvalue (recursive lane creation).
+        //
+        luaL_error( L, "Recursive use of upvalues; cannot copy the function" );
+
+    } else {
+        lua_remove(L2,-2);
+    }
+  STACK_END(L2,1)
+    //
+    // L2 [-1]: function
+
+    ASSERT_L( lua_isfunction(L2,-1) );
+}
+
+
+/*
+* Copy a function over, which has not been found in the cache.
+*/
+enum e_vt {
+    VT_NORMAL, VT_KEY, VT_METATABLE
+};
+static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt value_type );
+
+static void inter_copy_func( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i ) {
+
+    lua_CFunction cfunc= lua_tocfunction( L,i );
+    unsigned n;
+
+    ASSERT_L( L2_cache_i != 0 );
+
+  STACK_GROW(L,2);
+
+  STACK_CHECK(L)
+    if (!cfunc) {   // Lua function
+        luaL_Buffer b;
+        const char *s;
+        size_t sz;
+        int tmp;
+        const char *name= NULL;
+
+#if 0
+        // "To get information about a function you push it onto the
+        // stack and start the what string with the character '>'."
+        //
+        { lua_Debug ar;
+        lua_pushvalue( L, i );
+        lua_getinfo(L, ">n", &ar);      // fills 'name' and 'namewhat', pops function
+        name= ar.namewhat;
+
+        fprintf( stderr, "NAME: %s\n", name );  // just gives NULL
+        }
+#endif
+        // 'lua_dump()' needs the function at top of stack
+        //
+        if (i!=-1) lua_pushvalue( L, i );
+
+        luaL_buffinit(L,&b);
+        tmp= lua_dump(L, buf_writer, &b);
+        ASSERT_L(tmp==0);
+            //
+            // "value returned is the error code returned by the last call
+            // to the writer" (and we only return 0)
+
+        luaL_pushresult(&b);    // pushes dumped string on 'L'
+        s= lua_tolstring(L,-1,&sz);
+        ASSERT_L( s && sz );
+
+        if (i!=-1) lua_remove( L, -2 );
+
+        // Note: Line numbers seem to be taken precisely from the
+        //       original function. 'name' is not used since the chunk
+        //       is precompiled (it seems...).
+        //
+        // TBD: Can we get the function's original name through, as well?
+        //
+        if (luaL_loadbuffer(L2, s, sz, name) != 0) {
+            // chunk is precompiled so only LUA_ERRMEM can happen
+            // "Otherwise, it pushes an error message"
+            //
+            STACK_GROW( L,1 );
+            luaL_error( L, "%s", lua_tostring(L2,-1) );
+        }
+        lua_pop(L,1);   // remove the dumped string
+  STACK_MID(L,0)
+    }
+
+    /* push over any upvalues; references to this function will come from
+    * cache so we don't end up in eternal loop.
+    */
+    for( n=0; lua_getupvalue( L, i, 1+n ) != NULL; n++ ) {
+        if ((!cfunc) && lua_equal(L,i,-1)) {
+            /* Lua closure that has a (recursive) upvalue to itself
+            */
+            lua_pushvalue( L2, -((int)n)-1 );
+        } else {
+            if (!inter_copy_one_( L2, L2_cache_i, L, lua_gettop(L), VT_NORMAL ))
+                luaL_error( L, "Cannot copy upvalue type '%s'", luaG_typename(L,-1) );
+        }
+        lua_pop(L,1);
+    }
+    // L2: function + 'n' upvalues (>=0)
+
+  STACK_MID(L,0)
+
+    if (cfunc) {
+        lua_pushcclosure( L2, cfunc, n );   // eats up upvalues
+    } else {
+        // Set upvalues (originally set to 'nil' by 'lua_load')
+        //
+        int func_index= lua_gettop(L2)-n;
+
+        for( ; n>0; n-- ) {
+            const char *rc= lua_setupvalue( L2, func_index, n );
+                //
+                // "assigns the value at the top of the stack to the upvalue and returns its name.
+                // It also pops the value from the stack."
+
+            ASSERT_L(rc);      // not having enough slots?
+        }
+    }
+  STACK_END(L,0)
+}
+
+
+/*
+* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove
+* the original value.
+*
+* NOTE: Both the states must be solely in the current OS thread's posession.
+*
+* 'i' is an absolute index (no -1, ...)
+*
+* Returns TRUE if value was pushed, FALSE if its type is non-supported.
+*/
+static bool_t inter_copy_one_( lua_State *L2, uint_t L2_cache_i, lua_State *L, uint_t i, enum e_vt vt )
+{
+    bool_t ret= TRUE;
+
+    STACK_GROW( L2, 1 );
+
+  STACK_CHECK(L2)
+
+    switch ( lua_type(L,i) ) {
+        /* Basic types allowed both as values, and as table keys */
+
+        case LUA_TBOOLEAN:
+            lua_pushboolean( L2, lua_toboolean(L, i) );
+            break;
+
+        case LUA_TNUMBER:
+            /* LNUM patch support (keeping integer accuracy) */
+#ifdef LUA_LNUM
+            if (lua_isinteger(L,i)) {
+                lua_pushinteger( L2, lua_tointeger(L, i) );
+                break;
+            }
+#endif
+            lua_pushnumber( L2, lua_tonumber(L, i) );
+            break;
+
+        case LUA_TSTRING: {
+            size_t len; const char *s = lua_tolstring( L, i, &len );
+            lua_pushlstring( L2, s, len );
+            } break;
+
+        case LUA_TLIGHTUSERDATA:
+            lua_pushlightuserdata( L2, lua_touserdata(L, i) );
+            break;
+
+        /* The following types are not allowed as table keys */
+
+        case LUA_TUSERDATA: if (vt==VT_KEY) { ret=FALSE; break; }
+            /* Allow only deep userdata entities to be copied across
+             */
+            if (!luaG_copydeep( L, L2, i )) {
+                // Cannot copy it full; copy as light userdata
+                //
+                lua_pushlightuserdata( L2, lua_touserdata(L, i) );
+            } break;
+
+        case LUA_TNIL: if (vt==VT_KEY) { ret=FALSE; break; }
+            lua_pushnil(L2);
+            break;
+
+        case LUA_TFUNCTION: if (vt==VT_KEY) { ret=FALSE; break; } {
+            /*
+            * Passing C functions is risky; if they refer to LUA_ENVIRONINDEX
+            * and/or LUA_REGISTRYINDEX they might work unintended (not work)
+            * at the target.
+            *
+            * On the other hand, NOT copying them causes many self tests not
+            * to work (timer, hangtest, ...)
+            *
+            * The trouble is, we cannot KNOW if the function at hand is safe
+            * or not. We cannot study it's behaviour. We could trust the user,
+            * but they might not even know they're sending lua_CFunction over
+            * (as upvalues etc.).
+            */
+#if 0
+            if (lua_iscfunction(L,i))
+                luaL_error( L, "Copying lua_CFunction between Lua states is risky, and currently disabled." );
+#endif
+          STACK_CHECK(L2)
+            push_cached_func( L2, L2_cache_i, L, i );
+            ASSERT_L( lua_isfunction(L2,-1) );
+          STACK_END(L2,1)
+            } break;
+
+        case LUA_TTABLE: if (vt==VT_KEY) { ret=FALSE; break; } {
+
+          STACK_CHECK(L)
+          STACK_CHECK(L2)
+
+            /* Check if we've already copied the same table from 'L' (during this transmission), and
+             * reuse the old copy. This allows table upvalues shared by multiple
+             * local functions to point to the same table, also in the target.
+             * Also, this takes care of cyclic tables and multiple references
+             * to the same subtable.
+             *
+             * Note: Even metatables need to go through this test; to detect
+             *      loops s.a. those in required module tables (getmetatable(lanes).lanes == lanes)
+             */
+            if (push_cached_table( L2, L2_cache_i, L, i )) {
+                ASSERT_L( lua_istable(L2, -1) );    // from cache
+                break;
+            }
+            ASSERT_L( lua_istable(L2,-1) );
+
+            STACK_GROW( L, 2 );
+            STACK_GROW( L2, 2 );
+
+            lua_pushnil(L);    // start iteration
+            while( lua_next( L, i ) ) {
+                uint_t val_i= lua_gettop(L);
+                uint_t key_i= val_i-1;
+
+                /* Only basic key types are copied over; others ignored
+                 */
+                if (inter_copy_one_( L2, 0 /*key*/, L, key_i, VT_KEY )) {
+                    /*
+                    * Contents of metatables are copied with cache checking;
+                    * important to detect loops.
+                    */
+                    if (inter_copy_one_( L2, L2_cache_i, L, val_i, VT_NORMAL )) {
+                        ASSERT_L( lua_istable(L2,-3) );
+                        lua_rawset( L2, -3 );    // add to table (pops key & val)
+                    } else {
+                        luaL_error( L, "Unable to copy over type '%s' (in %s)",
+                                        luaG_typename(L,val_i),
+                                        vt==VT_NORMAL ? "table":"metatable" );
+                    }
+                }
+                lua_pop( L, 1 );    // pop value (next round)
+            }
+          STACK_MID(L,0)
+          STACK_MID(L2,1)
+
+            /* Metatables are expected to be immutable, and copied only once.
+            */
+            if (lua_getmetatable( L, i )) {
+                //
+                // L [-1]: metatable
+
+                uint_t mt_id= get_mt_id( L, -1 );    // Unique id for the metatable
+
+                STACK_GROW(L2,4);
+
+                push_registry_subtable( L2, REG_MTID );
+              STACK_MID(L2,2);
+                lua_pushinteger( L2, mt_id );
+                lua_rawget( L2, -2 );
+                    //
+                    // L2 ([-3]: copied table)
+                    //    [-2]: reg[REG_MTID]
+                    //    [-1]: nil/metatable pre-known in L2
+
+              STACK_MID(L2,3);
+
+                if (lua_isnil(L2,-1)) {   /* L2 did not know the metatable */
+                    lua_pop(L2,1);
+              STACK_MID(L2,2);
+ASSERT_L( lua_istable(L,-1) );
+                    if (inter_copy_one_( L2, L2_cache_i /*for function cacheing*/, L, lua_gettop(L) /*[-1]*/, VT_METATABLE )) {
+                        //
+                        // L2 ([-3]: copied table)
+                        //    [-2]: reg[REG_MTID]
+                        //    [-1]: metatable (copied from L)
+
+              STACK_MID(L2,3);
+                        // mt_id -> metatable
+                        //
+                        lua_pushinteger(L2,mt_id);
+                        lua_pushvalue(L2,-2);
+                        lua_rawset(L2,-4);
+
+                        // metatable -> mt_id
+                        //
+                        lua_pushvalue(L2,-1);
+                        lua_pushinteger(L2,mt_id);
+                        lua_rawset(L2,-4);
+
+              STACK_MID(L2,3);
+                    } else {
+                        luaL_error( L, "Error copying a metatable" );
+                    }
+              STACK_MID(L2,3);
+                }
+                    // L2 ([-3]: copied table)
+                    //    [-2]: reg[REG_MTID]
+                    //    [-1]: metatable (pre-known or copied from L)
+
+                lua_remove(L2,-2);   // take away 'reg[REG_MTID]'
+                    //
+                    // L2: ([-2]: copied table)
+                    //     [-1]: metatable for that table
+
+                lua_setmetatable( L2, -2 );
+
+                // L2: [-1]: copied table (with metatable set if source had it)
+
+                lua_pop(L,1);   // remove source metatable (L, not L2!)
+            }
+          STACK_END(L2,1)
+          STACK_END(L,0)
+            } break;
+
+        /* The following types cannot be copied */
+
+        case LUA_TTHREAD:
+            ret=FALSE; break;
+    }
+
+  STACK_END(L2, ret? 1:0)
+
+    return ret;
+}
+
+
+/*
+* Akin to 'lua_xmove' but copies values between _any_ Lua states.
+*
+* NOTE: Both the states must be solely in the current OS thread's posession.
+*
+* Note: Parameters are in this order ('L' = from first) to be same as 'lua_xmove'.
+*/
+void luaG_inter_copy( lua_State* L, lua_State *L2, uint_t n )
+{
+    uint_t top_L= lua_gettop(L);
+    uint_t top_L2= lua_gettop(L2);
+    uint_t i;
+
+    /* steal Lua library's 'luaB_tostring()' from the first call. Other calls
+    * don't have to have access to it.
+    *
+    * Note: multiple threads won't come here at once; this function will
+    *       be called before there can be multiple threads (no locking needed).
+    */
+    if (!hijacked_tostring) {
+        STACK_GROW( L,1 );
+
+      STACK_CHECK(L)
+        lua_getglobal( L, "tostring" );
+            //
+            // [-1]: function|nil
+
+        hijacked_tostring= lua_tocfunction( L, -1 );
+        lua_pop(L,1);
+      STACK_END(L,0)
+
+        if (!hijacked_tostring) {
+            luaL_error( L, "Need to see 'tostring()' once" );
+        }
+    }
+
+    if (n > top_L)
+        luaL_error( L, "Not enough values: %d < %d", top_L, n );
+
+    STACK_GROW( L2, n+1 );
+
+    /*
+    * Make a cache table for the duration of this copy. Collects tables and
+    * function entries, avoiding the same entries to be passed on as multiple
+    * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
+    */
+    lua_newtable(L2);
+
+    for (i=top_L-n+1; i <= top_L; i++) {
+        if (!inter_copy_one_( L2, top_L2+1, L, i, VT_NORMAL )) {
+
+            luaL_error( L, "Cannot copy type: %s", luaG_typename(L,i) );
+       }
+    }
+
+    /*
+    * Remove the cache table. Persistant caching would cause i.e. multiple
+    * messages passed in the same table to use the same table also in receiving
+    * end.
+    */
+    lua_remove( L2, top_L2+1 );
+
+    ASSERT_L( (uint_t)lua_gettop(L) == top_L );
+    ASSERT_L( (uint_t)lua_gettop(L2) == top_L2+n );
+}
+
+
+void luaG_inter_move( lua_State* L, lua_State *L2, uint_t n )
+{
+    luaG_inter_copy( L, L2, n );
+    lua_pop( L,(int)n );
+}
diff --git a/src/lualanes/tools.h b/src/lualanes/tools.h
new file mode 100644
index 0000000000000000000000000000000000000000..d155c65eb65447ebccd76ef1a6bf49eee5b96f5a
--- /dev/null
+++ b/src/lualanes/tools.h
@@ -0,0 +1,72 @@
+/*
+* TOOLS.H
+*/
+#ifndef TOOLS_H
+#define TOOLS_H
+
+#include "lua.h"
+#include "threading.h"
+    // MUTEX_T
+
+#include <assert.h>
+
+// Note: The < -10000 test is to leave registry/global/upvalue indices untouched
+//
+#define /*int*/ STACK_ABS(L,n) \
+	( ((n) >= 0 || (n) <= -10000) ? (n) : lua_gettop(L) +(n) +1 )
+
+#ifdef NDEBUG
+  #define _ASSERT_L(lua,c)  /*nothing*/
+  #define STACK_CHECK(L)    /*nothing*/
+  #define STACK_MID(L,c)    /*nothing*/
+  #define STACK_END(L,c)    /*nothing*/
+  #define STACK_DUMP(L)    /*nothing*/
+  #define DEBUG()   /*nothing*/
+#else
+  #define _ASSERT_L(lua,c)  { if (!(c)) luaL_error( lua, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #c ); }
+  //
+  #define STACK_CHECK(L)     { int _oldtop_##L = lua_gettop(L);
+  #define STACK_MID(L,change)  { int a= lua_gettop(L)-_oldtop_##L; int b= (change); \
+                               if (a != b) luaL_error( L, "STACK ASSERT failed (%d not %d): %s:%d", a, b, __FILE__, __LINE__ ); }
+  #define STACK_END(L,change)  STACK_MID(L,change) }
+
+  #define STACK_DUMP(L)    luaG_dump(L);
+  #define DEBUG()   fprintf( stderr, "<<%s %d>>\n", __FILE__, __LINE__ );
+#endif
+#define ASSERT_L(c) _ASSERT_L(L,c)
+
+#define STACK_GROW(L,n) { if (!lua_checkstack(L,n)) luaL_error( L, "Cannot grow stack!" ); }
+
+#define LUAG_FUNC( func_name ) static int LG_##func_name( lua_State *L )
+
+#define luaG_optunsigned(L,i,d) ((uint_t) luaL_optinteger(L,i,d))
+#define luaG_tounsigned(L,i) ((uint_t) lua_tointeger(L,i))
+
+#define luaG_isany(L,i)  (!lua_isnil(L,i))
+
+#define luaG_typename( L, index ) lua_typename( L, lua_type(L,index) )
+
+void luaG_dump( lua_State* L );
+
+const char *luaG_openlibs( lua_State *L, const char *libs );
+
+int luaG_deep_userdata( lua_State *L );
+void *luaG_todeep( lua_State *L, lua_CFunction idfunc, int index );
+
+typedef struct {
+    volatile int refcount;
+    void *deep;
+} DEEP_PRELUDE;
+
+void luaG_push_proxy( lua_State *L, lua_CFunction idfunc, DEEP_PRELUDE *deep_userdata );
+
+void luaG_inter_copy( lua_State *L, lua_State *L2, uint_t n );
+void luaG_inter_move( lua_State *L, lua_State *L2, uint_t n );
+
+// Lock for reference counter inc/dec locks (to be initialized by outside code)
+//
+extern MUTEX_T deep_lock;
+extern MUTEX_T mtid_lock;
+
+#endif
+    // TOOLS_H
diff --git a/src/luasocket/tcp.c b/src/luasocket/tcp.c
index 6b8a79b4bc555dad97aac8e6c82b01c170bbec47..80eb8b317a12921617eff4e1067c30dede596da9 100644
--- a/src/luasocket/tcp.c
+++ b/src/luasocket/tcp.c
@@ -30,6 +30,7 @@ static int meth_getpeername(lua_State *L);
 static int meth_shutdown(lua_State *L);
 static int meth_receive(lua_State *L);
 static int meth_accept(lua_State *L);
+static int meth_acceptfd(lua_State *L);
 static int meth_close(lua_State *L);
 static int meth_setoption(lua_State *L);
 static int meth_settimeout(lua_State *L);
@@ -42,6 +43,7 @@ static luaL_reg tcp[] = {
     {"__gc",        meth_close},
     {"__tostring",  auxiliar_tostring},
     {"accept",      meth_accept},
+    {"acceptfd",      meth_acceptfd},
     {"bind",        meth_bind},
     {"close",       meth_close},
     {"connect",     meth_connect},
@@ -185,6 +187,27 @@ static int meth_accept(lua_State *L)
     }
 }
 
+/*-------------------------------------------------------------------------*\
+* Waits for and returns a client object attempting connection to the
+* server object
+\*-------------------------------------------------------------------------*/
+static int meth_acceptfd(lua_State *L)
+{
+    p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1);
+    p_timeout tm = timeout_markstart(&server->tm);
+    t_socket sock;
+    int err = socket_accept(&server->sock, &sock, NULL, NULL, tm);
+    /* if successful, push client socket */
+    if (err == IO_DONE) {
+        lua_pushnumber(L, sock);
+        return 1;
+    } else {
+        lua_pushnil(L);
+        lua_pushstring(L, socket_strerror(err));
+        return 2;
+    }
+}
+
 /*-------------------------------------------------------------------------*\
 * Binds an object to an address 
 \*-------------------------------------------------------------------------*/
@@ -316,12 +339,19 @@ static int meth_settimeout(lua_State *L)
 static int global_create(lua_State *L)
 {
     t_socket sock;
-    const char *err = inet_trycreate(&sock, SOCK_STREAM);
+    const char *err = NULL;
+    int fd = luaL_optnumber(L, 1, -1);
+    if (fd < 1)
+        err = inet_trycreate(&sock, SOCK_STREAM);
+    else
+        sock = fd;
     /* try to allocate a system socket */
     if (!err) { 
         /* allocate tcp object */
         p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
-        /* set its type as master object */
+        if (fd >= 1)
+            auxiliar_setclass(L, "tcp{client}", -1);
+        else
         auxiliar_setclass(L, "tcp{master}", -1);
         /* initialize remaining structure fields */
         socket_setnonblocking(&sock);