mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
working on 1.14.4
This commit is contained in:
+47
-38
@@ -21,12 +21,12 @@ 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.
|
||||
]]
|
||||
local multi = require("multi")
|
||||
os.sleep=love.timer.sleep
|
||||
multi.drawF={}
|
||||
function multi:onDraw(func,i)
|
||||
i=i or 1
|
||||
table.insert(self.drawF,i,func)
|
||||
local multi, thread = require("multi").init()
|
||||
os.sleep = love.timer.sleep
|
||||
multi.drawF = {}
|
||||
function multi:onDraw(func, i)
|
||||
i = i or 1
|
||||
table.insert(self.drawF, i, func)
|
||||
end
|
||||
multi.OnKeyPressed = multi:newConnection()
|
||||
multi.OnKeyReleased = multi:newConnection()
|
||||
@@ -38,39 +38,48 @@ multi.OnDraw = multi:newConnection()
|
||||
multi.OnTextInput = multi:newConnection()
|
||||
multi.OnUpdate = multi:newConnection()
|
||||
multi.OnQuit = multi:newConnection()
|
||||
multi.OnPreLoad(function()
|
||||
local function Hook(func,conn)
|
||||
if love[func]~=nil then
|
||||
love[func] = Library.convert(love[func])
|
||||
love[func]:inject(function(...)
|
||||
conn:Fire(...)
|
||||
return {...}
|
||||
end,1)
|
||||
elseif love[func]==nil then
|
||||
love[func] = function(...)
|
||||
conn:Fire(...)
|
||||
multi.OnPreLoad(
|
||||
function()
|
||||
local function Hook(func, conn)
|
||||
if love[func] ~= nil then
|
||||
love[func] = Library.convert(love[func])
|
||||
love[func]:inject(
|
||||
function(...)
|
||||
conn:Fire(...)
|
||||
return {...}
|
||||
end,
|
||||
1
|
||||
)
|
||||
elseif love[func] == nil then
|
||||
love[func] = function(...)
|
||||
conn:Fire(...)
|
||||
end
|
||||
end
|
||||
end
|
||||
Hook("quit", multi.OnQuit)
|
||||
Hook("keypressed", multi.OnKeyPressed)
|
||||
Hook("keyreleased", multi.OnKeyReleased)
|
||||
Hook("mousepressed", multi.OnMousePressed)
|
||||
Hook("mousereleased", multi.OnMouseReleased)
|
||||
Hook("wheelmoved", multi.OnMouseWheelMoved)
|
||||
Hook("mousemoved", multi.OnMouseMoved)
|
||||
Hook("draw", multi.OnDraw)
|
||||
Hook("textinput", multi.OnTextInput)
|
||||
Hook("update", multi.OnUpdate)
|
||||
multi.OnDraw(
|
||||
function()
|
||||
for i = 1, #multi.drawF do
|
||||
love.graphics.setColor(255, 255, 255, 255)
|
||||
multi.drawF[i]()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
Hook("quit",multi.OnQuit)
|
||||
Hook("keypressed",multi.OnKeyPressed)
|
||||
Hook("keyreleased",multi.OnKeyReleased)
|
||||
Hook("mousepressed",multi.OnMousePressed)
|
||||
Hook("mousereleased",multi.OnMouseReleased)
|
||||
Hook("wheelmoved",multi.OnMouseWheelMoved)
|
||||
Hook("mousemoved",multi.OnMouseMoved)
|
||||
Hook("draw",multi.OnDraw)
|
||||
Hook("textinput",multi.OnTextInput)
|
||||
Hook("update",multi.OnUpdate)
|
||||
multi.OnDraw(function()
|
||||
for i=1,#multi.drawF do
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
multi.drawF[i]()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
multi.OnQuit(function()
|
||||
multi.Stop()
|
||||
love.event.quit()
|
||||
end)
|
||||
)
|
||||
multi.OnQuit(
|
||||
function()
|
||||
multi.Stop()
|
||||
love.event.quit()
|
||||
end
|
||||
)
|
||||
return multi
|
||||
|
||||
+54
-10
@@ -25,6 +25,9 @@ local bin = pcall(require,"bin")
|
||||
local multi = {}
|
||||
local clock = os.clock
|
||||
local thread = {}
|
||||
if not _G["$multi"] then
|
||||
_G["$multi"] = {multi=multi,thread=thread}
|
||||
end
|
||||
multi.Version = "13.1.0"
|
||||
multi._VERSION = "13.1.0"
|
||||
multi.stage = "stable"
|
||||
@@ -78,6 +81,9 @@ multi.PriorityTick=1 -- Between 1, 2 and 4
|
||||
multi.Priority=multi.Priority_High
|
||||
multi.threshold=256
|
||||
multi.threstimed=.001
|
||||
function multi.init()
|
||||
return _G["$multi"].multi,_G["$multi"].thread
|
||||
end
|
||||
function multi.queuefinal(self)
|
||||
self:Destroy()
|
||||
if self.Parent.Mainloop[#self.Parent.Mainloop] then
|
||||
@@ -758,6 +764,9 @@ function multi:newConnector()
|
||||
local c = {Type = "connector"}
|
||||
return c
|
||||
end
|
||||
local CRef = {
|
||||
Fire = function() end
|
||||
}
|
||||
function multi:newConnection(protect,func)
|
||||
local c={}
|
||||
c.callback = func
|
||||
@@ -802,11 +811,9 @@ function multi:newConnection(protect,func)
|
||||
return self
|
||||
end
|
||||
c.FConnect=c.fConnect
|
||||
function c:getConnection(name,ingore)
|
||||
if ingore then
|
||||
return self.connections[name] or {
|
||||
Fire=function() return end -- if the connection doesn't exist lets call all of them or silently ignore
|
||||
}
|
||||
function c:getConnection(name,ignore)
|
||||
if ignore then
|
||||
return self.connections[name] or CRef
|
||||
else
|
||||
return self.connections[name] or self
|
||||
end
|
||||
@@ -1489,6 +1496,10 @@ function thread.hold(n)
|
||||
thread._Requests()
|
||||
return coroutine.yield({"_hold_",n or function() return true end})
|
||||
end
|
||||
function thread.holdFor(sec,n)
|
||||
thread._Requests()
|
||||
return coroutine.yield({"_holdF_", sec, n or function() return true end})
|
||||
end
|
||||
function thread.skip(n)
|
||||
thread._Requests()
|
||||
if not n then n = 1 elseif n<1 then n = 1 end
|
||||
@@ -1652,6 +1663,13 @@ function multi.initThreads()
|
||||
threads[i].task = "hold"
|
||||
threads[i].__ready = false
|
||||
ret = nil
|
||||
elseif ret[1]=="_holdF_" then
|
||||
threads[i].sec = ret[2]
|
||||
threads[i].func = ret[3]
|
||||
threads[i].task = "holdF"
|
||||
threads[i].time = clock()
|
||||
threads[i].__ready = false
|
||||
ret = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1684,8 +1702,19 @@ function multi.initThreads()
|
||||
threads[i].task = ""
|
||||
threads[i].__ready = true
|
||||
end
|
||||
elseif threads[i].task == "holdF" then
|
||||
t0,t1,t2,t3,t4,t5,t6 = threads[i].func()
|
||||
if t0 then
|
||||
threads[i].task = ""
|
||||
threads[i].__ready = true
|
||||
elseif clock() - threads[i].time>=threads[i].sec then
|
||||
threads[i].task = ""
|
||||
threads[i].__ready = true
|
||||
t0 = nil
|
||||
t1 = "TIMEOUT"
|
||||
end
|
||||
end
|
||||
if threads[i].__ready then
|
||||
if threads[i] and threads[i].__ready then
|
||||
threads[i].__ready = false
|
||||
_,ret=coroutine.resume(threads[i].thread,t0,t1,t2,t3,t4,t5,t6)
|
||||
end
|
||||
@@ -1724,6 +1753,13 @@ function multi:threadloop()
|
||||
threads[i].task = "hold"
|
||||
threads[i].__ready = false
|
||||
ret = nil
|
||||
elseif ret[1]=="_holdF_" then
|
||||
threads[i].sec = ret[2]
|
||||
threads[i].func = ret[3]
|
||||
threads[i].task = "holdF"
|
||||
threads[i].time = clock()
|
||||
threads[i].__ready = false
|
||||
ret = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1753,6 +1789,17 @@ function multi:threadloop()
|
||||
threads[i].task = ""
|
||||
threads[i].__ready = true
|
||||
end
|
||||
elseif threads[i].task == "holdF" then
|
||||
t0,t1,t2,t3,t4,t5,t6 = threads[i].func()
|
||||
if t0 then
|
||||
threads[i].task = ""
|
||||
threads[i].__ready = true
|
||||
elseif clock() - threads[i].time>=threads[i].sec then
|
||||
threads[i].task = ""
|
||||
threads[i].__ready = true
|
||||
t0 = nil
|
||||
t1 = "TIMEOUT"
|
||||
end
|
||||
end
|
||||
if threads[i].__ready then
|
||||
threads[i].__ready = false
|
||||
@@ -2550,7 +2597,4 @@ end
|
||||
function multi:setDefualtStateFlag(opt)
|
||||
--
|
||||
end
|
||||
if not(multi.Version == "13.2.0" or multi.Version == "14.0.0") then
|
||||
_G.thread = thread
|
||||
end
|
||||
return multi, thread
|
||||
return multi
|
||||
|
||||
@@ -21,20 +21,26 @@ 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.
|
||||
]]
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
package.path = "?/init.lua;?.lua;" .. package.path
|
||||
local multi, thread = require("multi").init() -- get it all and have it on all lanes
|
||||
if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassel
|
||||
return {
|
||||
init = function()
|
||||
return multi.integration.GLOBAL, multi.integration.THREAD
|
||||
end
|
||||
}
|
||||
end
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=='\\' then
|
||||
return 'windows'
|
||||
if package.config:sub(1, 1) == "\\" then
|
||||
return "windows"
|
||||
else
|
||||
return 'unix'
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
-- Step 1 get lanes
|
||||
lanes=require("lanes").configure()
|
||||
local multi, thread = require("multi") -- get it all and have it on all lanes
|
||||
lanes = require("lanes").configure()
|
||||
multi.SystemThreads = {}
|
||||
local thread = thread
|
||||
multi.isMainThread=true
|
||||
multi.isMainThread = true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
@@ -45,40 +51,45 @@ end
|
||||
local __GlobalLinda = lanes.linda() -- handles global stuff
|
||||
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
|
||||
-- For convenience a GLOBAL table will be constructed to handle requests
|
||||
local GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
return __GlobalLinda:get(k)
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
__GlobalLinda:set(k,v)
|
||||
end,
|
||||
})
|
||||
local GLOBAL = {}
|
||||
setmetatable(
|
||||
GLOBAL,
|
||||
{
|
||||
__index = function(t, k)
|
||||
return __GlobalLinda:get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
__GlobalLinda:set(k, v)
|
||||
end
|
||||
}
|
||||
)
|
||||
-- Step 3 rewrite the thread methods to use Lindas
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
__GlobalLinda:set(name,val)
|
||||
local THREAD = {}
|
||||
function THREAD.set(name, val)
|
||||
__GlobalLinda:set(name, val)
|
||||
end
|
||||
function THREAD.get(name)
|
||||
__GlobalLinda:get(name)
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ''
|
||||
local str = ""
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
for i = 1, n do
|
||||
str = str .. "" .. strings[math.random(1, #strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
local function wait()
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(.001,randomString(12))
|
||||
__SleepingLinda:receive(.001, randomString(12))
|
||||
end
|
||||
repeat wait() until __GlobalLinda:get(name)
|
||||
repeat
|
||||
wait()
|
||||
until __GlobalLinda:get(name)
|
||||
return __GlobalLinda:get(name)
|
||||
end
|
||||
function THREAD.testFor(name,val,sym)
|
||||
function THREAD.testFor(name, val, sym)
|
||||
--
|
||||
end
|
||||
function THREAD.getCores()
|
||||
@@ -87,10 +98,10 @@ end
|
||||
function THREAD.getThreads()
|
||||
return GLOBAL.__THREADS__
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
if os.getOS() == "windows" then
|
||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function THREAD.kill() -- trigger the lane destruction
|
||||
error("Thread was killed!")
|
||||
@@ -107,40 +118,42 @@ Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lane
|
||||
however, a linda recieve will in fact be a idle wait! So we use that and wrap it in a nice package]]
|
||||
function THREAD.sleep(n)
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(n,randomString(12))
|
||||
__SleepingLinda:receive(n, randomString(12))
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
local function wait()
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(.001,randomString(12))
|
||||
__SleepingLinda:receive(.001, randomString(12))
|
||||
end
|
||||
repeat wait() until n()
|
||||
repeat
|
||||
wait()
|
||||
until n()
|
||||
end
|
||||
local rand = math.random(1,10000000)
|
||||
local rand = math.random(1, 10000000)
|
||||
-- Step 5 Basic Threads!
|
||||
local threads = {}
|
||||
local count = 1
|
||||
local started = false
|
||||
local livingThreads = {}
|
||||
function multi:newSystemThread(name,func,...)
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
multi.InitSystemThreadErrorHandler()
|
||||
rand = math.random(1,10000000)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
rand = math.random(1, 10000000)
|
||||
local c = {}
|
||||
local __self = c
|
||||
c.name = name
|
||||
c.Name = name
|
||||
c.Id = count
|
||||
livingThreads[count] = {true,name}
|
||||
livingThreads[count] = {true, name}
|
||||
local THREAD_ID = count
|
||||
count = count + 1
|
||||
c.Type="sthread"
|
||||
c.Type = "sthread"
|
||||
c.creationTime = os.clock()
|
||||
c.alive = true
|
||||
local THREAD_NAME=name
|
||||
local THREAD_NAME = name
|
||||
local function func2(...)
|
||||
local multi = require("multi")
|
||||
_G["THREAD_NAME"]=THREAD_NAME
|
||||
_G["THREAD_ID"]=THREAD_ID
|
||||
_G["THREAD_NAME"] = THREAD_NAME
|
||||
_G["THREAD_ID"] = THREAD_ID
|
||||
math.randomseed(rand)
|
||||
func(...)
|
||||
if _G.__Needs_Multi then
|
||||
@@ -148,52 +161,61 @@ function multi:newSystemThread(name,func,...)
|
||||
end
|
||||
THREAD.kill()
|
||||
end
|
||||
c.thread=lanes.gen("*", func2)(...)
|
||||
c.thread = lanes.gen("*", func2)(...)
|
||||
function c:kill()
|
||||
self.thread:cancel()
|
||||
multi.print("Thread: '"..self.name.."' has been stopped!")
|
||||
multi.print("Thread: '" .. self.name .. "' has been stopped!")
|
||||
self.alive = false
|
||||
end
|
||||
table.insert(multi.SystemThreads,c)
|
||||
table.insert(multi.SystemThreads, c)
|
||||
c.OnError = multi:newConnection()
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
return c
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
return c
|
||||
end
|
||||
multi.OnSystemThreadDied = multi:newConnection()
|
||||
function multi.InitSystemThreadErrorHandler()
|
||||
if started==true then return end
|
||||
if started == true then
|
||||
return
|
||||
end
|
||||
started = true
|
||||
multi:newThread("ThreadErrorHandler",function()
|
||||
local threads = multi.SystemThreads
|
||||
while true do
|
||||
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
|
||||
for i=#threads,1,-1 do
|
||||
local v,err,t=threads[i].thread:join(.001)
|
||||
if err then
|
||||
if err:find("Thread was killed!") then
|
||||
print(err)
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
threads[i].alive = false
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
elseif err:find("stack traceback") then
|
||||
print(err)
|
||||
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
|
||||
threads[i].alive = false
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
multi:newThread(
|
||||
"ThreadErrorHandler",
|
||||
function()
|
||||
local threads = multi.SystemThreads
|
||||
while true do
|
||||
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
|
||||
for i = #threads, 1, -1 do
|
||||
local v, err, t = threads[i].thread:join(.001)
|
||||
if err then
|
||||
if err:find("Thread was killed!") then
|
||||
print(err)
|
||||
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||
threads[i].alive = false
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
elseif err:find("stack traceback") then
|
||||
print(err)
|
||||
threads[i].OnError:Fire(threads[i], err, "Error in systemThread: '" .. threads[i].name .. "' <" .. err .. ">")
|
||||
threads[i].alive = false
|
||||
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
end
|
||||
multi.print("Integrated Lanes!")
|
||||
multi.integration={} -- for module creators
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
multi.integration = {} -- for module creators
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.shared")
|
||||
return {init=function() return GLOBAL, THREAD end}
|
||||
return {
|
||||
init = function()
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
}
|
||||
|
||||
+165
-138
@@ -22,15 +22,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi = require("multi.compat.love2d")
|
||||
if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassel
|
||||
return {
|
||||
init = function()
|
||||
return multi.integration.GLOBAL, multi.integration.THREAD
|
||||
end
|
||||
}
|
||||
end
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
function multi:getPlatform()
|
||||
return "love2d"
|
||||
end
|
||||
multi.integration={}
|
||||
multi.integration.love2d={}
|
||||
multi.integration.love2d.ThreadBase=[[
|
||||
multi.integration = {}
|
||||
multi.integration.love2d = {}
|
||||
multi.integration.love2d.ThreadBase =
|
||||
[[
|
||||
tab={...}
|
||||
__THREADID__=table.remove(tab,1)
|
||||
__THREADNAME__=table.remove(tab,1)
|
||||
@@ -198,27 +206,30 @@ updater:OnUpdate(__sync__)
|
||||
func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
|
||||
multi:mainloop()
|
||||
]]
|
||||
GLOBAL={} -- Allow main thread to interact with these objects as well
|
||||
GLOBAL = {} -- Allow main thread to interact with these objects as well
|
||||
_G.THREAD_ID = 0
|
||||
__proxy__={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
return __proxy__[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
__proxy__[k]=v
|
||||
for i=1,#__channels__ do
|
||||
if type(v)=="userdata" then
|
||||
__channels__[i]:push(v)
|
||||
else
|
||||
__channels__[i]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
||||
__proxy__ = {}
|
||||
setmetatable(
|
||||
GLOBAL,
|
||||
{
|
||||
__index = function(t, k)
|
||||
return __proxy__[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
__proxy__[k] = v
|
||||
for i = 1, #__channels__ do
|
||||
if type(v) == "userdata" then
|
||||
__channels__[i]:push(v)
|
||||
else
|
||||
__channels__[i]:push("SYNC " .. type(v) .. " " .. k .. " " .. resolveData(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
THREAD={} -- Allow main thread to interact with these objects as well
|
||||
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
||||
isMainThread=true
|
||||
}
|
||||
)
|
||||
THREAD = {} -- Allow main thread to interact with these objects as well
|
||||
multi.integration.love2d.mainChannel = love.thread.getChannel("__MainChan__")
|
||||
isMainThread = true
|
||||
multi.SystemThreads = {}
|
||||
function THREAD.getName()
|
||||
return __THREADNAME__
|
||||
@@ -227,119 +238,125 @@ function THREAD.getID()
|
||||
return THREAD_ID
|
||||
end
|
||||
function ToStr(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
if type(name) == "string" then
|
||||
tmp = tmp .. "[\""..name.."\"] = "
|
||||
tmp = tmp .. '["' .. name .. '"] = '
|
||||
else
|
||||
tmp = tmp .. "["..(name or "").."] = "
|
||||
tmp = tmp .. "[" .. (name or "") .. "] = "
|
||||
end
|
||||
end
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
|
||||
end
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
elseif type(val) == "string" then
|
||||
tmp = tmp .. string.format("%q", val)
|
||||
elseif type(val) == "boolean" then
|
||||
tmp = tmp .. (val and "true" or "false")
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
|
||||
end
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
elseif type(val) == "string" then
|
||||
tmp = tmp .. string.format("%q", val)
|
||||
elseif type(val) == "boolean" then
|
||||
tmp = tmp .. (val and "true" or "false")
|
||||
elseif type(val) == "function" then
|
||||
tmp = tmp .. "loadDump([===["..dump(val).."]===])"
|
||||
else
|
||||
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
||||
end
|
||||
return tmp
|
||||
tmp = tmp .. "loadDump([===[" .. dump(val) .. "]===])"
|
||||
else
|
||||
tmp = tmp .. '"[inserializeable datatype:' .. type(val) .. ']"'
|
||||
end
|
||||
return tmp
|
||||
end
|
||||
function resolveType(tp,d)
|
||||
if tp=="number" then
|
||||
function resolveType(tp, d)
|
||||
if tp == "number" then
|
||||
return tonumber(d)
|
||||
elseif tp=="bool" then
|
||||
return (d=="true")
|
||||
elseif tp=="function" then
|
||||
return loadDump("[==["..d.."]==]")
|
||||
elseif tp=="table" then
|
||||
return loadstring("return "..d)()
|
||||
elseif tp=="nil" then
|
||||
elseif tp == "bool" then
|
||||
return (d == "true")
|
||||
elseif tp == "function" then
|
||||
return loadDump("[==[" .. d .. "]==]")
|
||||
elseif tp == "table" then
|
||||
return loadstring("return " .. d)()
|
||||
elseif tp == "nil" then
|
||||
return nil
|
||||
else
|
||||
return d
|
||||
end
|
||||
end
|
||||
function resolveData(v)
|
||||
local data=""
|
||||
if type(v)=="table" then
|
||||
local data = ""
|
||||
if type(v) == "table" then
|
||||
return ToStr(v)
|
||||
elseif type(v)=="function" then
|
||||
elseif type(v) == "function" then
|
||||
return dump(v)
|
||||
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" or type(v)=="nil" then
|
||||
elseif type(v) == "string" or type(v) == "number" or type(v) == "bool" or type(v) == "nil" then
|
||||
return tostring(v)
|
||||
end
|
||||
return data
|
||||
end
|
||||
function loadDump(d)
|
||||
local s={}
|
||||
local s = {}
|
||||
for p in d:gmatch("(%d-)\\") do
|
||||
s[#s+1]=string.char(tonumber(p))
|
||||
s[#s + 1] = string.char(tonumber(p))
|
||||
end
|
||||
return loadstring(table.concat(s))
|
||||
end
|
||||
function dump(func)
|
||||
local code,t={},string.dump(func)
|
||||
for i=1,#t do
|
||||
code[#code+1]=string.byte(t:sub(i,i)).."\\"
|
||||
local code, t = {}, string.dump(func)
|
||||
for i = 1, #t do
|
||||
code[#code + 1] = string.byte(t:sub(i, i)) .. "\\"
|
||||
end
|
||||
return table.concat(code)
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ''
|
||||
local str = ""
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
for i = 1, n do
|
||||
str = str .. "" .. strings[math.random(1, #strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
local count = 1
|
||||
local livingThreads = {}
|
||||
function multi:newSystemThread(name,func,...) -- the main method
|
||||
function multi:newSystemThread(name, func, ...) -- the main method
|
||||
multi.InitSystemThreadErrorHandler()
|
||||
local c={}
|
||||
c.name=name
|
||||
local c = {}
|
||||
c.name = name
|
||||
c.Name = name
|
||||
c.Type="sthread"
|
||||
c.ID=c.name.."<ID|"..randomString(8)..">"
|
||||
c.Id=count
|
||||
c.Type = "sthread"
|
||||
c.ID = c.name .. "<ID|" .. randomString(8) .. ">"
|
||||
c.Id = count
|
||||
c.creationTime = os.clock()
|
||||
count = count + 1
|
||||
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
||||
livingThreads[count] = {true,name}
|
||||
c.thread = love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE", dump(func)))
|
||||
livingThreads[count] = {true, name}
|
||||
livingThreads[c.thread] = c
|
||||
c.OnError = multi:newConnection()
|
||||
c.thread:start(c.ID,c.name,THREAD_ID,...)
|
||||
c.thread:start(c.ID, c.name, THREAD_ID, ...)
|
||||
function c:kill()
|
||||
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
|
||||
multi.integration.GLOBAL["__DIEPLZ" .. self.ID .. "__"] = "__DIEPLZ" .. self.ID .. "__"
|
||||
end
|
||||
return c
|
||||
end
|
||||
function love.threaderror( thread, errorstr )
|
||||
multi.OnError:Fire(thread,errorstr)
|
||||
livingThreads[thread].OnError:Fire(threads[i],err,"Error in systemThread: '"..livingThreads[thread].name.."' <"..errorstr..">")
|
||||
multi.print("Error in systemThread: "..tostring(thread)..": "..errorstr)
|
||||
function love.threaderror(thread, errorstr)
|
||||
multi.OnError:Fire(thread, errorstr)
|
||||
livingThreads[thread].OnError:Fire(
|
||||
threads[i],
|
||||
err,
|
||||
"Error in systemThread: '" .. livingThreads[thread].name .. "' <" .. errorstr .. ">"
|
||||
)
|
||||
multi.print("Error in systemThread: " .. tostring(thread) .. ": " .. errorstr)
|
||||
end
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
GLOBAL[name]=val
|
||||
local THREAD = {}
|
||||
function THREAD.set(name, val)
|
||||
GLOBAL[name] = val
|
||||
end
|
||||
function THREAD.get(name)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
repeat multi:uManager() until GLOBAL[name]
|
||||
repeat
|
||||
multi:uManager()
|
||||
until GLOBAL[name]
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.getCores()
|
||||
@@ -349,85 +366,95 @@ function THREAD.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
repeat multi:uManager() until n()
|
||||
repeat
|
||||
multi:uManager()
|
||||
until n()
|
||||
end
|
||||
__channels__={}
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
updater=multi:newLoop(function(self)
|
||||
local data=multi.integration.love2d.mainChannel:pop()
|
||||
while data do
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
__proxy__[name]=resolveType(tp,d)
|
||||
for i=1,#__channels__ do
|
||||
-- send data to other threads
|
||||
if type(v)=="userdata" then
|
||||
__channels__[i]:push(v)
|
||||
else
|
||||
__channels__[i]:push("SYNC "..tp.." "..name.." "..d)
|
||||
__channels__ = {}
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
updater =
|
||||
multi:newLoop(
|
||||
function(self)
|
||||
local data = multi.integration.love2d.mainChannel:pop()
|
||||
while data do
|
||||
if type(data) == "string" then
|
||||
local cmd, tp, name, d = data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd == "SYNC" then
|
||||
__proxy__[name] = resolveType(tp, d)
|
||||
for i = 1, #__channels__ do
|
||||
-- send data to other threads
|
||||
if type(v) == "userdata" then
|
||||
__channels__[i]:push(v)
|
||||
else
|
||||
__channels__[i]:push("SYNC " .. tp .. " " .. name .. " " .. d)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif cmd=="NEWTHREAD" then
|
||||
__channels__[#__channels__+1]=love.thread.getChannel(tp)
|
||||
for k,v in pairs(__proxy__) do -- sync the global with each new thread
|
||||
if type(v)=="userdata" then
|
||||
__channels__[#__channels__]:push(v)
|
||||
else
|
||||
__channels__[#__channels__]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
||||
elseif cmd == "NEWTHREAD" then
|
||||
__channels__[#__channels__ + 1] = love.thread.getChannel(tp)
|
||||
for k, v in pairs(__proxy__) do -- sync the global with each new thread
|
||||
if type(v) == "userdata" then
|
||||
__channels__[#__channels__]:push(v)
|
||||
else
|
||||
__channels__[#__channels__]:push("SYNC " .. type(v) .. " " .. k .. " " .. resolveData(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
__proxy__[name] = data
|
||||
end
|
||||
else
|
||||
__proxy__[name]=data
|
||||
data = multi.integration.love2d.mainChannel:pop()
|
||||
end
|
||||
data=multi.integration.love2d.mainChannel:pop()
|
||||
end
|
||||
end)
|
||||
)
|
||||
multi.OnSystemThreadDied = multi:newConnection()
|
||||
local started = false
|
||||
function multi.InitSystemThreadErrorHandler()
|
||||
if started==true then return end
|
||||
if started == true then
|
||||
return
|
||||
end
|
||||
started = true
|
||||
multi:newThread("ThreadErrorHandler",function()
|
||||
local threads = multi.SystemThreads
|
||||
while true do
|
||||
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
|
||||
for i=#threads,1,-1 do
|
||||
local v,err,t=threads[i].thread:join(.001)
|
||||
if err then
|
||||
if err:find("Thread was killed!") then
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
else
|
||||
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
multi:newThread(
|
||||
"ThreadErrorHandler",
|
||||
function()
|
||||
local threads = multi.SystemThreads
|
||||
while true do
|
||||
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
|
||||
for i = #threads, 1, -1 do
|
||||
local v, err, t = threads[i].thread:join(.001)
|
||||
if err then
|
||||
if err:find("Thread was killed!") then
|
||||
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
else
|
||||
threads[i].OnError:Fire(threads[i], err, "Error in systemThread: '" .. threads[i].name .. "' <" .. err .. ">")
|
||||
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
end
|
||||
require("multi.integration.shared")
|
||||
multi.print("Integrated Love2d!")
|
||||
return {
|
||||
init=function(t)
|
||||
init = function(t)
|
||||
if t then
|
||||
if t.threadNamespace then
|
||||
multi.integration.THREADNAME=t.threadNamespace
|
||||
multi.integration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
|
||||
multi.integration.THREADNAME = t.threadNamespace
|
||||
multi.integration.love2d.ThreadBase:gsub("sThread", t.threadNamespace)
|
||||
end
|
||||
if t.globalNamespace then
|
||||
multi.integration.GLOBALNAME=t.globalNamespace
|
||||
multi.integration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
|
||||
multi.integration.GLOBALNAME = t.globalNamespace
|
||||
multi.integration.love2d.ThreadBase:gsub("GLOBAL", t.globalNamespace)
|
||||
end
|
||||
end
|
||||
return GLOBAL,THREAD
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
}
|
||||
|
||||
@@ -21,68 +21,133 @@ 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.
|
||||
]]
|
||||
|
||||
-- I DEMAND USAGE FOR LUVIT
|
||||
-- Cannot use discordia without my multitasking library (Which I love more that the luvit platform... then again i'm partial :P)
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
local function _INIT(luvitThread,timer)
|
||||
package.path = "?/init.lua;?.lua;" .. package.path
|
||||
local function _INIT(luvitThread, timer)
|
||||
-- lots of this stuff should be able to stay the same
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=='\\' then
|
||||
return 'windows'
|
||||
if package.config:sub(1, 1) == "\\" then
|
||||
return "windows"
|
||||
else
|
||||
return 'unix'
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
||||
local multi, thread = require("multi")
|
||||
isMainThread=true
|
||||
local multi, thread = require("multi").init()
|
||||
isMainThread = true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
function multi:getPlatform()
|
||||
return "luvit"
|
||||
end
|
||||
local multi=multi
|
||||
local multi = multi
|
||||
-- Step 2 set up the Global table... is this possible?
|
||||
local GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
--print("No Global table when using luvit integration!")
|
||||
return nil
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end,
|
||||
})
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
local GLOBAL = {}
|
||||
setmetatable(
|
||||
GLOBAL,
|
||||
{
|
||||
__index = function(t, k)
|
||||
--print("No Global table when using luvit integration!")
|
||||
return nil
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
}
|
||||
)
|
||||
local THREAD = {}
|
||||
function THREAD.set(name, val)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.get(name)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
local str = ""
|
||||
local strings = {
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"0",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z"
|
||||
}
|
||||
for i = 1, n do
|
||||
str = str .. "" .. strings[math.random(1, #strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.testFor(name,val,sym)
|
||||
function THREAD.testFor(name, val, sym)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
if os.getOS() == "windows" then
|
||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function THREAD.kill() -- trigger the thread destruction
|
||||
error("Thread was Killed!")
|
||||
@@ -95,34 +160,41 @@ local function _INIT(luvitThread,timer)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
-- Step 5 Basic Threads!
|
||||
local function entry(path,name,func,...)
|
||||
local timer = require'timer'
|
||||
local luvitThread = require'thread'
|
||||
package.path=path
|
||||
local function entry(path, name, func, ...)
|
||||
local timer = require "timer"
|
||||
local luvitThread = require "thread"
|
||||
package.path = path
|
||||
loadstring(func)(...)
|
||||
end
|
||||
function multi:newSystemThread(name,func,...)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
c.Type="sthread"
|
||||
c.thread={}
|
||||
c.func=string.dump(func)
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
local c = {}
|
||||
local __self = c
|
||||
c.name = name
|
||||
c.Type = "sthread"
|
||||
c.thread = {}
|
||||
c.func = string.dump(func)
|
||||
function c:kill()
|
||||
-- print("No Global table when using luvit integration!")
|
||||
end
|
||||
luvitThread.start(entry,package.path,name,c.func,...)
|
||||
luvitThread.start(entry, package.path, name, c.func, ...)
|
||||
return c
|
||||
end
|
||||
multi.print("Integrated Luvit!")
|
||||
multi.integration={} -- for module creators
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
multi.integration = {} -- for module creators
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.shared")
|
||||
-- Start the main mainloop... This allows you to process your multi objects, but the engine on the main thread will be limited to .001 or 1 millisecond sigh...
|
||||
local interval = timer.setInterval(1, function ()
|
||||
multi:uManager()
|
||||
end)
|
||||
local interval =
|
||||
timer.setInterval(
|
||||
1,
|
||||
function()
|
||||
multi:uManager()
|
||||
end
|
||||
)
|
||||
return multi
|
||||
end
|
||||
return {init=function(threadHandle,timerHandle) local multi = _INIT(threadHandle,timerHandle) return GLOBAL, THREAD end}
|
||||
return {init = function(threadHandle, timerHandle)
|
||||
local multi = _INIT(threadHandle, timerHandle)
|
||||
return GLOBAL, THREAD
|
||||
end}
|
||||
|
||||
@@ -21,33 +21,33 @@ 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.
|
||||
]]
|
||||
local multi, thread = require("multi")
|
||||
local multi, thread = require("multi").init()
|
||||
local net = require("net")
|
||||
local bin = require("bin")
|
||||
bin.setBitsInterface(infinabits) -- the bits interface does not work so well, another bug to fix
|
||||
|
||||
-- Commands that the master and node will respect, max of 256 commands
|
||||
local CMD_ERROR = 0x00
|
||||
local CMD_PING = 0x01
|
||||
local CMD_PONG = 0x02
|
||||
local CMD_QUEUE = 0x03
|
||||
local CMD_TASK = 0x04
|
||||
local CMD_INITNODE = 0x05
|
||||
local CMD_INITMASTER = 0x06
|
||||
local CMD_GLOBAL = 0x07
|
||||
local CMD_LOAD = 0x08
|
||||
local CMD_CALL = 0x09
|
||||
local CMD_REG = 0x0A
|
||||
local CMD_CONSOLE = 0x0B
|
||||
local CMD_ERROR = 0x00
|
||||
local CMD_PING = 0x01
|
||||
local CMD_PONG = 0x02
|
||||
local CMD_QUEUE = 0x03
|
||||
local CMD_TASK = 0x04
|
||||
local CMD_INITNODE = 0x05
|
||||
local CMD_INITMASTER = 0x06
|
||||
local CMD_GLOBAL = 0x07
|
||||
local CMD_LOAD = 0x08
|
||||
local CMD_CALL = 0x09
|
||||
local CMD_REG = 0x0A
|
||||
local CMD_CONSOLE = 0x0B
|
||||
|
||||
local char = string.char
|
||||
local byte = string.byte
|
||||
-- Process to hold all of the networkManager's muilt objects
|
||||
|
||||
-- Helper for piecing commands
|
||||
local function pieceCommand(cmd,...)
|
||||
local function pieceCommand(cmd, ...)
|
||||
local tab = {...}
|
||||
table.insert(tab,1,cmd)
|
||||
table.insert(tab, 1, cmd)
|
||||
return table.concat(tab)
|
||||
end
|
||||
|
||||
@@ -56,17 +56,17 @@ local Queue = {}
|
||||
Queue.__index = Queue
|
||||
function Queue:newQueue()
|
||||
local c = {}
|
||||
setmetatable(c,self)
|
||||
setmetatable(c, self)
|
||||
return c
|
||||
end
|
||||
function Queue:push(data)
|
||||
table.insert(self,data)
|
||||
table.insert(self, data)
|
||||
end
|
||||
function Queue:raw_push(data) -- Internal usage only
|
||||
table.insert(self,data)
|
||||
table.insert(self, data)
|
||||
end
|
||||
function Queue:pop()
|
||||
return table.remove(self,1)
|
||||
return table.remove(self, 1)
|
||||
end
|
||||
function Queue:peek()
|
||||
return self[1]
|
||||
@@ -79,39 +79,39 @@ multi.OnGUpdate = multi:newConnection()
|
||||
local function packData(data)
|
||||
-- returns the data that was sterilized
|
||||
local dat = bin.new()
|
||||
dat:addBlock(#type(data),1)
|
||||
dat:addBlock(#type(data), 1)
|
||||
dat:addBlock(type(data)) -- The type is first
|
||||
if type(data)=="table" then
|
||||
dat:addBlock(data,nil,"t")
|
||||
if type(data) == "table" then
|
||||
dat:addBlock(data, nil, "t")
|
||||
elseif type(data) == "userdata" then
|
||||
error("Cannot sterilize userdata!")
|
||||
elseif type(data) == "number" then
|
||||
dat:addBlock(data,nil,"d")
|
||||
dat:addBlock(data, nil, "d")
|
||||
elseif type(data) == "string" then
|
||||
dat:addBlock(#data,4)
|
||||
dat:addBlock(data,nil,"s")
|
||||
dat:addBlock(#data, 4)
|
||||
dat:addBlock(data, nil, "s")
|
||||
elseif type(data) == "boolean" then
|
||||
dat:addBlock(data,1)
|
||||
dat:addBlock(data, 1)
|
||||
elseif type(data) == "function" then
|
||||
dat:addBlock(data,nil,"f")
|
||||
dat:addBlock(data, nil, "f")
|
||||
end
|
||||
return dat.data
|
||||
end
|
||||
local function resolveData(data)
|
||||
-- returns the data that was sterilized
|
||||
local dat = bin.new(data)
|
||||
local tlen = dat:getBlock("n",1)
|
||||
local tp = dat:getBlock("s",tlen)
|
||||
if tp=="table" then
|
||||
local tlen = dat:getBlock("n", 1)
|
||||
local tp = dat:getBlock("s", tlen)
|
||||
if tp == "table" then
|
||||
return dat:getBlock("t")
|
||||
elseif tp=="number" then
|
||||
elseif tp == "number" then
|
||||
return dat:getBlock("d")
|
||||
elseif tp=="string" then
|
||||
local num = dat:getBlock("n",4)
|
||||
return dat:getBlock("s",num)
|
||||
elseif tp=="boolean" then
|
||||
return dat:getBlock("b",1)
|
||||
elseif tp=="function" then
|
||||
elseif tp == "string" then
|
||||
local num = dat:getBlock("n", 4)
|
||||
return dat:getBlock("s", num)
|
||||
elseif tp == "boolean" then
|
||||
return dat:getBlock("b", 1)
|
||||
elseif tp == "function" then
|
||||
return dat:getBlock("f")
|
||||
end
|
||||
end
|
||||
@@ -119,16 +119,19 @@ end
|
||||
-- internal global system
|
||||
local GLOBAL = {}
|
||||
local PROXY = {}
|
||||
setmetatable(GLOBAL,{
|
||||
__index = function(t,k)
|
||||
return PROXY[k]
|
||||
end,
|
||||
__newindex = function(t,k,v)
|
||||
local v = v
|
||||
PROXY[k] = v
|
||||
multi.OnGUpdate:Fire(k,packData(v))
|
||||
end
|
||||
})
|
||||
setmetatable(
|
||||
GLOBAL,
|
||||
{
|
||||
__index = function(t, k)
|
||||
return PROXY[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
local v = v
|
||||
PROXY[k] = v
|
||||
multi.OnGUpdate:Fire(k, packData(v))
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
-- In case you are unable to use broadcasting this can be used to help connect to nodes
|
||||
function multi:nodeManager(port)
|
||||
@@ -140,39 +143,48 @@ function multi:nodeManager(port)
|
||||
server.timeouts = {}
|
||||
server.OnNodeAdded = multi:newConnection()
|
||||
server.OnNodeRemoved = multi:newConnection()
|
||||
server.OnDataRecieved(function(server,data,cid,ip,port)
|
||||
local cmd = data:sub(1,1)
|
||||
if cmd == "R" then
|
||||
multi:newThread("Node Client Manager",function(loop)
|
||||
while true do
|
||||
if server.timeouts[cid]==true then
|
||||
server.OnNodeRemoved:Fire(server.nodes[cid])
|
||||
server.nodes[cid] = nil
|
||||
server.timeouts[cid] = nil
|
||||
thread.kill()
|
||||
else
|
||||
server.timeouts[cid] = true
|
||||
server:send(cid,"ping")
|
||||
server.OnDataRecieved(
|
||||
function(server, data, cid, ip, port)
|
||||
local cmd = data:sub(1, 1)
|
||||
if cmd == "R" then
|
||||
multi:newThread(
|
||||
"Node Client Manager",
|
||||
function(loop)
|
||||
while true do
|
||||
if server.timeouts[cid] == true then
|
||||
server.OnNodeRemoved:Fire(server.nodes[cid])
|
||||
server.nodes[cid] = nil
|
||||
server.timeouts[cid] = nil
|
||||
thread.kill()
|
||||
else
|
||||
server.timeouts[cid] = true
|
||||
server:send(cid, "ping")
|
||||
end
|
||||
thread.sleep(1)
|
||||
end
|
||||
end
|
||||
thread.sleep(1)
|
||||
)
|
||||
server.nodes[cid] = data:sub(2, -1)
|
||||
server.OnNodeAdded:Fire(server.nodes[cid])
|
||||
elseif cmd == "G" then
|
||||
server.OnNodeAdded(
|
||||
function(node)
|
||||
server:send(cid, node)
|
||||
end
|
||||
)
|
||||
server.OnNodeRemoved(
|
||||
function(node)
|
||||
server:send(cid, "R" .. node:match("(.-)|"))
|
||||
end
|
||||
)
|
||||
for i, v in pairs(server.nodes) do
|
||||
server:send(cid, v)
|
||||
end
|
||||
end)
|
||||
server.nodes[cid]=data:sub(2,-1)
|
||||
server.OnNodeAdded:Fire(server.nodes[cid])
|
||||
elseif cmd == "G" then
|
||||
server.OnNodeAdded(function(node)
|
||||
server:send(cid,node)
|
||||
end)
|
||||
server.OnNodeRemoved(function(node)
|
||||
server:send(cid,"R"..node:match("(.-)|"))
|
||||
end)
|
||||
for i,v in pairs(server.nodes) do
|
||||
server:send(cid,v)
|
||||
elseif cmd == "P" then
|
||||
server.timeouts[cid] = nil
|
||||
end
|
||||
elseif cmd == "P" then
|
||||
server.timeouts[cid] = nil
|
||||
end
|
||||
end)
|
||||
)
|
||||
end
|
||||
-- The main driving force of the network manager: Nodes
|
||||
function multi:newNode(settings)
|
||||
@@ -183,60 +195,69 @@ function multi:newNode(settings)
|
||||
local name = settings.name or multi.randomString(8)
|
||||
local node = {}
|
||||
node.name = name
|
||||
multi.OnError(function(i,error)
|
||||
node.OnError:Fire(node,error,node.server)
|
||||
end)
|
||||
multi.OnError(
|
||||
function(i, error)
|
||||
node.OnError:Fire(node, error, node.server)
|
||||
end
|
||||
)
|
||||
node.server = net:newUDPServer(0) -- hosts the node using the default port
|
||||
_, node.port = node.server.udp:getsockname()
|
||||
node.connections = net.ClientCache
|
||||
node.queue = Queue:newQueue()
|
||||
node.functions = bin.stream("RegisteredFunctions.dat",false)
|
||||
node.functions = bin.stream("RegisteredFunctions.dat", false)
|
||||
node.hasFuncs = {}
|
||||
node.OnError = multi:newConnection()
|
||||
node.OnError(function(node,err,master)
|
||||
multi.print("ERROR",err,node.name)
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#node.name,2)
|
||||
temp:addBlock(node.name)
|
||||
temp:addBlock(#err,2)
|
||||
temp:addBlock(err)
|
||||
for i,v in pairs(node.connections) do
|
||||
multi.print(i)
|
||||
v[1]:send(v[2],char(CMD_ERROR)..temp.data,v[3])
|
||||
node.OnError(
|
||||
function(node, err, master)
|
||||
multi.print("ERROR", err, node.name)
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#node.name, 2)
|
||||
temp:addBlock(node.name)
|
||||
temp:addBlock(#err, 2)
|
||||
temp:addBlock(err)
|
||||
for i, v in pairs(node.connections) do
|
||||
multi.print(i)
|
||||
v[1]:send(v[2], char(CMD_ERROR) .. temp.data, v[3])
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
if settings.managerDetails then
|
||||
local c = net:newTCPClient(settings.managerDetails[1],settings.managerDetails[2])
|
||||
local c = net:newTCPClient(settings.managerDetails[1], settings.managerDetails[2])
|
||||
if not c then
|
||||
multi.print("Cannot connect to the node manager! Ensuring broadcast is enabled!") settings.noBroadCast = false
|
||||
multi.print("Cannot connect to the node manager! Ensuring broadcast is enabled!")
|
||||
settings.noBroadCast = false
|
||||
else
|
||||
c.OnDataRecieved(function(self,data)
|
||||
if data == "ping" then
|
||||
self:send("P")
|
||||
c.OnDataRecieved(
|
||||
function(self, data)
|
||||
if data == "ping" then
|
||||
self:send("P")
|
||||
end
|
||||
end
|
||||
end)
|
||||
c:send("RNODE_"..name.."|"..net.getLocalIP().."|"..node.port)
|
||||
)
|
||||
c:send("RNODE_" .. name .. "|" .. net.getLocalIP() .. "|" .. node.port)
|
||||
end
|
||||
end
|
||||
if not settings.preload then
|
||||
if node.functions:getSize()~=0 then
|
||||
if node.functions:getSize() ~= 0 then
|
||||
multi.print("We have function(s) to preload!")
|
||||
local len = node.functions:getBlock("n",1)
|
||||
local name,func
|
||||
local len = node.functions:getBlock("n", 1)
|
||||
local name, func
|
||||
while len do
|
||||
name = node.functions:getBlock("s",len)
|
||||
len = node.functions:getBlock("n",2)
|
||||
func = node.functions:getBlock("s",len)
|
||||
name = node.functions:getBlock("s", len)
|
||||
len = node.functions:getBlock("n", 2)
|
||||
func = node.functions:getBlock("s", len)
|
||||
len = node.functions:read(1)
|
||||
_G[name]=resolveData(func)
|
||||
node.hasFuncs[name]=true
|
||||
if not len then break end
|
||||
_G[name] = resolveData(func)
|
||||
node.hasFuncs[name] = true
|
||||
if not len then
|
||||
break
|
||||
end
|
||||
len = byte(len)
|
||||
end
|
||||
end
|
||||
end
|
||||
function node:pushTo(name,data)
|
||||
node:sendTo(name,char(CMD_QUEUE)..packData(data))
|
||||
function node:pushTo(name, data)
|
||||
node:sendTo(name, char(CMD_QUEUE) .. packData(data))
|
||||
end
|
||||
function node:peek()
|
||||
return node.queue:peek()
|
||||
@@ -248,89 +269,100 @@ function multi:newNode(settings)
|
||||
local c = {}
|
||||
local conn = node.connections
|
||||
function c.print(...)
|
||||
local data = char(CMD_CONSOLE)..packData({...})
|
||||
for i,v in pairs(conn) do
|
||||
local data = char(CMD_CONSOLE) .. packData({...})
|
||||
for i, v in pairs(conn) do
|
||||
--print(i)
|
||||
v[1]:send(v[2],data,v[3])
|
||||
v[1]:send(v[2], data, v[3])
|
||||
end
|
||||
end
|
||||
-- function c:printTo()
|
||||
|
||||
|
||||
-- end
|
||||
return c
|
||||
end
|
||||
node.loadRate=1
|
||||
node.loadRate = 1
|
||||
-- Lets tell the network we are alive!
|
||||
node.server.OnDataRecieved(function(server,data,cid,ip,port)
|
||||
local cmd = byte(data:sub(1,1)) -- the first byte is the command
|
||||
local dat = data:sub(2,-1) -- the data that you want to read
|
||||
if cmd == CMD_PING then
|
||||
server:send(ip,char(CMD_PONG),port)
|
||||
elseif cmd == CMD_QUEUE then
|
||||
node.queue:push(resolveData(dat))
|
||||
elseif cmd == CMD_REG then
|
||||
if not settings.allowRemoteRegistering then
|
||||
multi.print(ip..": has attempted to register a function when it is currently not allowed!")
|
||||
return
|
||||
node.server.OnDataRecieved(
|
||||
function(server, data, cid, ip, port)
|
||||
local cmd = byte(data:sub(1, 1)) -- the first byte is the command
|
||||
local dat = data:sub(2, -1) -- the data that you want to read
|
||||
if cmd == CMD_PING then
|
||||
server:send(ip, char(CMD_PONG), port)
|
||||
elseif cmd == CMD_QUEUE then
|
||||
node.queue:push(resolveData(dat))
|
||||
elseif cmd == CMD_REG then
|
||||
if not settings.allowRemoteRegistering then
|
||||
multi.print(ip .. ": has attempted to register a function when it is currently not allowed!")
|
||||
return
|
||||
end
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n", 1)
|
||||
local name = temp:getBlock("s", len)
|
||||
if node.hasFuncs[name] then
|
||||
multi.print("Function already preloaded onto the node!")
|
||||
return
|
||||
end
|
||||
len = temp:getBlock("n", 2)
|
||||
local func = temp:getBlock("s", len)
|
||||
_G[name] = resolveData(func)
|
||||
node.functions:addBlock(dat)
|
||||
elseif cmd == CMD_CALL then
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n", 1)
|
||||
local name = temp:getBlock("s", len)
|
||||
len = temp:getBlock("n", 4)
|
||||
local args = temp:getBlock("s", len)
|
||||
_G[name](unpack(resolveData(args)))
|
||||
elseif cmd == CMD_TASK then
|
||||
local holder = bin.new(dat)
|
||||
local len = holder:getBlock("n", 4)
|
||||
local args = holder:getBlock("s", len)
|
||||
local len2 = holder:getBlock("n", 4)
|
||||
local func = holder:getBlock("s", len2)
|
||||
args = resolveData(args)
|
||||
func = resolveData(func)
|
||||
status, err = pcall(func, node, unpack(args))
|
||||
if not status then
|
||||
node.OnError:Fire(node, err, server)
|
||||
end
|
||||
elseif cmd == CMD_INITNODE then
|
||||
multi.print("Connected with another node!")
|
||||
node.connections[dat] = {server, ip, port}
|
||||
multi.OnGUpdate(
|
||||
function(k, v)
|
||||
server:send(ip, table.concat {char(CMD_GLOBAL), k, "|", v}, port)
|
||||
end
|
||||
)
|
||||
-- set this up
|
||||
elseif cmd == CMD_INITMASTER then
|
||||
multi.print("Connected to the master!", dat)
|
||||
node.connections[dat] = {server, ip, port}
|
||||
multi.OnGUpdate(
|
||||
function(k, v)
|
||||
server:send(ip, table.concat {char(CMD_GLOBAL), k, "|", v}, port)
|
||||
end
|
||||
)
|
||||
-- set this up
|
||||
multi:newTLoop(
|
||||
function()
|
||||
server:send(ip, char(CMD_LOAD) .. node.name .. "|" .. multi:getLoad(), port)
|
||||
end,
|
||||
node.loadRate
|
||||
)
|
||||
server:send(ip, char(CMD_LOAD) .. node.name .. "|" .. multi:getLoad(), port)
|
||||
server:send(ip, char(CMD_INITNODE) .. node.name, port)
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k, v = dat:match("(.-)|(.+)")
|
||||
PROXY[k] = resolveData(v)
|
||||
end
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n",1)
|
||||
local name = temp:getBlock("s",len)
|
||||
if node.hasFuncs[name] then
|
||||
multi.print("Function already preloaded onto the node!")
|
||||
return
|
||||
end
|
||||
len = temp:getBlock("n",2)
|
||||
local func = temp:getBlock("s",len)
|
||||
_G[name]=resolveData(func)
|
||||
node.functions:addBlock(dat)
|
||||
elseif cmd == CMD_CALL then
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n",1)
|
||||
local name = temp:getBlock("s",len)
|
||||
len = temp:getBlock("n",4)
|
||||
local args = temp:getBlock("s",len)
|
||||
_G[name](unpack(resolveData(args)))
|
||||
elseif cmd == CMD_TASK then
|
||||
local holder = bin.new(dat)
|
||||
local len = holder:getBlock("n",4)
|
||||
local args = holder:getBlock("s",len)
|
||||
local len2 = holder:getBlock("n",4)
|
||||
local func = holder:getBlock("s",len2)
|
||||
args = resolveData(args)
|
||||
func = resolveData(func)
|
||||
status, err = pcall(func,node,unpack(args))
|
||||
if not status then
|
||||
node.OnError:Fire(node,err,server)
|
||||
end
|
||||
elseif cmd == CMD_INITNODE then
|
||||
multi.print("Connected with another node!")
|
||||
node.connections[dat]={server,ip,port}
|
||||
multi.OnGUpdate(function(k,v)
|
||||
server:send(ip,table.concat{char(CMD_GLOBAL),k,"|",v},port)
|
||||
end)-- set this up
|
||||
elseif cmd == CMD_INITMASTER then
|
||||
multi.print("Connected to the master!",dat)
|
||||
node.connections[dat]={server,ip,port}
|
||||
multi.OnGUpdate(function(k,v)
|
||||
server:send(ip,table.concat{char(CMD_GLOBAL),k,"|",v},port)
|
||||
end)-- set this up
|
||||
multi:newTLoop(function()
|
||||
server:send(ip,char(CMD_LOAD)..node.name.."|"..multi:getLoad(),port)
|
||||
end,node.loadRate)
|
||||
server:send(ip,char(CMD_LOAD)..node.name.."|"..multi:getLoad(),port)
|
||||
server:send(ip,char(CMD_INITNODE)..node.name,port)
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k,v = dat:match("(.-)|(.+)")
|
||||
PROXY[k]=resolveData(v)
|
||||
end
|
||||
end)
|
||||
function node:sendTo(name,data)
|
||||
)
|
||||
function node:sendTo(name, data)
|
||||
local conn = node.connections[name]
|
||||
conn[1]:send(conn[2],data,conn[3])
|
||||
conn[1]:send(conn[2], data, conn[3])
|
||||
end
|
||||
if not settings.noBroadCast then
|
||||
node.server:broadcast("NODE_"..name)
|
||||
node.server:broadcast("NODE_" .. name)
|
||||
end
|
||||
return node
|
||||
end
|
||||
@@ -350,69 +382,76 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
master.connections = net.ClientCache -- Link to the client cache that is created on the net interface
|
||||
master.loads = {}
|
||||
master.timeouts = {}
|
||||
master.trigger = multi:newFunction(function(self,node)
|
||||
master.OnFirstNodeConnected:Fire(node)
|
||||
self:Pause()
|
||||
end)
|
||||
master.trigger =
|
||||
multi:newFunction(
|
||||
function(self, node)
|
||||
master.OnFirstNodeConnected:Fire(node)
|
||||
self:Pause()
|
||||
end
|
||||
)
|
||||
if settings.managerDetails then
|
||||
local client = net:newTCPClient(settings.managerDetails[1],settings.managerDetails[2])
|
||||
local client = net:newTCPClient(settings.managerDetails[1], settings.managerDetails[2])
|
||||
if not client then
|
||||
multi.print("Warning: Cannot connect to the node manager! Ensuring broadcast listening is enabled!") settings.noBroadCast = false
|
||||
multi.print("Warning: Cannot connect to the node manager! Ensuring broadcast listening is enabled!")
|
||||
settings.noBroadCast = false
|
||||
else
|
||||
client.OnDataRecieved(function(client,data)
|
||||
local cmd = data:sub(1,1)
|
||||
if cmd == "N" then
|
||||
print(data)
|
||||
local name,ip,port = data:match("(.-)|(.-)|(.+)")
|
||||
local c = net:newUDPClient(ip,port)
|
||||
net.OnCastedClientInfo:Fire(c,name,ip,port)master.connections[name]=c
|
||||
elseif cmd == "R" then
|
||||
local name = data:sub(2,-1)
|
||||
master.connections[name]=nil
|
||||
client.OnDataRecieved(
|
||||
function(client, data)
|
||||
local cmd = data:sub(1, 1)
|
||||
if cmd == "N" then
|
||||
print(data)
|
||||
local name, ip, port = data:match("(.-)|(.-)|(.+)")
|
||||
local c = net:newUDPClient(ip, port)
|
||||
net.OnCastedClientInfo:Fire(c, name, ip, port)
|
||||
master.connections[name] = c
|
||||
elseif cmd == "R" then
|
||||
local name = data:sub(2, -1)
|
||||
master.connections[name] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
client:send("G") -- init your connection as a master
|
||||
end
|
||||
end
|
||||
function master:doToAll(func)
|
||||
for i,v in pairs(master.connections) do
|
||||
func(i,v)
|
||||
for i, v in pairs(master.connections) do
|
||||
func(i, v)
|
||||
end
|
||||
end
|
||||
function master:register(name,node,func)
|
||||
function master:register(name, node, func)
|
||||
if not node then
|
||||
error("You must specify a node to execute a command on!")
|
||||
end
|
||||
local temp = bin.new()
|
||||
local fData = packData(func)
|
||||
temp:addBlock(CMD_REG,1)
|
||||
temp:addBlock(#name,1)
|
||||
temp:addBlock(name,#name)
|
||||
temp:addBlock(#fData,2)
|
||||
temp:addBlock(fData,#fData)
|
||||
master:sendTo(node,temp.data)
|
||||
temp:addBlock(CMD_REG, 1)
|
||||
temp:addBlock(#name, 1)
|
||||
temp:addBlock(name, #name)
|
||||
temp:addBlock(#fData, 2)
|
||||
temp:addBlock(fData, #fData)
|
||||
master:sendTo(node, temp.data)
|
||||
end
|
||||
function master:execute(name,node,...)
|
||||
function master:execute(name, node, ...)
|
||||
if not node then
|
||||
error("You must specify a node to execute a command on!")
|
||||
end
|
||||
if not name then
|
||||
error("You must specify a function name to call on the node!")
|
||||
end
|
||||
local args = packData{...}
|
||||
local args = packData {...}
|
||||
local name = name
|
||||
local node = node
|
||||
local temp, len, data
|
||||
temp = bin.new()
|
||||
temp:addBlock(CMD_CALL,1)
|
||||
temp:addBlock(#name,1)
|
||||
temp:addBlock(name,#name)
|
||||
temp:addBlock(#args,4)
|
||||
temp:addBlock(args,#args)
|
||||
master:sendTo(node,temp.data)
|
||||
temp:addBlock(CMD_CALL, 1)
|
||||
temp:addBlock(#name, 1)
|
||||
temp:addBlock(name, #name)
|
||||
temp:addBlock(#args, 4)
|
||||
temp:addBlock(args, #args)
|
||||
master:sendTo(node, temp.data)
|
||||
end
|
||||
function master:pushTo(name,data)
|
||||
master:sendTo(name,char(CMD_QUEUE)..packData(data))
|
||||
function master:pushTo(name, data)
|
||||
master:sendTo(name, char(CMD_QUEUE) .. packData(data))
|
||||
end
|
||||
function master:peek()
|
||||
return self.queue:peek()
|
||||
@@ -420,52 +459,68 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
function master:pop()
|
||||
return self.queue:pop()
|
||||
end
|
||||
function master:newNetworkThread(tname,func,name,...) -- If name specified then it will be sent to the specified node! Otherwise the least worked node will get the job
|
||||
function master:newNetworkThread(tname, func, name, ...) -- If name specified then it will be sent to the specified node! Otherwise the least worked node will get the job
|
||||
local fData = packData(func)
|
||||
local tab = {...}
|
||||
local aData = ""
|
||||
if #tab~=o then
|
||||
aData = (packData{...})
|
||||
if #tab ~= o then
|
||||
aData = (packData {...})
|
||||
else
|
||||
aData = (packData{"NO","ARGS"})
|
||||
aData = (packData {"NO", "ARGS"})
|
||||
end
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#aData,4)
|
||||
temp:addBlock(#aData, 4)
|
||||
local len = temp.data
|
||||
local temp2 = bin.new()
|
||||
temp2:addBlock(#fData,4)
|
||||
temp2:addBlock(#fData, 4)
|
||||
local len2 = temp2.data
|
||||
if not name then
|
||||
local name = self:getFreeNode()
|
||||
if not name then
|
||||
name = self:getRandomNode()
|
||||
end
|
||||
if name==nil then
|
||||
multi:newEvent(function() return name~=nil end):OnEvent(function(evnt)
|
||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
||||
evnt:Destroy()
|
||||
end):SetName("DelayedSendTask"):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self)
|
||||
self:Destroy()
|
||||
end)
|
||||
if name == nil then
|
||||
multi:newEvent(
|
||||
function()
|
||||
return name ~= nil
|
||||
end
|
||||
):OnEvent(
|
||||
function(evnt)
|
||||
self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData)
|
||||
evnt:Destroy()
|
||||
end
|
||||
):SetName("DelayedSendTask"):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(
|
||||
function(self)
|
||||
self:Destroy()
|
||||
end
|
||||
)
|
||||
else
|
||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
||||
self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData)
|
||||
end
|
||||
else
|
||||
local name = "NODE_"..name
|
||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
||||
local name = "NODE_" .. name
|
||||
self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData)
|
||||
end
|
||||
end
|
||||
function master:sendTo(name,data)
|
||||
if name:sub(1,5)~="NODE_" then
|
||||
name = "NODE_"..name
|
||||
function master:sendTo(name, data)
|
||||
if name:sub(1, 5) ~= "NODE_" then
|
||||
name = "NODE_" .. name
|
||||
end
|
||||
if self.connections[name]==nil then
|
||||
multi:newEvent(function() return self.connections[name]~=nil end):OnEvent(function(evnt)
|
||||
self.connections[name]:send(data)
|
||||
evnt:Destroy()
|
||||
end):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self)
|
||||
self:Destroy()
|
||||
end)
|
||||
if self.connections[name] == nil then
|
||||
multi:newEvent(
|
||||
function()
|
||||
return self.connections[name] ~= nil
|
||||
end
|
||||
):OnEvent(
|
||||
function(evnt)
|
||||
self.connections[name]:send(data)
|
||||
evnt:Destroy()
|
||||
end
|
||||
):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(
|
||||
function(self)
|
||||
self:Destroy()
|
||||
end
|
||||
)
|
||||
else
|
||||
self.connections[name]:send(data)
|
||||
end
|
||||
@@ -474,8 +529,8 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
local count = 0
|
||||
local min = math.huge
|
||||
local refO
|
||||
for i,v in pairs(master.loads) do
|
||||
if v<min then
|
||||
for i, v in pairs(master.loads) do
|
||||
if v < min then
|
||||
min = v
|
||||
refO = i
|
||||
end
|
||||
@@ -484,66 +539,77 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
end
|
||||
function master:getRandomNode()
|
||||
local list = {}
|
||||
for i,v in pairs(master.connections) do
|
||||
list[#list+1]=i:sub(6,-1)
|
||||
for i, v in pairs(master.connections) do
|
||||
list[#list + 1] = i:sub(6, -1)
|
||||
end
|
||||
return list[math.random(1,#list)]
|
||||
return list[math.random(1, #list)]
|
||||
end
|
||||
net.OnCastedClientInfo(function(client,name,ip,port)
|
||||
multi.OnGUpdate(function(k,v)
|
||||
client:send(table.concat{char(CMD_GLOBAL),k,"|",v})
|
||||
end)
|
||||
local nodename
|
||||
for i,v in pairs(master.connections) do
|
||||
nodename = i
|
||||
end
|
||||
client.OnClientReady(function()
|
||||
client:send(char(CMD_INITMASTER)..master.name) -- Tell the node that you are a master trying to connect
|
||||
if not settings.managerDetails then
|
||||
multi:newThread("Node Data Link Controller",function(loop)
|
||||
while true do
|
||||
if master.timeouts[name]==true then
|
||||
master.timeouts[name] = nil
|
||||
master.connections[name] = nil
|
||||
thread.kill()
|
||||
else
|
||||
master.timeouts[name] = true
|
||||
master:sendTo(name,char(CMD_PING))
|
||||
end
|
||||
thread.sleep(1)
|
||||
end
|
||||
end)
|
||||
end
|
||||
client.name = name
|
||||
client.OnDataRecieved(function(client,data)
|
||||
local cmd = byte(data:sub(1,1)) -- the first byte is the command
|
||||
local dat = data:sub(2,-1) -- the data that you want to read
|
||||
master.trigger(nodename)
|
||||
if cmd == CMD_ERROR then
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n",2)
|
||||
local node = temp:getBlock("s",len)
|
||||
len = temp:getBlock("n",2)
|
||||
local err = temp:getBlock("s",len)
|
||||
master.OnError:Fire(name,err)
|
||||
elseif cmd == CMD_CONSOLE then
|
||||
print(unpack(resolveData(dat)))
|
||||
elseif cmd == CMD_PONG then
|
||||
master.timeouts[client.name] = nil
|
||||
elseif cmd == CMD_INITNODE then
|
||||
master.OnNodeConnected:Fire(dat)
|
||||
elseif cmd == CMD_QUEUE then
|
||||
master.queue:push(resolveData(dat))
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k,v = dat:match("(.-)|(.+)")
|
||||
PROXY[k]=resolveData(v)
|
||||
elseif cmd == CMD_LOAD then
|
||||
local name,load = dat:match("(.-)|(.+)")
|
||||
master.loads[name]=tonumber(load)
|
||||
net.OnCastedClientInfo(
|
||||
function(client, name, ip, port)
|
||||
multi.OnGUpdate(
|
||||
function(k, v)
|
||||
client:send(table.concat {char(CMD_GLOBAL), k, "|", v})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
)
|
||||
local nodename
|
||||
for i, v in pairs(master.connections) do
|
||||
nodename = i
|
||||
end
|
||||
client.OnClientReady(
|
||||
function()
|
||||
client:send(char(CMD_INITMASTER) .. master.name) -- Tell the node that you are a master trying to connect
|
||||
if not settings.managerDetails then
|
||||
multi:newThread(
|
||||
"Node Data Link Controller",
|
||||
function(loop)
|
||||
while true do
|
||||
if master.timeouts[name] == true then
|
||||
master.timeouts[name] = nil
|
||||
master.connections[name] = nil
|
||||
thread.kill()
|
||||
else
|
||||
master.timeouts[name] = true
|
||||
master:sendTo(name, char(CMD_PING))
|
||||
end
|
||||
thread.sleep(1)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
client.name = name
|
||||
client.OnDataRecieved(
|
||||
function(client, data)
|
||||
local cmd = byte(data:sub(1, 1)) -- the first byte is the command
|
||||
local dat = data:sub(2, -1) -- the data that you want to read
|
||||
master.trigger(nodename)
|
||||
if cmd == CMD_ERROR then
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n", 2)
|
||||
local node = temp:getBlock("s", len)
|
||||
len = temp:getBlock("n", 2)
|
||||
local err = temp:getBlock("s", len)
|
||||
master.OnError:Fire(name, err)
|
||||
elseif cmd == CMD_CONSOLE then
|
||||
print(unpack(resolveData(dat)))
|
||||
elseif cmd == CMD_PONG then
|
||||
master.timeouts[client.name] = nil
|
||||
elseif cmd == CMD_INITNODE then
|
||||
master.OnNodeConnected:Fire(dat)
|
||||
elseif cmd == CMD_QUEUE then
|
||||
master.queue:push(resolveData(dat))
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k, v = dat:match("(.-)|(.+)")
|
||||
PROXY[k] = resolveData(v)
|
||||
elseif cmd == CMD_LOAD then
|
||||
local name, load = dat:match("(.-)|(.+)")
|
||||
master.loads[name] = tonumber(load)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
if not settings.noBroadCast then
|
||||
net:newCastedClients("NODE_(.+)") -- Searches for nodes and connects to them, the master.clients table will contain them by name
|
||||
end
|
||||
@@ -551,6 +617,8 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
end
|
||||
-- The init function that gets returned
|
||||
multi.print("Integrated Network Parallelism")
|
||||
return {init = function()
|
||||
return GLOBAL
|
||||
end}
|
||||
return {
|
||||
init = function()
|
||||
return GLOBAL
|
||||
end
|
||||
}
|
||||
|
||||
+390
-330
@@ -21,23 +21,23 @@ 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.
|
||||
]]
|
||||
multi, thread = require("multi")
|
||||
local multi, thread = require("multi").init()
|
||||
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||
local c={} -- where we will store our object
|
||||
c.name=name -- set the name this is important for the love2d side
|
||||
local c = {} -- where we will store our object
|
||||
c.name = name -- set the name this is important for the love2d side
|
||||
if love then -- check love
|
||||
if love.thread then -- make sure we can use the threading module
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
self.chan = love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
function self:push(v) -- push to the channel
|
||||
local tab
|
||||
if type(v)=="table" then
|
||||
if type(v) == "table" then
|
||||
tab = {}
|
||||
for i,c in pairs(v) do
|
||||
if type(c)=="function" then
|
||||
tab[i]="\1"..string.dump(c)
|
||||
for i, c in pairs(v) do
|
||||
if type(c) == "function" then
|
||||
tab[i] = "\1" .. string.dump(c)
|
||||
else
|
||||
tab[i]=c
|
||||
tab[i] = c
|
||||
end
|
||||
end
|
||||
self.chan:push(tab)
|
||||
@@ -46,19 +46,21 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
end
|
||||
end
|
||||
function self:pop() -- pop from the channel
|
||||
local v=self.chan:pop()
|
||||
if not v then return end
|
||||
if type(v)=="table" then
|
||||
local v = self.chan:pop()
|
||||
if not v then
|
||||
return
|
||||
end
|
||||
if type(v) == "table" then
|
||||
tab = {}
|
||||
for i,c in pairs(v) do
|
||||
if type(c)=="string" then
|
||||
if c:sub(1,1)=="\1" then
|
||||
tab[i]=loadstring(c:sub(2,-1))
|
||||
for i, c in pairs(v) do
|
||||
if type(c) == "string" then
|
||||
if c:sub(1, 1) == "\1" then
|
||||
tab[i] = loadstring(c:sub(2, -1))
|
||||
else
|
||||
tab[i]=c
|
||||
tab[i] = c
|
||||
end
|
||||
else
|
||||
tab[i]=c
|
||||
tab[i] = c
|
||||
end
|
||||
end
|
||||
return tab
|
||||
@@ -67,19 +69,21 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
end
|
||||
end
|
||||
function self:peek()
|
||||
local v=self.chan:peek()
|
||||
if not v then return end
|
||||
if type(v)=="table" then
|
||||
local v = self.chan:peek()
|
||||
if not v then
|
||||
return
|
||||
end
|
||||
if type(v) == "table" then
|
||||
tab = {}
|
||||
for i,c in pairs(v) do
|
||||
if type(c)=="string" then
|
||||
if c:sub(1,1)=="\1" then
|
||||
tab[i]=loadstring(c:sub(2,-1))
|
||||
for i, c in pairs(v) do
|
||||
if type(c) == "string" then
|
||||
if c:sub(1, 1) == "\1" then
|
||||
tab[i] = loadstring(c:sub(2, -1))
|
||||
else
|
||||
tab[i]=c
|
||||
tab[i] = c
|
||||
end
|
||||
else
|
||||
tab[i]=c
|
||||
tab[i] = c
|
||||
end
|
||||
end
|
||||
return tab
|
||||
@@ -87,7 +91,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
return self.chan:pop()
|
||||
end
|
||||
end
|
||||
GLOBAL[self.name]=self -- send the object to the thread through the global interface
|
||||
GLOBAL[self.name] = self -- send the object to the thread through the global interface
|
||||
return self -- return the object
|
||||
end
|
||||
return c
|
||||
@@ -95,12 +99,12 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||
end
|
||||
else
|
||||
c.linda=lanes.linda() -- lanes is a bit easier, create the linda on the main thread
|
||||
c.linda = lanes.linda() -- lanes is a bit easier, create the linda on the main thread
|
||||
function c:push(v) -- push to the queue
|
||||
self.linda:send("Q",v)
|
||||
self.linda:send("Q", v)
|
||||
end
|
||||
function c:pop() -- pop the queue
|
||||
return ({self.linda:receive(0,"Q")})[2]
|
||||
return ({self.linda:receive(0, "Q")})[2]
|
||||
end
|
||||
function c:peek()
|
||||
return self.linda:get("Q")
|
||||
@@ -108,41 +112,44 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
function c:init() -- mimic the feature that love2d requires, so code can be consistent
|
||||
return self
|
||||
end
|
||||
multi.integration.GLOBAL[name]=c -- send the object to the thread through the global interface
|
||||
multi.integration.GLOBAL[name] = c -- send the object to the thread through the global interface
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedConnection(name,protect)
|
||||
local c={}
|
||||
function multi:newSystemThreadedConnection(name, protect)
|
||||
local c = {}
|
||||
c.name = name or error("You must provide a name for the connection object!")
|
||||
c.protect = protect or false
|
||||
c.idle = nil
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local connSync = multi:newSystemThreadedQueue(c.name.."_CONN_SYNC")
|
||||
local connFire = multi:newSystemThreadedQueue(c.name.."_CONN_FIRE")
|
||||
local sThread = multi.integration.THREAD
|
||||
local GLOBAL = multi.integration.GLOBAL
|
||||
local connSync = multi:newSystemThreadedQueue(c.name .. "_CONN_SYNC")
|
||||
local connFire = multi:newSystemThreadedQueue(c.name .. "_CONN_FIRE")
|
||||
function c:init()
|
||||
local multi = require("multi")
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
GLOBAL = _G.GLOBAL
|
||||
sThread = _G.sThread
|
||||
end
|
||||
local conn = {}
|
||||
conn.obj = multi:newConnection()
|
||||
setmetatable(conn,{
|
||||
__call=function(self,...)
|
||||
return self:connect(...)
|
||||
end
|
||||
})
|
||||
setmetatable(
|
||||
conn,
|
||||
{
|
||||
__call = function(self, ...)
|
||||
return self:connect(...)
|
||||
end
|
||||
}
|
||||
)
|
||||
local ID = sThread.getID()
|
||||
local sync = sThread.waitFor(self.name.."_CONN_SYNC"):init()
|
||||
local fire = sThread.waitFor(self.name.."_CONN_FIRE"):init()
|
||||
local sync = sThread.waitFor(self.name .. "_CONN_SYNC"):init()
|
||||
local fire = sThread.waitFor(self.name .. "_CONN_FIRE"):init()
|
||||
local connections = {}
|
||||
if not multi.isMainThread then
|
||||
connections = {0}
|
||||
end
|
||||
sync:push{"INIT",ID} -- Register this as an active connection!
|
||||
sync:push {"INIT", ID} -- Register this as an active connection!
|
||||
function conn:connect(func)
|
||||
return self.obj(func)
|
||||
end
|
||||
@@ -153,415 +160,468 @@ function multi:newSystemThreadedConnection(name,protect)
|
||||
self.obj:Remove()
|
||||
end
|
||||
function conn:Fire(...)
|
||||
for i = 1,#connections do
|
||||
fire:push{connections[i],ID,{...}}
|
||||
for i = 1, #connections do
|
||||
fire:push {connections[i], ID, {...}}
|
||||
end
|
||||
end
|
||||
function conn:FireTo(to,...)
|
||||
function conn:FireTo(to, ...)
|
||||
local good = false
|
||||
for i = 1,#connections do
|
||||
if connections[i]==to then
|
||||
for i = 1, #connections do
|
||||
if connections[i] == to then
|
||||
good = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not good then return multi.print("NonExisting Connection!") end
|
||||
fire:push{to,ID,{...}}
|
||||
if not good then
|
||||
return multi.print("NonExisting Connection!")
|
||||
end
|
||||
fire:push {to, ID, {...}}
|
||||
end
|
||||
-- FIRE {TO,FROM,{ARGS}}
|
||||
local data
|
||||
local clock = os.clock
|
||||
conn.OnConnectionAdded = multi:newConnection()
|
||||
multi:newLoop(function()
|
||||
data = fire:peek()
|
||||
if type(data)=="table" and data[1]==ID then
|
||||
if data[2]==ID and conn.IgnoreSelf then
|
||||
multi:newLoop(
|
||||
function()
|
||||
data = fire:peek()
|
||||
if type(data) == "table" and data[1] == ID then
|
||||
if data[2] == ID and conn.IgnoreSelf then
|
||||
fire:pop()
|
||||
return
|
||||
end
|
||||
fire:pop()
|
||||
return
|
||||
conn.obj:Fire(unpack(data[3]))
|
||||
end
|
||||
fire:pop()
|
||||
conn.obj:Fire(unpack(data[3]))
|
||||
end
|
||||
data = sync:peek()
|
||||
if data~=nil and data[1]=="SYNCA" and data[2]==ID then
|
||||
sync:pop()
|
||||
multi.nextStep(function()
|
||||
conn.OnConnectionAdded:Fire(data[3])
|
||||
end)
|
||||
table.insert(connections,data[3])
|
||||
end
|
||||
if type(data)=="table" and data[1]=="SYNCR" and data[2]==ID then
|
||||
sync:pop()
|
||||
for i=1,#connections do
|
||||
if connections[i] == data[3] then
|
||||
table.remove(connections,i)
|
||||
data = sync:peek()
|
||||
if data ~= nil and data[1] == "SYNCA" and data[2] == ID then
|
||||
sync:pop()
|
||||
multi.nextStep(
|
||||
function()
|
||||
conn.OnConnectionAdded:Fire(data[3])
|
||||
end
|
||||
)
|
||||
table.insert(connections, data[3])
|
||||
end
|
||||
if type(data) == "table" and data[1] == "SYNCR" and data[2] == ID then
|
||||
sync:pop()
|
||||
for i = 1, #connections do
|
||||
if connections[i] == data[3] then
|
||||
table.remove(connections, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end):setName("STConn.syncer")
|
||||
):setName("STConn.syncer")
|
||||
return conn
|
||||
end
|
||||
local cleanUp = {}
|
||||
multi.OnSystemThreadDied(function(ThreadID)
|
||||
for i=1,#syncs do
|
||||
connSync:push{"SYNCR",syncs[i],ThreadID}
|
||||
end
|
||||
cleanUp[ThreadID] = true
|
||||
end)
|
||||
multi:newThread(c.name.." Connection-Handler",function()
|
||||
local data
|
||||
local clock = os.clock
|
||||
local syncs = {}
|
||||
while true do
|
||||
if not c.idle then
|
||||
thread.sleep(.5)
|
||||
else
|
||||
if clock() - c.idle >= 15 then
|
||||
c.idle = nil
|
||||
end
|
||||
thread.skip()
|
||||
multi.OnSystemThreadDied(
|
||||
function(ThreadID)
|
||||
for i = 1, #syncs do
|
||||
connSync:push {"SYNCR", syncs[i], ThreadID}
|
||||
end
|
||||
data = connSync:peek()
|
||||
if data~= nil and data[1]=="INIT" then
|
||||
connSync:pop()
|
||||
c.idle = clock()
|
||||
table.insert(syncs,data[2])
|
||||
for i=1,#syncs do
|
||||
connSync:push{"SYNCA",syncs[i],data[2]}
|
||||
cleanUp[ThreadID] = true
|
||||
end
|
||||
)
|
||||
multi:newThread(
|
||||
c.name .. " Connection-Handler",
|
||||
function()
|
||||
local data
|
||||
local clock = os.clock
|
||||
local syncs = {}
|
||||
while true do
|
||||
if not c.idle then
|
||||
thread.sleep(.5)
|
||||
else
|
||||
if clock() - c.idle >= 15 then
|
||||
c.idle = nil
|
||||
end
|
||||
thread.skip()
|
||||
end
|
||||
data = connSync:peek()
|
||||
if data ~= nil and data[1] == "INIT" then
|
||||
connSync:pop()
|
||||
c.idle = clock()
|
||||
table.insert(syncs, data[2])
|
||||
for i = 1, #syncs do
|
||||
connSync:push {"SYNCA", syncs[i], data[2]}
|
||||
end
|
||||
end
|
||||
data = connFire:peek()
|
||||
if data ~= nil and cleanUp[data[1]] then
|
||||
local meh = data[1]
|
||||
connFire:pop() -- lets remove dead thread stuff
|
||||
multi:newAlarm(15):OnRing(
|
||||
function(a)
|
||||
cleanUp[meh] = nil
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
data = connFire:peek()
|
||||
if data~=nil and cleanUp[data[1]] then
|
||||
local meh = data[1]
|
||||
connFire:pop() -- lets remove dead thread stuff
|
||||
multi:newAlarm(15):OnRing(function(a)
|
||||
cleanUp[meh] = nil
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
GLOBAL[c.name]=c
|
||||
)
|
||||
GLOBAL[c.name] = c
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:SystemThreadedBenchmark(n)
|
||||
n=n or 1
|
||||
local cores=multi.integration.THREAD.getCores()
|
||||
local queue=multi:newSystemThreadedQueue("THREAD_BENCH_QUEUE"):init()
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
n = n or 1
|
||||
local cores = multi.integration.THREAD.getCores()
|
||||
local queue = multi:newSystemThreadedQueue("THREAD_BENCH_QUEUE"):init()
|
||||
local sThread = multi.integration.THREAD
|
||||
local GLOBAL = multi.integration.GLOBAL
|
||||
local c = {}
|
||||
for i=1,cores do
|
||||
multi:newSystemThread("STHREAD_BENCH",function(n)
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end -- we cannot have upvalues... in love2d globals, not locals must be used
|
||||
queue=sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
|
||||
multi:benchMark(n):OnBench(function(self,count)
|
||||
queue:push(count)
|
||||
sThread.kill()
|
||||
error("Thread was killed!")
|
||||
end)
|
||||
multi:mainloop()
|
||||
end,n)
|
||||
for i = 1, cores do
|
||||
multi:newSystemThread(
|
||||
"STHREAD_BENCH",
|
||||
function(n)
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform() == "love2d" then
|
||||
GLOBAL = _G.GLOBAL
|
||||
sThread = _G.sThread
|
||||
end -- we cannot have upvalues... in love2d globals, not locals must be used
|
||||
queue = sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
|
||||
multi:benchMark(n):OnBench(
|
||||
function(self, count)
|
||||
queue:push(count)
|
||||
sThread.kill()
|
||||
error("Thread was killed!")
|
||||
end
|
||||
)
|
||||
multi:mainloop()
|
||||
end,
|
||||
n
|
||||
)
|
||||
end
|
||||
multi:newThread("THREAD_BENCH",function()
|
||||
local count = 0
|
||||
local cc = 0
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local dat = queue:pop()
|
||||
if dat then
|
||||
cc=cc+1
|
||||
count = count + dat
|
||||
if cc == cores then
|
||||
c.OnBench:Fire(count)
|
||||
thread.kill()
|
||||
multi:newThread(
|
||||
"THREAD_BENCH",
|
||||
function()
|
||||
local count = 0
|
||||
local cc = 0
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local dat = queue:pop()
|
||||
if dat then
|
||||
cc = cc + 1
|
||||
count = count + dat
|
||||
if cc == cores then
|
||||
c.OnBench:Fire(count)
|
||||
thread.kill()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
c.OnBench = multi:newConnection()
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedConsole(name)
|
||||
local c={}
|
||||
local c = {}
|
||||
c.name = name
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local sThread = multi.integration.THREAD
|
||||
local GLOBAL = multi.integration.GLOBAL
|
||||
function c:init()
|
||||
_G.__Needs_Multi = true
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
if multi:getPlatform() == "love2d" then
|
||||
GLOBAL = _G.GLOBAL
|
||||
sThread = _G.sThread
|
||||
end
|
||||
local cc={}
|
||||
local cc = {}
|
||||
if multi.isMainThread then
|
||||
if GLOBAL["__SYSTEM_CONSOLE__"] then
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
||||
else
|
||||
cc.stream = multi:newSystemThreadedQueue("__SYSTEM_CONSOLE__"):init()
|
||||
multi:newLoop(function()
|
||||
local data = cc.stream:pop()
|
||||
if data then
|
||||
local dat = table.remove(data,1)
|
||||
if dat=="w" then
|
||||
io.write(unpack(data))
|
||||
elseif dat=="p" then
|
||||
print(unpack(data))
|
||||
multi:newLoop(
|
||||
function()
|
||||
local data = cc.stream:pop()
|
||||
if data then
|
||||
local dat = table.remove(data, 1)
|
||||
if dat == "w" then
|
||||
io.write(unpack(data))
|
||||
elseif dat == "p" then
|
||||
print(unpack(data))
|
||||
end
|
||||
end
|
||||
end
|
||||
end):setName("ST.consoleSyncer")
|
||||
):setName("ST.consoleSyncer")
|
||||
end
|
||||
else
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
||||
end
|
||||
function cc:write(msg)
|
||||
self.stream:push({"w",tostring(msg)})
|
||||
self.stream:push({"w", tostring(msg)})
|
||||
end
|
||||
function cc:print(...)
|
||||
local tab = {...}
|
||||
for i=1,#tab do
|
||||
tab[i]=tostring(tab[i])
|
||||
for i = 1, #tab do
|
||||
tab[i] = tostring(tab[i])
|
||||
end
|
||||
self.stream:push({"p",unpack(tab)})
|
||||
self.stream:push({"p", unpack(tab)})
|
||||
end
|
||||
return cc
|
||||
end
|
||||
GLOBAL[c.name]=c
|
||||
GLOBAL[c.name] = c
|
||||
return c
|
||||
end
|
||||
-- NEEDS WORK
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c={}
|
||||
c.name=name -- set the name this is important for identifying what is what
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local c = {}
|
||||
c.name = name -- set the name this is important for identifying what is what
|
||||
local sThread = multi.integration.THREAD
|
||||
local GLOBAL = multi.integration.GLOBAL
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
_G.__Needs_Multi = true
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
if multi:getPlatform() == "love2d" then
|
||||
GLOBAL = _G.GLOBAL
|
||||
sThread = _G.sThread
|
||||
end
|
||||
local cc={}
|
||||
cc.tab={}
|
||||
local cc = {}
|
||||
cc.tab = {}
|
||||
if multi.isMainThread then
|
||||
if not GLOBAL[self.name.."_Tabled_Connection"] then
|
||||
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
|
||||
if not GLOBAL[self.name .. "_Tabled_Connection"] then
|
||||
cc.conn = multi:newSystemThreadedConnection(self.name .. "_Tabled_Connection"):init()
|
||||
end
|
||||
else
|
||||
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
|
||||
cc.conn = sThread.waitFor(self.name .. "_Tabled_Connection"):init()
|
||||
end
|
||||
function cc:waitFor(name)
|
||||
repeat multi:uManager() until tab[name]~=nil
|
||||
repeat
|
||||
multi:uManager()
|
||||
until tab[name] ~= nil
|
||||
return tab[name]
|
||||
end
|
||||
local link = cc
|
||||
cc.conn(function(k,v)
|
||||
link.tab[k]=v
|
||||
end)
|
||||
setmetatable(cc,{
|
||||
__index=function(t,k)
|
||||
return t.tab[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
t.tab[k]=v
|
||||
t.conn:Fire(k,v)
|
||||
cc.conn(
|
||||
function(k, v)
|
||||
link.tab[k] = v
|
||||
end
|
||||
})
|
||||
)
|
||||
setmetatable(
|
||||
cc,
|
||||
{
|
||||
__index = function(t, k)
|
||||
return t.tab[k]
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
t.tab[k] = v
|
||||
t.conn:Fire(k, v)
|
||||
end
|
||||
}
|
||||
)
|
||||
return cc
|
||||
end
|
||||
GLOBAL[c.name]=c
|
||||
GLOBAL[c.name] = c
|
||||
return c
|
||||
end
|
||||
local jobqueuecount = 0
|
||||
local jqueues = {}
|
||||
function multi:newSystemThreadedJobQueue(a,b)
|
||||
jobqueuecount=jobqueuecount+1
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local sThread=multi.integration.THREAD
|
||||
function multi:newSystemThreadedJobQueue(a, b)
|
||||
jobqueuecount = jobqueuecount + 1
|
||||
local GLOBAL = multi.integration.GLOBAL
|
||||
local sThread = multi.integration.THREAD
|
||||
local c = {}
|
||||
c.numberofcores = 4
|
||||
c.idle = nil
|
||||
c.name = "SYSTEM_THREADED_JOBQUEUE_"..jobqueuecount
|
||||
c.name = "SYSTEM_THREADED_JOBQUEUE_" .. jobqueuecount
|
||||
-- This is done to keep backwards compatibility for older code
|
||||
if type(a)=="string" and not(b) then
|
||||
if type(a) == "string" and not (b) then
|
||||
c.name = a
|
||||
elseif type(a)=="number" and not (b) then
|
||||
elseif type(a) == "number" and not (b) then
|
||||
c.numberofcores = a
|
||||
elseif type(a)=="string" and type(b)=="number" then
|
||||
elseif type(a) == "string" and type(b) == "number" then
|
||||
c.name = a
|
||||
c.numberofcores = b
|
||||
elseif type(a)=="number" and type(b)=="string" then
|
||||
elseif type(a) == "number" and type(b) == "string" then
|
||||
c.name = b
|
||||
c.numberofcores = a
|
||||
end
|
||||
if jqueues[c.name] then
|
||||
error("A job queue by the name: "..c.name.." already exists!")
|
||||
error("A job queue by the name: " .. c.name .. " already exists!")
|
||||
end
|
||||
jqueues[c.name] = true
|
||||
c.isReady = false
|
||||
c.jobnum=1
|
||||
c.jobnum = 1
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
local queueIN = self:newSystemThreadedQueue("QUEUE_IN_"..c.name):init()
|
||||
local queueCC = self:newSystemThreadedQueue("QUEUE_CC_"..c.name):init()
|
||||
local queueREG = self:newSystemThreadedQueue("QUEUE_REG_"..c.name):init()
|
||||
local queueJD = self:newSystemThreadedQueue("QUEUE_JD_"..c.name):init()
|
||||
local queueDA = self:newSystemThreadedQueue("QUEUE_DA_"..c.name):init()
|
||||
local queueIN = self:newSystemThreadedQueue("QUEUE_IN_" .. c.name):init()
|
||||
local queueCC = self:newSystemThreadedQueue("QUEUE_CC_" .. c.name):init()
|
||||
local queueREG = self:newSystemThreadedQueue("QUEUE_REG_" .. c.name):init()
|
||||
local queueJD = self:newSystemThreadedQueue("QUEUE_JD_" .. c.name):init()
|
||||
local queueDA = self:newSystemThreadedQueue("QUEUE_DA_" .. c.name):init()
|
||||
c.OnReady = multi:newConnection()
|
||||
function c:registerJob(name,func)
|
||||
function c:registerJob(name, func)
|
||||
for i = 1, self.numberofcores do
|
||||
queueREG:push({name,func})
|
||||
queueREG:push({name, func})
|
||||
end
|
||||
end
|
||||
c.tempQueue = {}
|
||||
function c:pushJob(name,...)
|
||||
function c:pushJob(name, ...)
|
||||
c.idle = os.clock()
|
||||
if not self.isReady then
|
||||
table.insert(c.tempQueue,{self.jobnum,name,...})
|
||||
self.jobnum=self.jobnum+1
|
||||
return self.jobnum-1
|
||||
table.insert(c.tempQueue, {self.jobnum, name, ...})
|
||||
self.jobnum = self.jobnum + 1
|
||||
return self.jobnum - 1
|
||||
else
|
||||
queueIN:push{self.jobnum,name,...}
|
||||
self.jobnum=self.jobnum+1
|
||||
return self.jobnum-1
|
||||
queueIN:push {self.jobnum, name, ...}
|
||||
self.jobnum = self.jobnum + 1
|
||||
return self.jobnum - 1
|
||||
end
|
||||
end
|
||||
function c:doToAll(func)
|
||||
local r = multi.randomString(12)
|
||||
for i = 1, self.numberofcores do
|
||||
queueDA:push{r,func}
|
||||
queueDA:push {r, func}
|
||||
end
|
||||
end
|
||||
for i=1,c.numberofcores do
|
||||
multi:newSystemThread(c.name.." Worker Thread #"..i,function(name)
|
||||
local multi = require("multi")
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local CC = sThread.waitFor("QUEUE_CC_"..name):init()
|
||||
CC:push("ready")
|
||||
local FUNCS={}
|
||||
local ids = {}
|
||||
local JQI = sThread.waitFor("QUEUE_IN_"..name):init()
|
||||
local JD = sThread.waitFor("QUEUE_JD_"..name):init()
|
||||
local REG = sThread.waitFor("QUEUE_REG_"..name):init()
|
||||
local DA = sThread.waitFor("QUEUE_DA_"..name):init()
|
||||
local lastjob = os.clock()
|
||||
multi:newLoop(function()
|
||||
local job=JQI:pop()
|
||||
local rd=REG:peek()
|
||||
local da=DA:peek()
|
||||
if rd then
|
||||
if not FUNCS[rd[1]] then
|
||||
FUNCS[rd[1]]=rd[2]
|
||||
rd=nil
|
||||
REG:pop()
|
||||
for i = 1, c.numberofcores do
|
||||
multi:newSystemThread(
|
||||
c.name .. " Worker Thread #" .. i,
|
||||
function(name)
|
||||
local multi = require("multi")
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL = _G.GLOBAL
|
||||
sThread = _G.sThread
|
||||
end
|
||||
local CC = sThread.waitFor("QUEUE_CC_" .. name):init()
|
||||
CC:push("ready")
|
||||
local FUNCS = {}
|
||||
local ids = {}
|
||||
local JQI = sThread.waitFor("QUEUE_IN_" .. name):init()
|
||||
local JD = sThread.waitFor("QUEUE_JD_" .. name):init()
|
||||
local REG = sThread.waitFor("QUEUE_REG_" .. name):init()
|
||||
local DA = sThread.waitFor("QUEUE_DA_" .. name):init()
|
||||
local lastjob = os.clock()
|
||||
multi:newLoop(
|
||||
function()
|
||||
local job = JQI:pop()
|
||||
local rd = REG:peek()
|
||||
local da = DA:peek()
|
||||
if rd then
|
||||
if not FUNCS[rd[1]] then
|
||||
FUNCS[rd[1]] = rd[2]
|
||||
rd = nil
|
||||
REG:pop()
|
||||
end
|
||||
end
|
||||
if da then
|
||||
if not ids[da[1]] then
|
||||
local meh = da[1]
|
||||
ids[da[1]] = true
|
||||
da[2](multi)
|
||||
da = nil
|
||||
DA:pop()
|
||||
multi:newAlarm(60):OnRing(
|
||||
function(a)
|
||||
ids[meh] = nil
|
||||
a:Destroy()
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
if job then
|
||||
lastjob = os.clock()
|
||||
local ID = table.remove(job, 1) -- return and remove
|
||||
local _name = table.remove(job, 1) -- return and remove
|
||||
if FUNCS[_name] then
|
||||
JD:push({ID, FUNCS[_name](unpack(job))})
|
||||
else -- making use of that new holding feature
|
||||
JD:push({ID, FUNCS:waitFor(_name)(unpack(job))})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if da then
|
||||
if not ids[da[1]] then
|
||||
local meh = da[1]
|
||||
ids[da[1]]=true
|
||||
da[2](multi)
|
||||
da=nil
|
||||
DA:pop()
|
||||
multi:newAlarm(60):OnRing(function(a)
|
||||
ids[meh] = nil
|
||||
a:Destroy()
|
||||
end)
|
||||
)
|
||||
multi:newLoop(
|
||||
function()
|
||||
if os.clock() - lastjob > 1 then
|
||||
sThread.sleep(.1)
|
||||
end
|
||||
end
|
||||
)
|
||||
setmetatable(
|
||||
_G,
|
||||
{
|
||||
__index = function(t, k)
|
||||
return FUNCS[k]
|
||||
end
|
||||
}
|
||||
)
|
||||
if not love then
|
||||
multi:mainloop()
|
||||
end
|
||||
if job then
|
||||
lastjob = os.clock()
|
||||
local ID=table.remove(job,1) -- return and remove
|
||||
local _name=table.remove(job,1) -- return and remove
|
||||
if FUNCS[_name] then
|
||||
JD:push({ID,FUNCS[_name](unpack(job))})
|
||||
else -- making use of that new holding feature
|
||||
JD:push({ID,FUNCS:waitFor(_name)(unpack(job))})
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:newLoop(function()
|
||||
if os.clock()-lastjob>1 then
|
||||
sThread.sleep(.1)
|
||||
end
|
||||
end)
|
||||
setmetatable(_G,{
|
||||
__index=function(t,k)
|
||||
return FUNCS[k]
|
||||
end
|
||||
})
|
||||
if not love then
|
||||
multi:mainloop()
|
||||
end
|
||||
end,c.name)
|
||||
end,
|
||||
c.name
|
||||
)
|
||||
end
|
||||
local clock = os.clock
|
||||
multi:newThread("JQ-"..c.name.." Manager",function()
|
||||
local _count = 0
|
||||
while _count<c.numberofcores do
|
||||
thread.skip()
|
||||
if queueCC:pop() then
|
||||
_count = _count + 1
|
||||
end
|
||||
end
|
||||
c.isReady = true
|
||||
for i=1,#c.tempQueue do
|
||||
queueIN:push(c.tempQueue[i])
|
||||
end
|
||||
c.tempQueue = nil
|
||||
c.OnReady:Fire(c)
|
||||
local dat
|
||||
while true do
|
||||
if not c.idle then
|
||||
thread.sleep(.5)
|
||||
else
|
||||
if clock() - c.idle >= 15 then
|
||||
c.idle = nil
|
||||
end
|
||||
multi:newThread(
|
||||
"JQ-" .. c.name .. " Manager",
|
||||
function()
|
||||
local _count = 0
|
||||
while _count < c.numberofcores do
|
||||
thread.skip()
|
||||
if queueCC:pop() then
|
||||
_count = _count + 1
|
||||
end
|
||||
end
|
||||
dat = queueJD:pop()
|
||||
if dat then
|
||||
c.idle = clock()
|
||||
c.OnJobCompleted:Fire(unpack(dat))
|
||||
c.isReady = true
|
||||
for i = 1, #c.tempQueue do
|
||||
queueIN:push(c.tempQueue[i])
|
||||
end
|
||||
c.tempQueue = nil
|
||||
c.OnReady:Fire(c)
|
||||
local dat
|
||||
while true do
|
||||
if not c.idle then
|
||||
thread.sleep(.5)
|
||||
else
|
||||
if clock() - c.idle >= 15 then
|
||||
c.idle = nil
|
||||
end
|
||||
thread.skip()
|
||||
end
|
||||
dat = queueJD:pop()
|
||||
if dat then
|
||||
c.idle = clock()
|
||||
c.OnJobCompleted:Fire(unpack(dat))
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
)
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedExecute(cmd)
|
||||
local c={}
|
||||
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
|
||||
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
|
||||
local name="Execute_Thread"..multi.randomString(16)
|
||||
c.name=name
|
||||
GLOBAL[name.."CMD"]=cmd
|
||||
multi:newSystemThread(name,function()
|
||||
if love then -- lets make sure we don't reference upvalues if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
name=__THREADNAME__ -- global data same as the name we used in this functions creation
|
||||
end -- Lanes should take the local upvalues ^^^
|
||||
cmd=sThread.waitFor(name.."CMD")
|
||||
local ret=os.execute(cmd)
|
||||
GLOBAL[name.."R"]=ret
|
||||
end)
|
||||
c.OnCMDFinished=multi:newConnection()
|
||||
c.looper=multi:newLoop(function(self)
|
||||
local ret=GLOBAL[self.link.name.."R"]
|
||||
if ret then
|
||||
self.link.OnCMDFinished:Fire(ret)
|
||||
self:Destroy()
|
||||
local c = {}
|
||||
local GLOBAL = multi.integration.GLOBAL -- set up locals incase we are using lanes
|
||||
local sThread = multi.integration.THREAD -- set up locals incase we are using lanes
|
||||
local name = "Execute_Thread" .. multi.randomString(16)
|
||||
c.name = name
|
||||
GLOBAL[name .. "CMD"] = cmd
|
||||
multi:newSystemThread(
|
||||
name,
|
||||
function()
|
||||
if love then -- lets make sure we don't reference upvalues if using love2d
|
||||
GLOBAL = _G.GLOBAL
|
||||
sThread = _G.sThread
|
||||
name = __THREADNAME__ -- global data same as the name we used in this functions creation
|
||||
end -- Lanes should take the local upvalues ^^^
|
||||
cmd = sThread.waitFor(name .. "CMD")
|
||||
local ret = os.execute(cmd)
|
||||
GLOBAL[name .. "R"] = ret
|
||||
end
|
||||
end)
|
||||
c.looper.link=c
|
||||
)
|
||||
c.OnCMDFinished = multi:newConnection()
|
||||
c.looper =
|
||||
multi:newLoop(
|
||||
function(self)
|
||||
local ret = GLOBAL[self.link.name .. "R"]
|
||||
if ret then
|
||||
self.link.OnCMDFinished:Fire(ret)
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
)
|
||||
c.looper.link = c
|
||||
return c
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user