working on 1.14.4
This commit is contained in:
parent
296d56d233
commit
689133e71f
33
changes.md
33
changes.md
@ -1,5 +1,38 @@
|
|||||||
# Changes
|
# Changes
|
||||||
[TOC]
|
[TOC]
|
||||||
|
Update 14.0.0 Consistency and stability
|
||||||
|
-------------
|
||||||
|
Added:
|
||||||
|
- multi.init() -- Initlizes the library! Must be called for multiple files to have the same handle. Example below
|
||||||
|
- thread.holdFor(NUMBER sec, FUNCTION condition) -- Works like hold, but timesout when a certain amount of time has passed!
|
||||||
|
- thread.holdWithin(NUMBER; cycles,FUNCTION; condition) -- Holds until the condition is met! If the number of cycles passed is equal to cycles, hold will return a timeout error
|
||||||
|
**Note:** when hold has a timeout the first argument will return nil and the second atgument will be TIMEOUT, if not timed out hold will return the values from the conditions
|
||||||
|
|
||||||
|
Fixed:
|
||||||
|
- Connections had a preformance issue where they would create a non function when using connection.getConnection() of a non existing label.
|
||||||
|
- An internal mismanagement of the treads scheduler was fixed. Now it should be quicker and free of bugs
|
||||||
|
- Thread error management is the integrations was not properly implemented. This is now fixed
|
||||||
|
-
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Ties in to the new function that has been added multi.init()
|
||||||
|
```lua
|
||||||
|
local multi, thread = require("multi").init() -- The require multi function still returns the multi object like before
|
||||||
|
```
|
||||||
|
Note: Using init allows you to get access to the thread handle. This was done because thread was modifying the global space as well as multi. I wanted to not modify the global space anymore.
|
||||||
|
internally most of your code can stay the same, you only need to change how the library is required. I do toy a bit with the global space, buy I use a variable name that is invalid as a variable name. The variable name is $multi. This is used internally to keep some records and maintain a clean space
|
||||||
|
|
||||||
|
Also when using intergrations things now look more consistant.
|
||||||
|
```lua
|
||||||
|
local multi, thread = require("multi").init()
|
||||||
|
local GLOBSL, THREAD = require("multi.integration.lanesManager").init() -- or whichever manager you are using
|
||||||
|
local nGLOBAL, nTHREAD = require("multi.intergration.networkManager).inti()
|
||||||
|
```
|
||||||
|
Note: You can mix and match integrations together. You can create systemthreads within network threads, and you can also create cotoutine based threads within bothe network and system threads. This gives you quite a bit of flexibility to create something awesome.
|
||||||
|
|
||||||
|
Going forward:
|
||||||
|
- Sterlization is still being worked on. I was planning of having a way to save state of multi objects and such, but that isn't possible without knowing how your code is strutured or if it is even made to handle something like that. So I decided on giving a tostirng/tofile method for each multi object as well as a fromstring/fromfile method for use. This is technically in the code already, but not documented. It has actually been in the code for a while, but its not done just yet and I want to make it perfect before sending it out.
|
||||||
|
|
||||||
Update 13.1.0 Bug fixes and features added
|
Update 13.1.0 Bug fixes and features added
|
||||||
-------------
|
-------------
|
||||||
Added:
|
Added:
|
||||||
|
|||||||
@ -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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
local multi = require("multi")
|
local multi, thread = require("multi").init()
|
||||||
os.sleep=love.timer.sleep
|
os.sleep = love.timer.sleep
|
||||||
multi.drawF={}
|
multi.drawF = {}
|
||||||
function multi:onDraw(func,i)
|
function multi:onDraw(func, i)
|
||||||
i=i or 1
|
i = i or 1
|
||||||
table.insert(self.drawF,i,func)
|
table.insert(self.drawF, i, func)
|
||||||
end
|
end
|
||||||
multi.OnKeyPressed = multi:newConnection()
|
multi.OnKeyPressed = multi:newConnection()
|
||||||
multi.OnKeyReleased = multi:newConnection()
|
multi.OnKeyReleased = multi:newConnection()
|
||||||
@ -38,39 +38,48 @@ multi.OnDraw = multi:newConnection()
|
|||||||
multi.OnTextInput = multi:newConnection()
|
multi.OnTextInput = multi:newConnection()
|
||||||
multi.OnUpdate = multi:newConnection()
|
multi.OnUpdate = multi:newConnection()
|
||||||
multi.OnQuit = multi:newConnection()
|
multi.OnQuit = multi:newConnection()
|
||||||
multi.OnPreLoad(function()
|
multi.OnPreLoad(
|
||||||
local function Hook(func,conn)
|
function()
|
||||||
if love[func]~=nil then
|
local function Hook(func, conn)
|
||||||
love[func] = Library.convert(love[func])
|
if love[func] ~= nil then
|
||||||
love[func]:inject(function(...)
|
love[func] = Library.convert(love[func])
|
||||||
conn:Fire(...)
|
love[func]:inject(
|
||||||
return {...}
|
function(...)
|
||||||
end,1)
|
conn:Fire(...)
|
||||||
elseif love[func]==nil then
|
return {...}
|
||||||
love[func] = function(...)
|
end,
|
||||||
conn:Fire(...)
|
1
|
||||||
|
)
|
||||||
|
elseif love[func] == nil then
|
||||||
|
love[func] = function(...)
|
||||||
|
conn:Fire(...)
|
||||||
|
end
|
||||||
end
|
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
|
end
|
||||||
Hook("quit",multi.OnQuit)
|
)
|
||||||
Hook("keypressed",multi.OnKeyPressed)
|
multi.OnQuit(
|
||||||
Hook("keyreleased",multi.OnKeyReleased)
|
function()
|
||||||
Hook("mousepressed",multi.OnMousePressed)
|
multi.Stop()
|
||||||
Hook("mousereleased",multi.OnMouseReleased)
|
love.event.quit()
|
||||||
Hook("wheelmoved",multi.OnMouseWheelMoved)
|
end
|
||||||
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)
|
|
||||||
return multi
|
return multi
|
||||||
|
|||||||
@ -25,6 +25,9 @@ local bin = pcall(require,"bin")
|
|||||||
local multi = {}
|
local multi = {}
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
local thread = {}
|
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._VERSION = "13.1.0"
|
multi._VERSION = "13.1.0"
|
||||||
multi.stage = "stable"
|
multi.stage = "stable"
|
||||||
@ -78,6 +81,9 @@ multi.PriorityTick=1 -- Between 1, 2 and 4
|
|||||||
multi.Priority=multi.Priority_High
|
multi.Priority=multi.Priority_High
|
||||||
multi.threshold=256
|
multi.threshold=256
|
||||||
multi.threstimed=.001
|
multi.threstimed=.001
|
||||||
|
function multi.init()
|
||||||
|
return _G["$multi"].multi,_G["$multi"].thread
|
||||||
|
end
|
||||||
function multi.queuefinal(self)
|
function multi.queuefinal(self)
|
||||||
self:Destroy()
|
self:Destroy()
|
||||||
if self.Parent.Mainloop[#self.Parent.Mainloop] then
|
if self.Parent.Mainloop[#self.Parent.Mainloop] then
|
||||||
@ -758,6 +764,9 @@ function multi:newConnector()
|
|||||||
local c = {Type = "connector"}
|
local c = {Type = "connector"}
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
local CRef = {
|
||||||
|
Fire = function() end
|
||||||
|
}
|
||||||
function multi:newConnection(protect,func)
|
function multi:newConnection(protect,func)
|
||||||
local c={}
|
local c={}
|
||||||
c.callback = func
|
c.callback = func
|
||||||
@ -802,11 +811,9 @@ function multi:newConnection(protect,func)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
c.FConnect=c.fConnect
|
c.FConnect=c.fConnect
|
||||||
function c:getConnection(name,ingore)
|
function c:getConnection(name,ignore)
|
||||||
if ingore then
|
if ignore then
|
||||||
return self.connections[name] or {
|
return self.connections[name] or CRef
|
||||||
Fire=function() return end -- if the connection doesn't exist lets call all of them or silently ignore
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return self.connections[name] or self
|
return self.connections[name] or self
|
||||||
end
|
end
|
||||||
@ -1489,6 +1496,10 @@ function thread.hold(n)
|
|||||||
thread._Requests()
|
thread._Requests()
|
||||||
return coroutine.yield({"_hold_",n or function() return true end})
|
return coroutine.yield({"_hold_",n or function() return true end})
|
||||||
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)
|
function thread.skip(n)
|
||||||
thread._Requests()
|
thread._Requests()
|
||||||
if not n then n = 1 elseif n<1 then n = 1 end
|
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].task = "hold"
|
||||||
threads[i].__ready = false
|
threads[i].__ready = false
|
||||||
ret = nil
|
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
|
end
|
||||||
end
|
end
|
||||||
@ -1684,8 +1702,19 @@ function multi.initThreads()
|
|||||||
threads[i].task = ""
|
threads[i].task = ""
|
||||||
threads[i].__ready = true
|
threads[i].__ready = true
|
||||||
end
|
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
|
end
|
||||||
if threads[i].__ready then
|
if threads[i] and threads[i].__ready then
|
||||||
threads[i].__ready = false
|
threads[i].__ready = false
|
||||||
_,ret=coroutine.resume(threads[i].thread,t0,t1,t2,t3,t4,t5,t6)
|
_,ret=coroutine.resume(threads[i].thread,t0,t1,t2,t3,t4,t5,t6)
|
||||||
end
|
end
|
||||||
@ -1724,6 +1753,13 @@ function multi:threadloop()
|
|||||||
threads[i].task = "hold"
|
threads[i].task = "hold"
|
||||||
threads[i].__ready = false
|
threads[i].__ready = false
|
||||||
ret = nil
|
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
|
end
|
||||||
end
|
end
|
||||||
@ -1753,6 +1789,17 @@ function multi:threadloop()
|
|||||||
threads[i].task = ""
|
threads[i].task = ""
|
||||||
threads[i].__ready = true
|
threads[i].__ready = true
|
||||||
end
|
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
|
end
|
||||||
if threads[i].__ready then
|
if threads[i].__ready then
|
||||||
threads[i].__ready = false
|
threads[i].__ready = false
|
||||||
@ -2550,7 +2597,4 @@ end
|
|||||||
function multi:setDefualtStateFlag(opt)
|
function multi:setDefualtStateFlag(opt)
|
||||||
--
|
--
|
||||||
end
|
end
|
||||||
if not(multi.Version == "13.2.0" or multi.Version == "14.0.0") then
|
return multi
|
||||||
_G.thread = thread
|
|
||||||
end
|
|
||||||
return multi, thread
|
|
||||||
|
|||||||
@ -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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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()
|
function os.getOS()
|
||||||
if package.config:sub(1,1)=='\\' then
|
if package.config:sub(1, 1) == "\\" then
|
||||||
return 'windows'
|
return "windows"
|
||||||
else
|
else
|
||||||
return 'unix'
|
return "unix"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Step 1 get lanes
|
-- Step 1 get lanes
|
||||||
lanes=require("lanes").configure()
|
lanes = require("lanes").configure()
|
||||||
local multi, thread = require("multi") -- get it all and have it on all lanes
|
|
||||||
multi.SystemThreads = {}
|
multi.SystemThreads = {}
|
||||||
local thread = thread
|
multi.isMainThread = true
|
||||||
multi.isMainThread=true
|
|
||||||
function multi:canSystemThread()
|
function multi:canSystemThread()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -45,40 +51,45 @@ end
|
|||||||
local __GlobalLinda = lanes.linda() -- handles global stuff
|
local __GlobalLinda = lanes.linda() -- handles global stuff
|
||||||
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
|
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
|
||||||
-- For convenience a GLOBAL table will be constructed to handle requests
|
-- For convenience a GLOBAL table will be constructed to handle requests
|
||||||
local GLOBAL={}
|
local GLOBAL = {}
|
||||||
setmetatable(GLOBAL,{
|
setmetatable(
|
||||||
__index=function(t,k)
|
GLOBAL,
|
||||||
return __GlobalLinda:get(k)
|
{
|
||||||
end,
|
__index = function(t, k)
|
||||||
__newindex=function(t,k,v)
|
return __GlobalLinda:get(k)
|
||||||
__GlobalLinda:set(k,v)
|
end,
|
||||||
end,
|
__newindex = function(t, k, v)
|
||||||
})
|
__GlobalLinda:set(k, v)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
)
|
||||||
-- Step 3 rewrite the thread methods to use Lindas
|
-- Step 3 rewrite the thread methods to use Lindas
|
||||||
local THREAD={}
|
local THREAD = {}
|
||||||
function THREAD.set(name,val)
|
function THREAD.set(name, val)
|
||||||
__GlobalLinda:set(name,val)
|
__GlobalLinda:set(name, val)
|
||||||
end
|
end
|
||||||
function THREAD.get(name)
|
function THREAD.get(name)
|
||||||
__GlobalLinda:get(name)
|
__GlobalLinda:get(name)
|
||||||
end
|
end
|
||||||
local function randomString(n)
|
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'}
|
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
|
for i = 1, n do
|
||||||
str = str..''..strings[math.random(1,#strings)]
|
str = str .. "" .. strings[math.random(1, #strings)]
|
||||||
end
|
end
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
function THREAD.waitFor(name)
|
function THREAD.waitFor(name)
|
||||||
local function wait()
|
local function wait()
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
__SleepingLinda:receive(.001,randomString(12))
|
__SleepingLinda:receive(.001, randomString(12))
|
||||||
end
|
end
|
||||||
repeat wait() until __GlobalLinda:get(name)
|
repeat
|
||||||
|
wait()
|
||||||
|
until __GlobalLinda:get(name)
|
||||||
return __GlobalLinda:get(name)
|
return __GlobalLinda:get(name)
|
||||||
end
|
end
|
||||||
function THREAD.testFor(name,val,sym)
|
function THREAD.testFor(name, val, sym)
|
||||||
--
|
--
|
||||||
end
|
end
|
||||||
function THREAD.getCores()
|
function THREAD.getCores()
|
||||||
@ -87,10 +98,10 @@ end
|
|||||||
function THREAD.getThreads()
|
function THREAD.getThreads()
|
||||||
return GLOBAL.__THREADS__
|
return GLOBAL.__THREADS__
|
||||||
end
|
end
|
||||||
if os.getOS()=="windows" then
|
if os.getOS() == "windows" then
|
||||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||||
else
|
else
|
||||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||||
end
|
end
|
||||||
function THREAD.kill() -- trigger the lane destruction
|
function THREAD.kill() -- trigger the lane destruction
|
||||||
error("Thread was killed!")
|
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]]
|
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)
|
function THREAD.sleep(n)
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
__SleepingLinda:receive(n,randomString(12))
|
__SleepingLinda:receive(n, randomString(12))
|
||||||
end
|
end
|
||||||
function THREAD.hold(n)
|
function THREAD.hold(n)
|
||||||
local function wait()
|
local function wait()
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
__SleepingLinda:receive(.001,randomString(12))
|
__SleepingLinda:receive(.001, randomString(12))
|
||||||
end
|
end
|
||||||
repeat wait() until n()
|
repeat
|
||||||
|
wait()
|
||||||
|
until n()
|
||||||
end
|
end
|
||||||
local rand = math.random(1,10000000)
|
local rand = math.random(1, 10000000)
|
||||||
-- Step 5 Basic Threads!
|
-- Step 5 Basic Threads!
|
||||||
local threads = {}
|
local threads = {}
|
||||||
local count = 1
|
local count = 1
|
||||||
local started = false
|
local started = false
|
||||||
local livingThreads = {}
|
local livingThreads = {}
|
||||||
function multi:newSystemThread(name,func,...)
|
function multi:newSystemThread(name, func, ...)
|
||||||
multi.InitSystemThreadErrorHandler()
|
multi.InitSystemThreadErrorHandler()
|
||||||
rand = math.random(1,10000000)
|
rand = math.random(1, 10000000)
|
||||||
local c={}
|
local c = {}
|
||||||
local __self=c
|
local __self = c
|
||||||
c.name=name
|
c.name = name
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.Id = count
|
c.Id = count
|
||||||
livingThreads[count] = {true,name}
|
livingThreads[count] = {true, name}
|
||||||
local THREAD_ID = count
|
local THREAD_ID = count
|
||||||
count = count + 1
|
count = count + 1
|
||||||
c.Type="sthread"
|
c.Type = "sthread"
|
||||||
c.creationTime = os.clock()
|
c.creationTime = os.clock()
|
||||||
c.alive = true
|
c.alive = true
|
||||||
local THREAD_NAME=name
|
local THREAD_NAME = name
|
||||||
local function func2(...)
|
local function func2(...)
|
||||||
local multi = require("multi")
|
local multi = require("multi")
|
||||||
_G["THREAD_NAME"]=THREAD_NAME
|
_G["THREAD_NAME"] = THREAD_NAME
|
||||||
_G["THREAD_ID"]=THREAD_ID
|
_G["THREAD_ID"] = THREAD_ID
|
||||||
math.randomseed(rand)
|
math.randomseed(rand)
|
||||||
func(...)
|
func(...)
|
||||||
if _G.__Needs_Multi then
|
if _G.__Needs_Multi then
|
||||||
@ -148,52 +161,61 @@ function multi:newSystemThread(name,func,...)
|
|||||||
end
|
end
|
||||||
THREAD.kill()
|
THREAD.kill()
|
||||||
end
|
end
|
||||||
c.thread=lanes.gen("*", func2)(...)
|
c.thread = lanes.gen("*", func2)(...)
|
||||||
function c:kill()
|
function c:kill()
|
||||||
self.thread:cancel()
|
self.thread:cancel()
|
||||||
multi.print("Thread: '"..self.name.."' has been stopped!")
|
multi.print("Thread: '" .. self.name .. "' has been stopped!")
|
||||||
self.alive = false
|
self.alive = false
|
||||||
end
|
end
|
||||||
table.insert(multi.SystemThreads,c)
|
table.insert(multi.SystemThreads, c)
|
||||||
c.OnError = multi:newConnection()
|
c.OnError = multi:newConnection()
|
||||||
GLOBAL["__THREADS__"]=livingThreads
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
multi.OnSystemThreadDied = multi:newConnection()
|
multi.OnSystemThreadDied = multi:newConnection()
|
||||||
function multi.InitSystemThreadErrorHandler()
|
function multi.InitSystemThreadErrorHandler()
|
||||||
if started==true then return end
|
if started == true then
|
||||||
|
return
|
||||||
|
end
|
||||||
started = true
|
started = true
|
||||||
multi:newThread("ThreadErrorHandler",function()
|
multi:newThread(
|
||||||
local threads = multi.SystemThreads
|
"ThreadErrorHandler",
|
||||||
while true do
|
function()
|
||||||
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.
|
local threads = multi.SystemThreads
|
||||||
for i=#threads,1,-1 do
|
while true do
|
||||||
local v,err,t=threads[i].thread:join(.001)
|
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.
|
||||||
if err then
|
for i = #threads, 1, -1 do
|
||||||
if err:find("Thread was killed!") then
|
local v, err, t = threads[i].thread:join(.001)
|
||||||
print(err)
|
if err then
|
||||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
if err:find("Thread was killed!") then
|
||||||
threads[i].alive = false
|
print(err)
|
||||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||||
GLOBAL["__THREADS__"]=livingThreads
|
threads[i].alive = false
|
||||||
table.remove(threads,i)
|
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||||
elseif err:find("stack traceback") then
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
print(err)
|
table.remove(threads, i)
|
||||||
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
|
elseif err:find("stack traceback") then
|
||||||
threads[i].alive = false
|
print(err)
|
||||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
threads[i].OnError:Fire(threads[i], err, "Error in systemThread: '" .. threads[i].name .. "' <" .. err .. ">")
|
||||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
threads[i].alive = false
|
||||||
GLOBAL["__THREADS__"]=livingThreads
|
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||||
table.remove(threads,i)
|
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||||
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
|
table.remove(threads, i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
end
|
end
|
||||||
multi.print("Integrated Lanes!")
|
multi.print("Integrated Lanes!")
|
||||||
multi.integration={} -- for module creators
|
multi.integration = {} -- for module creators
|
||||||
multi.integration.GLOBAL=GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD=THREAD
|
multi.integration.THREAD = THREAD
|
||||||
require("multi.integration.shared")
|
require("multi.integration.shared")
|
||||||
return {init=function() return GLOBAL, THREAD end}
|
return {
|
||||||
|
init = function()
|
||||||
|
return GLOBAL, THREAD
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|||||||
@ -22,15 +22,23 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
local multi = require("multi.compat.love2d")
|
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()
|
function multi:canSystemThread()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
function multi:getPlatform()
|
function multi:getPlatform()
|
||||||
return "love2d"
|
return "love2d"
|
||||||
end
|
end
|
||||||
multi.integration={}
|
multi.integration = {}
|
||||||
multi.integration.love2d={}
|
multi.integration.love2d = {}
|
||||||
multi.integration.love2d.ThreadBase=[[
|
multi.integration.love2d.ThreadBase =
|
||||||
|
[[
|
||||||
tab={...}
|
tab={...}
|
||||||
__THREADID__=table.remove(tab,1)
|
__THREADID__=table.remove(tab,1)
|
||||||
__THREADNAME__=table.remove(tab,1)
|
__THREADNAME__=table.remove(tab,1)
|
||||||
@ -198,27 +206,30 @@ updater:OnUpdate(__sync__)
|
|||||||
func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
|
func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
|
||||||
multi:mainloop()
|
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
|
_G.THREAD_ID = 0
|
||||||
__proxy__={}
|
__proxy__ = {}
|
||||||
setmetatable(GLOBAL,{
|
setmetatable(
|
||||||
__index=function(t,k)
|
GLOBAL,
|
||||||
return __proxy__[k]
|
{
|
||||||
end,
|
__index = function(t, k)
|
||||||
__newindex=function(t,k,v)
|
return __proxy__[k]
|
||||||
__proxy__[k]=v
|
end,
|
||||||
for i=1,#__channels__ do
|
__newindex = function(t, k, v)
|
||||||
if type(v)=="userdata" then
|
__proxy__[k] = v
|
||||||
__channels__[i]:push(v)
|
for i = 1, #__channels__ do
|
||||||
else
|
if type(v) == "userdata" then
|
||||||
__channels__[i]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
__channels__[i]:push(v)
|
||||||
|
else
|
||||||
|
__channels__[i]:push("SYNC " .. type(v) .. " " .. k .. " " .. resolveData(v))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
}
|
||||||
})
|
)
|
||||||
THREAD={} -- Allow main thread to interact with these objects as well
|
THREAD = {} -- Allow main thread to interact with these objects as well
|
||||||
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
multi.integration.love2d.mainChannel = love.thread.getChannel("__MainChan__")
|
||||||
isMainThread=true
|
isMainThread = true
|
||||||
multi.SystemThreads = {}
|
multi.SystemThreads = {}
|
||||||
function THREAD.getName()
|
function THREAD.getName()
|
||||||
return __THREADNAME__
|
return __THREADNAME__
|
||||||
@ -227,119 +238,125 @@ function THREAD.getID()
|
|||||||
return THREAD_ID
|
return THREAD_ID
|
||||||
end
|
end
|
||||||
function ToStr(val, name, skipnewlines, depth)
|
function ToStr(val, name, skipnewlines, depth)
|
||||||
skipnewlines = skipnewlines or false
|
skipnewlines = skipnewlines or false
|
||||||
depth = depth or 0
|
depth = depth or 0
|
||||||
local tmp = string.rep(" ", depth)
|
local tmp = string.rep(" ", depth)
|
||||||
if name then
|
if name then
|
||||||
if type(name) == "string" then
|
if type(name) == "string" then
|
||||||
tmp = tmp .. "[\""..name.."\"] = "
|
tmp = tmp .. '["' .. name .. '"] = '
|
||||||
else
|
else
|
||||||
tmp = tmp .. "["..(name or "").."] = "
|
tmp = tmp .. "[" .. (name or "") .. "] = "
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if type(val) == "table" then
|
if type(val) == "table" then
|
||||||
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
|
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
|
||||||
for k, v in pairs(val) do
|
for k, v in pairs(val) do
|
||||||
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
|
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
|
||||||
end
|
end
|
||||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||||
elseif type(val) == "number" then
|
elseif type(val) == "number" then
|
||||||
tmp = tmp .. tostring(val)
|
tmp = tmp .. tostring(val)
|
||||||
elseif type(val) == "string" then
|
elseif type(val) == "string" then
|
||||||
tmp = tmp .. string.format("%q", val)
|
tmp = tmp .. string.format("%q", val)
|
||||||
elseif type(val) == "boolean" then
|
elseif type(val) == "boolean" then
|
||||||
tmp = tmp .. (val and "true" or "false")
|
tmp = tmp .. (val and "true" or "false")
|
||||||
elseif type(val) == "function" then
|
elseif type(val) == "function" then
|
||||||
tmp = tmp .. "loadDump([===["..dump(val).."]===])"
|
tmp = tmp .. "loadDump([===[" .. dump(val) .. "]===])"
|
||||||
else
|
else
|
||||||
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
tmp = tmp .. '"[inserializeable datatype:' .. type(val) .. ']"'
|
||||||
end
|
end
|
||||||
return tmp
|
return tmp
|
||||||
end
|
end
|
||||||
function resolveType(tp,d)
|
function resolveType(tp, d)
|
||||||
if tp=="number" then
|
if tp == "number" then
|
||||||
return tonumber(d)
|
return tonumber(d)
|
||||||
elseif tp=="bool" then
|
elseif tp == "bool" then
|
||||||
return (d=="true")
|
return (d == "true")
|
||||||
elseif tp=="function" then
|
elseif tp == "function" then
|
||||||
return loadDump("[==["..d.."]==]")
|
return loadDump("[==[" .. d .. "]==]")
|
||||||
elseif tp=="table" then
|
elseif tp == "table" then
|
||||||
return loadstring("return "..d)()
|
return loadstring("return " .. d)()
|
||||||
elseif tp=="nil" then
|
elseif tp == "nil" then
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
return d
|
return d
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function resolveData(v)
|
function resolveData(v)
|
||||||
local data=""
|
local data = ""
|
||||||
if type(v)=="table" then
|
if type(v) == "table" then
|
||||||
return ToStr(v)
|
return ToStr(v)
|
||||||
elseif type(v)=="function" then
|
elseif type(v) == "function" then
|
||||||
return dump(v)
|
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)
|
return tostring(v)
|
||||||
end
|
end
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
function loadDump(d)
|
function loadDump(d)
|
||||||
local s={}
|
local s = {}
|
||||||
for p in d:gmatch("(%d-)\\") do
|
for p in d:gmatch("(%d-)\\") do
|
||||||
s[#s+1]=string.char(tonumber(p))
|
s[#s + 1] = string.char(tonumber(p))
|
||||||
end
|
end
|
||||||
return loadstring(table.concat(s))
|
return loadstring(table.concat(s))
|
||||||
end
|
end
|
||||||
function dump(func)
|
function dump(func)
|
||||||
local code,t={},string.dump(func)
|
local code, t = {}, string.dump(func)
|
||||||
for i=1,#t do
|
for i = 1, #t do
|
||||||
code[#code+1]=string.byte(t:sub(i,i)).."\\"
|
code[#code + 1] = string.byte(t:sub(i, i)) .. "\\"
|
||||||
end
|
end
|
||||||
return table.concat(code)
|
return table.concat(code)
|
||||||
end
|
end
|
||||||
local function randomString(n)
|
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'}
|
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
|
for i = 1, n do
|
||||||
str = str..''..strings[math.random(1,#strings)]
|
str = str .. "" .. strings[math.random(1, #strings)]
|
||||||
end
|
end
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
local count = 1
|
local count = 1
|
||||||
local livingThreads = {}
|
local livingThreads = {}
|
||||||
function multi:newSystemThread(name,func,...) -- the main method
|
function multi:newSystemThread(name, func, ...) -- the main method
|
||||||
multi.InitSystemThreadErrorHandler()
|
multi.InitSystemThreadErrorHandler()
|
||||||
local c={}
|
local c = {}
|
||||||
c.name=name
|
c.name = name
|
||||||
c.Name = name
|
c.Name = name
|
||||||
c.Type="sthread"
|
c.Type = "sthread"
|
||||||
c.ID=c.name.."<ID|"..randomString(8)..">"
|
c.ID = c.name .. "<ID|" .. randomString(8) .. ">"
|
||||||
c.Id=count
|
c.Id = count
|
||||||
c.creationTime = os.clock()
|
c.creationTime = os.clock()
|
||||||
count = count + 1
|
count = count + 1
|
||||||
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
c.thread = love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE", dump(func)))
|
||||||
livingThreads[count] = {true,name}
|
livingThreads[count] = {true, name}
|
||||||
livingThreads[c.thread] = c
|
livingThreads[c.thread] = c
|
||||||
c.OnError = multi:newConnection()
|
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()
|
function c:kill()
|
||||||
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
|
multi.integration.GLOBAL["__DIEPLZ" .. self.ID .. "__"] = "__DIEPLZ" .. self.ID .. "__"
|
||||||
end
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
function love.threaderror( thread, errorstr )
|
function love.threaderror(thread, errorstr)
|
||||||
multi.OnError:Fire(thread,errorstr)
|
multi.OnError:Fire(thread, errorstr)
|
||||||
livingThreads[thread].OnError:Fire(threads[i],err,"Error in systemThread: '"..livingThreads[thread].name.."' <"..errorstr..">")
|
livingThreads[thread].OnError:Fire(
|
||||||
multi.print("Error in systemThread: "..tostring(thread)..": "..errorstr)
|
threads[i],
|
||||||
|
err,
|
||||||
|
"Error in systemThread: '" .. livingThreads[thread].name .. "' <" .. errorstr .. ">"
|
||||||
|
)
|
||||||
|
multi.print("Error in systemThread: " .. tostring(thread) .. ": " .. errorstr)
|
||||||
end
|
end
|
||||||
local THREAD={}
|
local THREAD = {}
|
||||||
function THREAD.set(name,val)
|
function THREAD.set(name, val)
|
||||||
GLOBAL[name]=val
|
GLOBAL[name] = val
|
||||||
end
|
end
|
||||||
function THREAD.get(name)
|
function THREAD.get(name)
|
||||||
return GLOBAL[name]
|
return GLOBAL[name]
|
||||||
end
|
end
|
||||||
function THREAD.waitFor(name)
|
function THREAD.waitFor(name)
|
||||||
repeat multi:uManager() until GLOBAL[name]
|
repeat
|
||||||
|
multi:uManager()
|
||||||
|
until GLOBAL[name]
|
||||||
return GLOBAL[name]
|
return GLOBAL[name]
|
||||||
end
|
end
|
||||||
function THREAD.getCores()
|
function THREAD.getCores()
|
||||||
@ -349,85 +366,95 @@ function THREAD.sleep(n)
|
|||||||
love.timer.sleep(n)
|
love.timer.sleep(n)
|
||||||
end
|
end
|
||||||
function THREAD.hold(n)
|
function THREAD.hold(n)
|
||||||
repeat multi:uManager() until n()
|
repeat
|
||||||
|
multi:uManager()
|
||||||
|
until n()
|
||||||
end
|
end
|
||||||
__channels__={}
|
__channels__ = {}
|
||||||
multi.integration.GLOBAL=GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD=THREAD
|
multi.integration.THREAD = THREAD
|
||||||
updater=multi:newLoop(function(self)
|
updater =
|
||||||
local data=multi.integration.love2d.mainChannel:pop()
|
multi:newLoop(
|
||||||
while data do
|
function(self)
|
||||||
if type(data)=="string" then
|
local data = multi.integration.love2d.mainChannel:pop()
|
||||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
while data do
|
||||||
if cmd=="SYNC" then
|
if type(data) == "string" then
|
||||||
__proxy__[name]=resolveType(tp,d)
|
local cmd, tp, name, d = data:match("(%S-) (%S-) (%S-) (.+)")
|
||||||
for i=1,#__channels__ do
|
if cmd == "SYNC" then
|
||||||
-- send data to other threads
|
__proxy__[name] = resolveType(tp, d)
|
||||||
if type(v)=="userdata" then
|
for i = 1, #__channels__ do
|
||||||
__channels__[i]:push(v)
|
-- send data to other threads
|
||||||
else
|
if type(v) == "userdata" then
|
||||||
__channels__[i]:push("SYNC "..tp.." "..name.." "..d)
|
__channels__[i]:push(v)
|
||||||
end
|
else
|
||||||
end
|
__channels__[i]:push("SYNC " .. tp .. " " .. name .. " " .. d)
|
||||||
elseif cmd=="NEWTHREAD" then
|
end
|
||||||
__channels__[#__channels__+1]=love.thread.getChannel(tp)
|
end
|
||||||
for k,v in pairs(__proxy__) do -- sync the global with each new thread
|
elseif cmd == "NEWTHREAD" then
|
||||||
if type(v)=="userdata" then
|
__channels__[#__channels__ + 1] = love.thread.getChannel(tp)
|
||||||
__channels__[#__channels__]:push(v)
|
for k, v in pairs(__proxy__) do -- sync the global with each new thread
|
||||||
else
|
if type(v) == "userdata" then
|
||||||
__channels__[#__channels__]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
__channels__[#__channels__]:push(v)
|
||||||
|
else
|
||||||
|
__channels__[#__channels__]:push("SYNC " .. type(v) .. " " .. k .. " " .. resolveData(v))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
__proxy__[name] = data
|
||||||
end
|
end
|
||||||
else
|
data = multi.integration.love2d.mainChannel:pop()
|
||||||
__proxy__[name]=data
|
|
||||||
end
|
end
|
||||||
data=multi.integration.love2d.mainChannel:pop()
|
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
multi.OnSystemThreadDied = multi:newConnection()
|
multi.OnSystemThreadDied = multi:newConnection()
|
||||||
local started = false
|
local started = false
|
||||||
function multi.InitSystemThreadErrorHandler()
|
function multi.InitSystemThreadErrorHandler()
|
||||||
if started==true then return end
|
if started == true then
|
||||||
|
return
|
||||||
|
end
|
||||||
started = true
|
started = true
|
||||||
multi:newThread("ThreadErrorHandler",function()
|
multi:newThread(
|
||||||
local threads = multi.SystemThreads
|
"ThreadErrorHandler",
|
||||||
while true do
|
function()
|
||||||
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.
|
local threads = multi.SystemThreads
|
||||||
for i=#threads,1,-1 do
|
while true do
|
||||||
local v,err,t=threads[i].thread:join(.001)
|
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.
|
||||||
if err then
|
for i = #threads, 1, -1 do
|
||||||
if err:find("Thread was killed!") then
|
local v, err, t = threads[i].thread:join(.001)
|
||||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
if err then
|
||||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
if err:find("Thread was killed!") then
|
||||||
GLOBAL["__THREADS__"]=livingThreads
|
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||||
table.remove(threads,i)
|
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||||
else
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
|
table.remove(threads, i)
|
||||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
else
|
||||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
threads[i].OnError:Fire(threads[i], err, "Error in systemThread: '" .. threads[i].name .. "' <" .. err .. ">")
|
||||||
GLOBAL["__THREADS__"]=livingThreads
|
livingThreads[threads[i].Id] = {false, threads[i].Name}
|
||||||
table.remove(threads,i)
|
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||||
|
GLOBAL["__THREADS__"] = livingThreads
|
||||||
|
table.remove(threads, i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
end
|
end
|
||||||
require("multi.integration.shared")
|
require("multi.integration.shared")
|
||||||
multi.print("Integrated Love2d!")
|
multi.print("Integrated Love2d!")
|
||||||
return {
|
return {
|
||||||
init=function(t)
|
init = function(t)
|
||||||
if t then
|
if t then
|
||||||
if t.threadNamespace then
|
if t.threadNamespace then
|
||||||
multi.integration.THREADNAME=t.threadNamespace
|
multi.integration.THREADNAME = t.threadNamespace
|
||||||
multi.integration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
|
multi.integration.love2d.ThreadBase:gsub("sThread", t.threadNamespace)
|
||||||
end
|
end
|
||||||
if t.globalNamespace then
|
if t.globalNamespace then
|
||||||
multi.integration.GLOBALNAME=t.globalNamespace
|
multi.integration.GLOBALNAME = t.globalNamespace
|
||||||
multi.integration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
|
multi.integration.love2d.ThreadBase:gsub("GLOBAL", t.globalNamespace)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return GLOBAL,THREAD
|
return GLOBAL, THREAD
|
||||||
end
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
-- I DEMAND USAGE FOR LUVIT
|
-- 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)
|
-- 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
|
package.path = "?/init.lua;?.lua;" .. package.path
|
||||||
local function _INIT(luvitThread,timer)
|
local function _INIT(luvitThread, timer)
|
||||||
-- lots of this stuff should be able to stay the same
|
-- lots of this stuff should be able to stay the same
|
||||||
function os.getOS()
|
function os.getOS()
|
||||||
if package.config:sub(1,1)=='\\' then
|
if package.config:sub(1, 1) == "\\" then
|
||||||
return 'windows'
|
return "windows"
|
||||||
else
|
else
|
||||||
return 'unix'
|
return "unix"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
||||||
local multi, thread = require("multi")
|
local multi, thread = require("multi").init()
|
||||||
isMainThread=true
|
isMainThread = true
|
||||||
function multi:canSystemThread()
|
function multi:canSystemThread()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
function multi:getPlatform()
|
function multi:getPlatform()
|
||||||
return "luvit"
|
return "luvit"
|
||||||
end
|
end
|
||||||
local multi=multi
|
local multi = multi
|
||||||
-- Step 2 set up the Global table... is this possible?
|
-- Step 2 set up the Global table... is this possible?
|
||||||
local GLOBAL={}
|
local GLOBAL = {}
|
||||||
setmetatable(GLOBAL,{
|
setmetatable(
|
||||||
__index=function(t,k)
|
GLOBAL,
|
||||||
--print("No Global table when using luvit integration!")
|
{
|
||||||
return nil
|
__index = function(t, k)
|
||||||
end,
|
--print("No Global table when using luvit integration!")
|
||||||
__newindex=function(t,k,v)
|
return nil
|
||||||
--print("No Global table when using luvit integration!")
|
end,
|
||||||
end,
|
__newindex = function(t, k, v)
|
||||||
})
|
--print("No Global table when using luvit integration!")
|
||||||
local THREAD={}
|
end
|
||||||
function THREAD.set(name,val)
|
}
|
||||||
|
)
|
||||||
|
local THREAD = {}
|
||||||
|
function THREAD.set(name, val)
|
||||||
--print("No Global table when using luvit integration!")
|
--print("No Global table when using luvit integration!")
|
||||||
end
|
end
|
||||||
function THREAD.get(name)
|
function THREAD.get(name)
|
||||||
--print("No Global table when using luvit integration!")
|
--print("No Global table when using luvit integration!")
|
||||||
end
|
end
|
||||||
local function randomString(n)
|
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'}
|
local strings = {
|
||||||
for i=1,n do
|
"a",
|
||||||
str = str..''..strings[math.random(1,#strings)]
|
"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
|
end
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
function THREAD.waitFor(name)
|
function THREAD.waitFor(name)
|
||||||
--print("No Global table when using luvit integration!")
|
--print("No Global table when using luvit integration!")
|
||||||
end
|
end
|
||||||
function THREAD.testFor(name,val,sym)
|
function THREAD.testFor(name, val, sym)
|
||||||
--print("No Global table when using luvit integration!")
|
--print("No Global table when using luvit integration!")
|
||||||
end
|
end
|
||||||
function THREAD.getCores()
|
function THREAD.getCores()
|
||||||
return THREAD.__CORES
|
return THREAD.__CORES
|
||||||
end
|
end
|
||||||
if os.getOS()=="windows" then
|
if os.getOS() == "windows" then
|
||||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||||
else
|
else
|
||||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||||
end
|
end
|
||||||
function THREAD.kill() -- trigger the thread destruction
|
function THREAD.kill() -- trigger the thread destruction
|
||||||
error("Thread was Killed!")
|
error("Thread was Killed!")
|
||||||
@ -95,34 +160,41 @@ local function _INIT(luvitThread,timer)
|
|||||||
--print("No Global table when using luvit integration!")
|
--print("No Global table when using luvit integration!")
|
||||||
end
|
end
|
||||||
-- Step 5 Basic Threads!
|
-- Step 5 Basic Threads!
|
||||||
local function entry(path,name,func,...)
|
local function entry(path, name, func, ...)
|
||||||
local timer = require'timer'
|
local timer = require "timer"
|
||||||
local luvitThread = require'thread'
|
local luvitThread = require "thread"
|
||||||
package.path=path
|
package.path = path
|
||||||
loadstring(func)(...)
|
loadstring(func)(...)
|
||||||
end
|
end
|
||||||
function multi:newSystemThread(name,func,...)
|
function multi:newSystemThread(name, func, ...)
|
||||||
local c={}
|
local c = {}
|
||||||
local __self=c
|
local __self = c
|
||||||
c.name=name
|
c.name = name
|
||||||
c.Type="sthread"
|
c.Type = "sthread"
|
||||||
c.thread={}
|
c.thread = {}
|
||||||
c.func=string.dump(func)
|
c.func = string.dump(func)
|
||||||
function c:kill()
|
function c:kill()
|
||||||
-- print("No Global table when using luvit integration!")
|
-- print("No Global table when using luvit integration!")
|
||||||
end
|
end
|
||||||
luvitThread.start(entry,package.path,name,c.func,...)
|
luvitThread.start(entry, package.path, name, c.func, ...)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
multi.print("Integrated Luvit!")
|
multi.print("Integrated Luvit!")
|
||||||
multi.integration={} -- for module creators
|
multi.integration = {} -- for module creators
|
||||||
multi.integration.GLOBAL=GLOBAL
|
multi.integration.GLOBAL = GLOBAL
|
||||||
multi.integration.THREAD=THREAD
|
multi.integration.THREAD = THREAD
|
||||||
require("multi.integration.shared")
|
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...
|
-- 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 ()
|
local interval =
|
||||||
multi:uManager()
|
timer.setInterval(
|
||||||
end)
|
1,
|
||||||
|
function()
|
||||||
|
multi:uManager()
|
||||||
|
end
|
||||||
|
)
|
||||||
return multi
|
return multi
|
||||||
end
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
]]
|
]]
|
||||||
local multi, thread = require("multi")
|
local multi, thread = require("multi").init()
|
||||||
local net = require("net")
|
local net = require("net")
|
||||||
local bin = require("bin")
|
local bin = require("bin")
|
||||||
bin.setBitsInterface(infinabits) -- the bits interface does not work so well, another bug to fix
|
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
|
-- Commands that the master and node will respect, max of 256 commands
|
||||||
local CMD_ERROR = 0x00
|
local CMD_ERROR = 0x00
|
||||||
local CMD_PING = 0x01
|
local CMD_PING = 0x01
|
||||||
local CMD_PONG = 0x02
|
local CMD_PONG = 0x02
|
||||||
local CMD_QUEUE = 0x03
|
local CMD_QUEUE = 0x03
|
||||||
local CMD_TASK = 0x04
|
local CMD_TASK = 0x04
|
||||||
local CMD_INITNODE = 0x05
|
local CMD_INITNODE = 0x05
|
||||||
local CMD_INITMASTER = 0x06
|
local CMD_INITMASTER = 0x06
|
||||||
local CMD_GLOBAL = 0x07
|
local CMD_GLOBAL = 0x07
|
||||||
local CMD_LOAD = 0x08
|
local CMD_LOAD = 0x08
|
||||||
local CMD_CALL = 0x09
|
local CMD_CALL = 0x09
|
||||||
local CMD_REG = 0x0A
|
local CMD_REG = 0x0A
|
||||||
local CMD_CONSOLE = 0x0B
|
local CMD_CONSOLE = 0x0B
|
||||||
|
|
||||||
local char = string.char
|
local char = string.char
|
||||||
local byte = string.byte
|
local byte = string.byte
|
||||||
-- Process to hold all of the networkManager's muilt objects
|
-- Process to hold all of the networkManager's muilt objects
|
||||||
|
|
||||||
-- Helper for piecing commands
|
-- Helper for piecing commands
|
||||||
local function pieceCommand(cmd,...)
|
local function pieceCommand(cmd, ...)
|
||||||
local tab = {...}
|
local tab = {...}
|
||||||
table.insert(tab,1,cmd)
|
table.insert(tab, 1, cmd)
|
||||||
return table.concat(tab)
|
return table.concat(tab)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -56,17 +56,17 @@ local Queue = {}
|
|||||||
Queue.__index = Queue
|
Queue.__index = Queue
|
||||||
function Queue:newQueue()
|
function Queue:newQueue()
|
||||||
local c = {}
|
local c = {}
|
||||||
setmetatable(c,self)
|
setmetatable(c, self)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
function Queue:push(data)
|
function Queue:push(data)
|
||||||
table.insert(self,data)
|
table.insert(self, data)
|
||||||
end
|
end
|
||||||
function Queue:raw_push(data) -- Internal usage only
|
function Queue:raw_push(data) -- Internal usage only
|
||||||
table.insert(self,data)
|
table.insert(self, data)
|
||||||
end
|
end
|
||||||
function Queue:pop()
|
function Queue:pop()
|
||||||
return table.remove(self,1)
|
return table.remove(self, 1)
|
||||||
end
|
end
|
||||||
function Queue:peek()
|
function Queue:peek()
|
||||||
return self[1]
|
return self[1]
|
||||||
@ -79,39 +79,39 @@ multi.OnGUpdate = multi:newConnection()
|
|||||||
local function packData(data)
|
local function packData(data)
|
||||||
-- returns the data that was sterilized
|
-- returns the data that was sterilized
|
||||||
local dat = bin.new()
|
local dat = bin.new()
|
||||||
dat:addBlock(#type(data),1)
|
dat:addBlock(#type(data), 1)
|
||||||
dat:addBlock(type(data)) -- The type is first
|
dat:addBlock(type(data)) -- The type is first
|
||||||
if type(data)=="table" then
|
if type(data) == "table" then
|
||||||
dat:addBlock(data,nil,"t")
|
dat:addBlock(data, nil, "t")
|
||||||
elseif type(data) == "userdata" then
|
elseif type(data) == "userdata" then
|
||||||
error("Cannot sterilize userdata!")
|
error("Cannot sterilize userdata!")
|
||||||
elseif type(data) == "number" then
|
elseif type(data) == "number" then
|
||||||
dat:addBlock(data,nil,"d")
|
dat:addBlock(data, nil, "d")
|
||||||
elseif type(data) == "string" then
|
elseif type(data) == "string" then
|
||||||
dat:addBlock(#data,4)
|
dat:addBlock(#data, 4)
|
||||||
dat:addBlock(data,nil,"s")
|
dat:addBlock(data, nil, "s")
|
||||||
elseif type(data) == "boolean" then
|
elseif type(data) == "boolean" then
|
||||||
dat:addBlock(data,1)
|
dat:addBlock(data, 1)
|
||||||
elseif type(data) == "function" then
|
elseif type(data) == "function" then
|
||||||
dat:addBlock(data,nil,"f")
|
dat:addBlock(data, nil, "f")
|
||||||
end
|
end
|
||||||
return dat.data
|
return dat.data
|
||||||
end
|
end
|
||||||
local function resolveData(data)
|
local function resolveData(data)
|
||||||
-- returns the data that was sterilized
|
-- returns the data that was sterilized
|
||||||
local dat = bin.new(data)
|
local dat = bin.new(data)
|
||||||
local tlen = dat:getBlock("n",1)
|
local tlen = dat:getBlock("n", 1)
|
||||||
local tp = dat:getBlock("s",tlen)
|
local tp = dat:getBlock("s", tlen)
|
||||||
if tp=="table" then
|
if tp == "table" then
|
||||||
return dat:getBlock("t")
|
return dat:getBlock("t")
|
||||||
elseif tp=="number" then
|
elseif tp == "number" then
|
||||||
return dat:getBlock("d")
|
return dat:getBlock("d")
|
||||||
elseif tp=="string" then
|
elseif tp == "string" then
|
||||||
local num = dat:getBlock("n",4)
|
local num = dat:getBlock("n", 4)
|
||||||
return dat:getBlock("s",num)
|
return dat:getBlock("s", num)
|
||||||
elseif tp=="boolean" then
|
elseif tp == "boolean" then
|
||||||
return dat:getBlock("b",1)
|
return dat:getBlock("b", 1)
|
||||||
elseif tp=="function" then
|
elseif tp == "function" then
|
||||||
return dat:getBlock("f")
|
return dat:getBlock("f")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -119,16 +119,19 @@ end
|
|||||||
-- internal global system
|
-- internal global system
|
||||||
local GLOBAL = {}
|
local GLOBAL = {}
|
||||||
local PROXY = {}
|
local PROXY = {}
|
||||||
setmetatable(GLOBAL,{
|
setmetatable(
|
||||||
__index = function(t,k)
|
GLOBAL,
|
||||||
return PROXY[k]
|
{
|
||||||
end,
|
__index = function(t, k)
|
||||||
__newindex = function(t,k,v)
|
return PROXY[k]
|
||||||
local v = v
|
end,
|
||||||
PROXY[k] = v
|
__newindex = function(t, k, v)
|
||||||
multi.OnGUpdate:Fire(k,packData(v))
|
local v = v
|
||||||
end
|
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
|
-- In case you are unable to use broadcasting this can be used to help connect to nodes
|
||||||
function multi:nodeManager(port)
|
function multi:nodeManager(port)
|
||||||
@ -140,39 +143,48 @@ function multi:nodeManager(port)
|
|||||||
server.timeouts = {}
|
server.timeouts = {}
|
||||||
server.OnNodeAdded = multi:newConnection()
|
server.OnNodeAdded = multi:newConnection()
|
||||||
server.OnNodeRemoved = multi:newConnection()
|
server.OnNodeRemoved = multi:newConnection()
|
||||||
server.OnDataRecieved(function(server,data,cid,ip,port)
|
server.OnDataRecieved(
|
||||||
local cmd = data:sub(1,1)
|
function(server, data, cid, ip, port)
|
||||||
if cmd == "R" then
|
local cmd = data:sub(1, 1)
|
||||||
multi:newThread("Node Client Manager",function(loop)
|
if cmd == "R" then
|
||||||
while true do
|
multi:newThread(
|
||||||
if server.timeouts[cid]==true then
|
"Node Client Manager",
|
||||||
server.OnNodeRemoved:Fire(server.nodes[cid])
|
function(loop)
|
||||||
server.nodes[cid] = nil
|
while true do
|
||||||
server.timeouts[cid] = nil
|
if server.timeouts[cid] == true then
|
||||||
thread.kill()
|
server.OnNodeRemoved:Fire(server.nodes[cid])
|
||||||
else
|
server.nodes[cid] = nil
|
||||||
server.timeouts[cid] = true
|
server.timeouts[cid] = nil
|
||||||
server:send(cid,"ping")
|
thread.kill()
|
||||||
|
else
|
||||||
|
server.timeouts[cid] = true
|
||||||
|
server:send(cid, "ping")
|
||||||
|
end
|
||||||
|
thread.sleep(1)
|
||||||
|
end
|
||||||
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
|
||||||
end)
|
elseif cmd == "P" then
|
||||||
server.nodes[cid]=data:sub(2,-1)
|
server.timeouts[cid] = nil
|
||||||
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
|
||||||
elseif cmd == "P" then
|
|
||||||
server.timeouts[cid] = nil
|
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
end
|
end
|
||||||
-- The main driving force of the network manager: Nodes
|
-- The main driving force of the network manager: Nodes
|
||||||
function multi:newNode(settings)
|
function multi:newNode(settings)
|
||||||
@ -183,60 +195,69 @@ function multi:newNode(settings)
|
|||||||
local name = settings.name or multi.randomString(8)
|
local name = settings.name or multi.randomString(8)
|
||||||
local node = {}
|
local node = {}
|
||||||
node.name = name
|
node.name = name
|
||||||
multi.OnError(function(i,error)
|
multi.OnError(
|
||||||
node.OnError:Fire(node,error,node.server)
|
function(i, error)
|
||||||
end)
|
node.OnError:Fire(node, error, node.server)
|
||||||
|
end
|
||||||
|
)
|
||||||
node.server = net:newUDPServer(0) -- hosts the node using the default port
|
node.server = net:newUDPServer(0) -- hosts the node using the default port
|
||||||
_, node.port = node.server.udp:getsockname()
|
_, node.port = node.server.udp:getsockname()
|
||||||
node.connections = net.ClientCache
|
node.connections = net.ClientCache
|
||||||
node.queue = Queue:newQueue()
|
node.queue = Queue:newQueue()
|
||||||
node.functions = bin.stream("RegisteredFunctions.dat",false)
|
node.functions = bin.stream("RegisteredFunctions.dat", false)
|
||||||
node.hasFuncs = {}
|
node.hasFuncs = {}
|
||||||
node.OnError = multi:newConnection()
|
node.OnError = multi:newConnection()
|
||||||
node.OnError(function(node,err,master)
|
node.OnError(
|
||||||
multi.print("ERROR",err,node.name)
|
function(node, err, master)
|
||||||
local temp = bin.new()
|
multi.print("ERROR", err, node.name)
|
||||||
temp:addBlock(#node.name,2)
|
local temp = bin.new()
|
||||||
temp:addBlock(node.name)
|
temp:addBlock(#node.name, 2)
|
||||||
temp:addBlock(#err,2)
|
temp:addBlock(node.name)
|
||||||
temp:addBlock(err)
|
temp:addBlock(#err, 2)
|
||||||
for i,v in pairs(node.connections) do
|
temp:addBlock(err)
|
||||||
multi.print(i)
|
for i, v in pairs(node.connections) do
|
||||||
v[1]:send(v[2],char(CMD_ERROR)..temp.data,v[3])
|
multi.print(i)
|
||||||
|
v[1]:send(v[2], char(CMD_ERROR) .. temp.data, v[3])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
if settings.managerDetails then
|
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
|
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
|
else
|
||||||
c.OnDataRecieved(function(self,data)
|
c.OnDataRecieved(
|
||||||
if data == "ping" then
|
function(self, data)
|
||||||
self:send("P")
|
if data == "ping" then
|
||||||
|
self:send("P")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
c:send("RNODE_"..name.."|"..net.getLocalIP().."|"..node.port)
|
c:send("RNODE_" .. name .. "|" .. net.getLocalIP() .. "|" .. node.port)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not settings.preload then
|
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!")
|
multi.print("We have function(s) to preload!")
|
||||||
local len = node.functions:getBlock("n",1)
|
local len = node.functions:getBlock("n", 1)
|
||||||
local name,func
|
local name, func
|
||||||
while len do
|
while len do
|
||||||
name = node.functions:getBlock("s",len)
|
name = node.functions:getBlock("s", len)
|
||||||
len = node.functions:getBlock("n",2)
|
len = node.functions:getBlock("n", 2)
|
||||||
func = node.functions:getBlock("s",len)
|
func = node.functions:getBlock("s", len)
|
||||||
len = node.functions:read(1)
|
len = node.functions:read(1)
|
||||||
_G[name]=resolveData(func)
|
_G[name] = resolveData(func)
|
||||||
node.hasFuncs[name]=true
|
node.hasFuncs[name] = true
|
||||||
if not len then break end
|
if not len then
|
||||||
|
break
|
||||||
|
end
|
||||||
len = byte(len)
|
len = byte(len)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function node:pushTo(name,data)
|
function node:pushTo(name, data)
|
||||||
node:sendTo(name,char(CMD_QUEUE)..packData(data))
|
node:sendTo(name, char(CMD_QUEUE) .. packData(data))
|
||||||
end
|
end
|
||||||
function node:peek()
|
function node:peek()
|
||||||
return node.queue:peek()
|
return node.queue:peek()
|
||||||
@ -248,10 +269,10 @@ function multi:newNode(settings)
|
|||||||
local c = {}
|
local c = {}
|
||||||
local conn = node.connections
|
local conn = node.connections
|
||||||
function c.print(...)
|
function c.print(...)
|
||||||
local data = char(CMD_CONSOLE)..packData({...})
|
local data = char(CMD_CONSOLE) .. packData({...})
|
||||||
for i,v in pairs(conn) do
|
for i, v in pairs(conn) do
|
||||||
--print(i)
|
--print(i)
|
||||||
v[1]:send(v[2],data,v[3])
|
v[1]:send(v[2], data, v[3])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- function c:printTo()
|
-- function c:printTo()
|
||||||
@ -259,78 +280,89 @@ function multi:newNode(settings)
|
|||||||
-- end
|
-- end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
node.loadRate=1
|
node.loadRate = 1
|
||||||
-- Lets tell the network we are alive!
|
-- Lets tell the network we are alive!
|
||||||
node.server.OnDataRecieved(function(server,data,cid,ip,port)
|
node.server.OnDataRecieved(
|
||||||
local cmd = byte(data:sub(1,1)) -- the first byte is the command
|
function(server, data, cid, ip, port)
|
||||||
local dat = data:sub(2,-1) -- the data that you want to read
|
local cmd = byte(data:sub(1, 1)) -- the first byte is the command
|
||||||
if cmd == CMD_PING then
|
local dat = data:sub(2, -1) -- the data that you want to read
|
||||||
server:send(ip,char(CMD_PONG),port)
|
if cmd == CMD_PING then
|
||||||
elseif cmd == CMD_QUEUE then
|
server:send(ip, char(CMD_PONG), port)
|
||||||
node.queue:push(resolveData(dat))
|
elseif cmd == CMD_QUEUE then
|
||||||
elseif cmd == CMD_REG then
|
node.queue:push(resolveData(dat))
|
||||||
if not settings.allowRemoteRegistering then
|
elseif cmd == CMD_REG then
|
||||||
multi.print(ip..": has attempted to register a function when it is currently not allowed!")
|
if not settings.allowRemoteRegistering then
|
||||||
return
|
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
|
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
|
||||||
end)
|
)
|
||||||
function node:sendTo(name,data)
|
function node:sendTo(name, data)
|
||||||
local conn = node.connections[name]
|
local conn = node.connections[name]
|
||||||
conn[1]:send(conn[2],data,conn[3])
|
conn[1]:send(conn[2], data, conn[3])
|
||||||
end
|
end
|
||||||
if not settings.noBroadCast then
|
if not settings.noBroadCast then
|
||||||
node.server:broadcast("NODE_"..name)
|
node.server:broadcast("NODE_" .. name)
|
||||||
end
|
end
|
||||||
return node
|
return node
|
||||||
end
|
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.connections = net.ClientCache -- Link to the client cache that is created on the net interface
|
||||||
master.loads = {}
|
master.loads = {}
|
||||||
master.timeouts = {}
|
master.timeouts = {}
|
||||||
master.trigger = multi:newFunction(function(self,node)
|
master.trigger =
|
||||||
master.OnFirstNodeConnected:Fire(node)
|
multi:newFunction(
|
||||||
self:Pause()
|
function(self, node)
|
||||||
end)
|
master.OnFirstNodeConnected:Fire(node)
|
||||||
|
self:Pause()
|
||||||
|
end
|
||||||
|
)
|
||||||
if settings.managerDetails then
|
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
|
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
|
else
|
||||||
client.OnDataRecieved(function(client,data)
|
client.OnDataRecieved(
|
||||||
local cmd = data:sub(1,1)
|
function(client, data)
|
||||||
if cmd == "N" then
|
local cmd = data:sub(1, 1)
|
||||||
print(data)
|
if cmd == "N" then
|
||||||
local name,ip,port = data:match("(.-)|(.-)|(.+)")
|
print(data)
|
||||||
local c = net:newUDPClient(ip,port)
|
local name, ip, port = data:match("(.-)|(.-)|(.+)")
|
||||||
net.OnCastedClientInfo:Fire(c,name,ip,port)master.connections[name]=c
|
local c = net:newUDPClient(ip, port)
|
||||||
elseif cmd == "R" then
|
net.OnCastedClientInfo:Fire(c, name, ip, port)
|
||||||
local name = data:sub(2,-1)
|
master.connections[name] = c
|
||||||
master.connections[name]=nil
|
elseif cmd == "R" then
|
||||||
|
local name = data:sub(2, -1)
|
||||||
|
master.connections[name] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
client:send("G") -- init your connection as a master
|
client:send("G") -- init your connection as a master
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function master:doToAll(func)
|
function master:doToAll(func)
|
||||||
for i,v in pairs(master.connections) do
|
for i, v in pairs(master.connections) do
|
||||||
func(i,v)
|
func(i, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function master:register(name,node,func)
|
function master:register(name, node, func)
|
||||||
if not node then
|
if not node then
|
||||||
error("You must specify a node to execute a command on!")
|
error("You must specify a node to execute a command on!")
|
||||||
end
|
end
|
||||||
local temp = bin.new()
|
local temp = bin.new()
|
||||||
local fData = packData(func)
|
local fData = packData(func)
|
||||||
temp:addBlock(CMD_REG,1)
|
temp:addBlock(CMD_REG, 1)
|
||||||
temp:addBlock(#name,1)
|
temp:addBlock(#name, 1)
|
||||||
temp:addBlock(name,#name)
|
temp:addBlock(name, #name)
|
||||||
temp:addBlock(#fData,2)
|
temp:addBlock(#fData, 2)
|
||||||
temp:addBlock(fData,#fData)
|
temp:addBlock(fData, #fData)
|
||||||
master:sendTo(node,temp.data)
|
master:sendTo(node, temp.data)
|
||||||
end
|
end
|
||||||
function master:execute(name,node,...)
|
function master:execute(name, node, ...)
|
||||||
if not node then
|
if not node then
|
||||||
error("You must specify a node to execute a command on!")
|
error("You must specify a node to execute a command on!")
|
||||||
end
|
end
|
||||||
if not name then
|
if not name then
|
||||||
error("You must specify a function name to call on the node!")
|
error("You must specify a function name to call on the node!")
|
||||||
end
|
end
|
||||||
local args = packData{...}
|
local args = packData {...}
|
||||||
local name = name
|
local name = name
|
||||||
local node = node
|
local node = node
|
||||||
local temp, len, data
|
local temp, len, data
|
||||||
temp = bin.new()
|
temp = bin.new()
|
||||||
temp:addBlock(CMD_CALL,1)
|
temp:addBlock(CMD_CALL, 1)
|
||||||
temp:addBlock(#name,1)
|
temp:addBlock(#name, 1)
|
||||||
temp:addBlock(name,#name)
|
temp:addBlock(name, #name)
|
||||||
temp:addBlock(#args,4)
|
temp:addBlock(#args, 4)
|
||||||
temp:addBlock(args,#args)
|
temp:addBlock(args, #args)
|
||||||
master:sendTo(node,temp.data)
|
master:sendTo(node, temp.data)
|
||||||
end
|
end
|
||||||
function master:pushTo(name,data)
|
function master:pushTo(name, data)
|
||||||
master:sendTo(name,char(CMD_QUEUE)..packData(data))
|
master:sendTo(name, char(CMD_QUEUE) .. packData(data))
|
||||||
end
|
end
|
||||||
function master:peek()
|
function master:peek()
|
||||||
return self.queue: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()
|
function master:pop()
|
||||||
return self.queue:pop()
|
return self.queue:pop()
|
||||||
end
|
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 fData = packData(func)
|
||||||
local tab = {...}
|
local tab = {...}
|
||||||
local aData = ""
|
local aData = ""
|
||||||
if #tab~=o then
|
if #tab ~= o then
|
||||||
aData = (packData{...})
|
aData = (packData {...})
|
||||||
else
|
else
|
||||||
aData = (packData{"NO","ARGS"})
|
aData = (packData {"NO", "ARGS"})
|
||||||
end
|
end
|
||||||
local temp = bin.new()
|
local temp = bin.new()
|
||||||
temp:addBlock(#aData,4)
|
temp:addBlock(#aData, 4)
|
||||||
local len = temp.data
|
local len = temp.data
|
||||||
local temp2 = bin.new()
|
local temp2 = bin.new()
|
||||||
temp2:addBlock(#fData,4)
|
temp2:addBlock(#fData, 4)
|
||||||
local len2 = temp2.data
|
local len2 = temp2.data
|
||||||
if not name then
|
if not name then
|
||||||
local name = self:getFreeNode()
|
local name = self:getFreeNode()
|
||||||
if not name then
|
if not name then
|
||||||
name = self:getRandomNode()
|
name = self:getRandomNode()
|
||||||
end
|
end
|
||||||
if name==nil then
|
if name == nil then
|
||||||
multi:newEvent(function() return name~=nil end):OnEvent(function(evnt)
|
multi:newEvent(
|
||||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
function()
|
||||||
evnt:Destroy()
|
return name ~= nil
|
||||||
end):SetName("DelayedSendTask"):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self)
|
end
|
||||||
self:Destroy()
|
):OnEvent(
|
||||||
end)
|
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
|
else
|
||||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local name = "NODE_"..name
|
local name = "NODE_" .. name
|
||||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function master:sendTo(name,data)
|
function master:sendTo(name, data)
|
||||||
if name:sub(1,5)~="NODE_" then
|
if name:sub(1, 5) ~= "NODE_" then
|
||||||
name = "NODE_"..name
|
name = "NODE_" .. name
|
||||||
end
|
end
|
||||||
if self.connections[name]==nil then
|
if self.connections[name] == nil then
|
||||||
multi:newEvent(function() return self.connections[name]~=nil end):OnEvent(function(evnt)
|
multi:newEvent(
|
||||||
self.connections[name]:send(data)
|
function()
|
||||||
evnt:Destroy()
|
return self.connections[name] ~= nil
|
||||||
end):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self)
|
end
|
||||||
self:Destroy()
|
):OnEvent(
|
||||||
end)
|
function(evnt)
|
||||||
|
self.connections[name]:send(data)
|
||||||
|
evnt:Destroy()
|
||||||
|
end
|
||||||
|
):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(
|
||||||
|
function(self)
|
||||||
|
self:Destroy()
|
||||||
|
end
|
||||||
|
)
|
||||||
else
|
else
|
||||||
self.connections[name]:send(data)
|
self.connections[name]:send(data)
|
||||||
end
|
end
|
||||||
@ -474,8 +529,8 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
|||||||
local count = 0
|
local count = 0
|
||||||
local min = math.huge
|
local min = math.huge
|
||||||
local refO
|
local refO
|
||||||
for i,v in pairs(master.loads) do
|
for i, v in pairs(master.loads) do
|
||||||
if v<min then
|
if v < min then
|
||||||
min = v
|
min = v
|
||||||
refO = i
|
refO = i
|
||||||
end
|
end
|
||||||
@ -484,66 +539,77 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
|||||||
end
|
end
|
||||||
function master:getRandomNode()
|
function master:getRandomNode()
|
||||||
local list = {}
|
local list = {}
|
||||||
for i,v in pairs(master.connections) do
|
for i, v in pairs(master.connections) do
|
||||||
list[#list+1]=i:sub(6,-1)
|
list[#list + 1] = i:sub(6, -1)
|
||||||
end
|
end
|
||||||
return list[math.random(1,#list)]
|
return list[math.random(1, #list)]
|
||||||
end
|
end
|
||||||
net.OnCastedClientInfo(function(client,name,ip,port)
|
net.OnCastedClientInfo(
|
||||||
multi.OnGUpdate(function(k,v)
|
function(client, name, ip, port)
|
||||||
client:send(table.concat{char(CMD_GLOBAL),k,"|",v})
|
multi.OnGUpdate(
|
||||||
end)
|
function(k, v)
|
||||||
local nodename
|
client:send(table.concat {char(CMD_GLOBAL), k, "|", v})
|
||||||
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)
|
local nodename
|
||||||
end)
|
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
|
if not settings.noBroadCast then
|
||||||
net:newCastedClients("NODE_(.+)") -- Searches for nodes and connects to them, the master.clients table will contain them by name
|
net:newCastedClients("NODE_(.+)") -- Searches for nodes and connects to them, the master.clients table will contain them by name
|
||||||
end
|
end
|
||||||
@ -551,6 +617,8 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
|||||||
end
|
end
|
||||||
-- The init function that gets returned
|
-- The init function that gets returned
|
||||||
multi.print("Integrated Network Parallelism")
|
multi.print("Integrated Network Parallelism")
|
||||||
return {init = function()
|
return {
|
||||||
return GLOBAL
|
init = function()
|
||||||
end}
|
return GLOBAL
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|||||||
@ -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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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
|
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||||
local c={} -- where we will store our object
|
local c = {} -- where we will store our object
|
||||||
c.name=name -- set the name this is important for the love2d side
|
c.name = name -- set the name this is important for the love2d side
|
||||||
if love then -- check love
|
if love then -- check love
|
||||||
if love.thread then -- make sure we can use the threading module
|
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
|
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
|
function self:push(v) -- push to the channel
|
||||||
local tab
|
local tab
|
||||||
if type(v)=="table" then
|
if type(v) == "table" then
|
||||||
tab = {}
|
tab = {}
|
||||||
for i,c in pairs(v) do
|
for i, c in pairs(v) do
|
||||||
if type(c)=="function" then
|
if type(c) == "function" then
|
||||||
tab[i]="\1"..string.dump(c)
|
tab[i] = "\1" .. string.dump(c)
|
||||||
else
|
else
|
||||||
tab[i]=c
|
tab[i] = c
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.chan:push(tab)
|
self.chan:push(tab)
|
||||||
@ -46,19 +46,21 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
function self:pop() -- pop from the channel
|
function self:pop() -- pop from the channel
|
||||||
local v=self.chan:pop()
|
local v = self.chan:pop()
|
||||||
if not v then return end
|
if not v then
|
||||||
if type(v)=="table" then
|
return
|
||||||
|
end
|
||||||
|
if type(v) == "table" then
|
||||||
tab = {}
|
tab = {}
|
||||||
for i,c in pairs(v) do
|
for i, c in pairs(v) do
|
||||||
if type(c)=="string" then
|
if type(c) == "string" then
|
||||||
if c:sub(1,1)=="\1" then
|
if c:sub(1, 1) == "\1" then
|
||||||
tab[i]=loadstring(c:sub(2,-1))
|
tab[i] = loadstring(c:sub(2, -1))
|
||||||
else
|
else
|
||||||
tab[i]=c
|
tab[i] = c
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
tab[i]=c
|
tab[i] = c
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tab
|
return tab
|
||||||
@ -67,19 +69,21 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
function self:peek()
|
function self:peek()
|
||||||
local v=self.chan:peek()
|
local v = self.chan:peek()
|
||||||
if not v then return end
|
if not v then
|
||||||
if type(v)=="table" then
|
return
|
||||||
|
end
|
||||||
|
if type(v) == "table" then
|
||||||
tab = {}
|
tab = {}
|
||||||
for i,c in pairs(v) do
|
for i, c in pairs(v) do
|
||||||
if type(c)=="string" then
|
if type(c) == "string" then
|
||||||
if c:sub(1,1)=="\1" then
|
if c:sub(1, 1) == "\1" then
|
||||||
tab[i]=loadstring(c:sub(2,-1))
|
tab[i] = loadstring(c:sub(2, -1))
|
||||||
else
|
else
|
||||||
tab[i]=c
|
tab[i] = c
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
tab[i]=c
|
tab[i] = c
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tab
|
return tab
|
||||||
@ -87,7 +91,7 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
|||||||
return self.chan:pop()
|
return self.chan:pop()
|
||||||
end
|
end
|
||||||
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
|
return self -- return the object
|
||||||
end
|
end
|
||||||
return c
|
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
|
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||||
end
|
end
|
||||||
else
|
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
|
function c:push(v) -- push to the queue
|
||||||
self.linda:send("Q",v)
|
self.linda:send("Q", v)
|
||||||
end
|
end
|
||||||
function c:pop() -- pop the queue
|
function c:pop() -- pop the queue
|
||||||
return ({self.linda:receive(0,"Q")})[2]
|
return ({self.linda:receive(0, "Q")})[2]
|
||||||
end
|
end
|
||||||
function c:peek()
|
function c:peek()
|
||||||
return self.linda:get("Q")
|
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
|
function c:init() -- mimic the feature that love2d requires, so code can be consistent
|
||||||
return self
|
return self
|
||||||
end
|
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
|
end
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:newSystemThreadedConnection(name,protect)
|
function multi:newSystemThreadedConnection(name, protect)
|
||||||
local c={}
|
local c = {}
|
||||||
c.name = name or error("You must provide a name for the connection object!")
|
c.name = name or error("You must provide a name for the connection object!")
|
||||||
c.protect = protect or false
|
c.protect = protect or false
|
||||||
c.idle = nil
|
c.idle = nil
|
||||||
local sThread=multi.integration.THREAD
|
local sThread = multi.integration.THREAD
|
||||||
local GLOBAL=multi.integration.GLOBAL
|
local GLOBAL = multi.integration.GLOBAL
|
||||||
local connSync = multi:newSystemThreadedQueue(c.name.."_CONN_SYNC")
|
local connSync = multi:newSystemThreadedQueue(c.name .. "_CONN_SYNC")
|
||||||
local connFire = multi:newSystemThreadedQueue(c.name.."_CONN_FIRE")
|
local connFire = multi:newSystemThreadedQueue(c.name .. "_CONN_FIRE")
|
||||||
function c:init()
|
function c:init()
|
||||||
local multi = require("multi")
|
local multi = require("multi")
|
||||||
if love then -- lets make sure we don't reference up-values if using love2d
|
if love then -- lets make sure we don't reference up-values if using love2d
|
||||||
GLOBAL=_G.GLOBAL
|
GLOBAL = _G.GLOBAL
|
||||||
sThread=_G.sThread
|
sThread = _G.sThread
|
||||||
end
|
end
|
||||||
local conn = {}
|
local conn = {}
|
||||||
conn.obj = multi:newConnection()
|
conn.obj = multi:newConnection()
|
||||||
setmetatable(conn,{
|
setmetatable(
|
||||||
__call=function(self,...)
|
conn,
|
||||||
return self:connect(...)
|
{
|
||||||
end
|
__call = function(self, ...)
|
||||||
})
|
return self:connect(...)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
)
|
||||||
local ID = sThread.getID()
|
local ID = sThread.getID()
|
||||||
local sync = sThread.waitFor(self.name.."_CONN_SYNC"):init()
|
local sync = sThread.waitFor(self.name .. "_CONN_SYNC"):init()
|
||||||
local fire = sThread.waitFor(self.name.."_CONN_FIRE"):init()
|
local fire = sThread.waitFor(self.name .. "_CONN_FIRE"):init()
|
||||||
local connections = {}
|
local connections = {}
|
||||||
if not multi.isMainThread then
|
if not multi.isMainThread then
|
||||||
connections = {0}
|
connections = {0}
|
||||||
end
|
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)
|
function conn:connect(func)
|
||||||
return self.obj(func)
|
return self.obj(func)
|
||||||
end
|
end
|
||||||
@ -153,415 +160,468 @@ function multi:newSystemThreadedConnection(name,protect)
|
|||||||
self.obj:Remove()
|
self.obj:Remove()
|
||||||
end
|
end
|
||||||
function conn:Fire(...)
|
function conn:Fire(...)
|
||||||
for i = 1,#connections do
|
for i = 1, #connections do
|
||||||
fire:push{connections[i],ID,{...}}
|
fire:push {connections[i], ID, {...}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function conn:FireTo(to,...)
|
function conn:FireTo(to, ...)
|
||||||
local good = false
|
local good = false
|
||||||
for i = 1,#connections do
|
for i = 1, #connections do
|
||||||
if connections[i]==to then
|
if connections[i] == to then
|
||||||
good = true
|
good = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not good then return multi.print("NonExisting Connection!") end
|
if not good then
|
||||||
fire:push{to,ID,{...}}
|
return multi.print("NonExisting Connection!")
|
||||||
|
end
|
||||||
|
fire:push {to, ID, {...}}
|
||||||
end
|
end
|
||||||
-- FIRE {TO,FROM,{ARGS}}
|
-- FIRE {TO,FROM,{ARGS}}
|
||||||
local data
|
local data
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
conn.OnConnectionAdded = multi:newConnection()
|
conn.OnConnectionAdded = multi:newConnection()
|
||||||
multi:newLoop(function()
|
multi:newLoop(
|
||||||
data = fire:peek()
|
function()
|
||||||
if type(data)=="table" and data[1]==ID then
|
data = fire:peek()
|
||||||
if data[2]==ID and conn.IgnoreSelf then
|
if type(data) == "table" and data[1] == ID then
|
||||||
|
if data[2] == ID and conn.IgnoreSelf then
|
||||||
|
fire:pop()
|
||||||
|
return
|
||||||
|
end
|
||||||
fire:pop()
|
fire:pop()
|
||||||
return
|
conn.obj:Fire(unpack(data[3]))
|
||||||
end
|
end
|
||||||
fire:pop()
|
data = sync:peek()
|
||||||
conn.obj:Fire(unpack(data[3]))
|
if data ~= nil and data[1] == "SYNCA" and data[2] == ID then
|
||||||
end
|
sync:pop()
|
||||||
data = sync:peek()
|
multi.nextStep(
|
||||||
if data~=nil and data[1]=="SYNCA" and data[2]==ID then
|
function()
|
||||||
sync:pop()
|
conn.OnConnectionAdded:Fire(data[3])
|
||||||
multi.nextStep(function()
|
end
|
||||||
conn.OnConnectionAdded:Fire(data[3])
|
)
|
||||||
end)
|
table.insert(connections, data[3])
|
||||||
table.insert(connections,data[3])
|
end
|
||||||
end
|
if type(data) == "table" and data[1] == "SYNCR" and data[2] == ID then
|
||||||
if type(data)=="table" and data[1]=="SYNCR" and data[2]==ID then
|
sync:pop()
|
||||||
sync:pop()
|
for i = 1, #connections do
|
||||||
for i=1,#connections do
|
if connections[i] == data[3] then
|
||||||
if connections[i] == data[3] then
|
table.remove(connections, i)
|
||||||
table.remove(connections,i)
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end):setName("STConn.syncer")
|
):setName("STConn.syncer")
|
||||||
return conn
|
return conn
|
||||||
end
|
end
|
||||||
local cleanUp = {}
|
local cleanUp = {}
|
||||||
multi.OnSystemThreadDied(function(ThreadID)
|
multi.OnSystemThreadDied(
|
||||||
for i=1,#syncs do
|
function(ThreadID)
|
||||||
connSync:push{"SYNCR",syncs[i],ThreadID}
|
for i = 1, #syncs do
|
||||||
end
|
connSync:push {"SYNCR", syncs[i], ThreadID}
|
||||||
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
|
end
|
||||||
data = connSync:peek()
|
cleanUp[ThreadID] = true
|
||||||
if data~= nil and data[1]=="INIT" then
|
end
|
||||||
connSync:pop()
|
)
|
||||||
c.idle = clock()
|
multi:newThread(
|
||||||
table.insert(syncs,data[2])
|
c.name .. " Connection-Handler",
|
||||||
for i=1,#syncs do
|
function()
|
||||||
connSync:push{"SYNCA",syncs[i],data[2]}
|
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
|
||||||
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
|
||||||
end)
|
)
|
||||||
GLOBAL[c.name]=c
|
GLOBAL[c.name] = c
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
function multi:SystemThreadedBenchmark(n)
|
function multi:SystemThreadedBenchmark(n)
|
||||||
n=n or 1
|
n = n or 1
|
||||||
local cores=multi.integration.THREAD.getCores()
|
local cores = multi.integration.THREAD.getCores()
|
||||||
local queue=multi:newSystemThreadedQueue("THREAD_BENCH_QUEUE"):init()
|
local queue = multi:newSystemThreadedQueue("THREAD_BENCH_QUEUE"):init()
|
||||||
local sThread=multi.integration.THREAD
|
local sThread = multi.integration.THREAD
|
||||||
local GLOBAL=multi.integration.GLOBAL
|
local GLOBAL = multi.integration.GLOBAL
|
||||||
local c = {}
|
local c = {}
|
||||||
for i=1,cores do
|
for i = 1, cores do
|
||||||
multi:newSystemThread("STHREAD_BENCH",function(n)
|
multi:newSystemThread(
|
||||||
local multi = require("multi")
|
"STHREAD_BENCH",
|
||||||
if multi:getPlatform()=="love2d" then
|
function(n)
|
||||||
GLOBAL=_G.GLOBAL
|
local multi = require("multi")
|
||||||
sThread=_G.sThread
|
if multi:getPlatform() == "love2d" then
|
||||||
end -- we cannot have upvalues... in love2d globals, not locals must be used
|
GLOBAL = _G.GLOBAL
|
||||||
queue=sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
|
sThread = _G.sThread
|
||||||
multi:benchMark(n):OnBench(function(self,count)
|
end -- we cannot have upvalues... in love2d globals, not locals must be used
|
||||||
queue:push(count)
|
queue = sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
|
||||||
sThread.kill()
|
multi:benchMark(n):OnBench(
|
||||||
error("Thread was killed!")
|
function(self, count)
|
||||||
end)
|
queue:push(count)
|
||||||
multi:mainloop()
|
sThread.kill()
|
||||||
end,n)
|
error("Thread was killed!")
|
||||||
|
end
|
||||||
|
)
|
||||||
|
multi:mainloop()
|
||||||
|
end,
|
||||||
|
n
|
||||||
|
)
|
||||||
end
|
end
|
||||||
multi:newThread("THREAD_BENCH",function()
|
multi:newThread(
|
||||||
local count = 0
|
"THREAD_BENCH",
|
||||||
local cc = 0
|
function()
|
||||||
while true do
|
local count = 0
|
||||||
thread.skip(1)
|
local cc = 0
|
||||||
local dat = queue:pop()
|
while true do
|
||||||
if dat then
|
thread.skip(1)
|
||||||
cc=cc+1
|
local dat = queue:pop()
|
||||||
count = count + dat
|
if dat then
|
||||||
if cc == cores then
|
cc = cc + 1
|
||||||
c.OnBench:Fire(count)
|
count = count + dat
|
||||||
thread.kill()
|
if cc == cores then
|
||||||
|
c.OnBench:Fire(count)
|
||||||
|
thread.kill()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
)
|
||||||
c.OnBench = multi:newConnection()
|
c.OnBench = multi:newConnection()
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
function multi:newSystemThreadedConsole(name)
|
function multi:newSystemThreadedConsole(name)
|
||||||
local c={}
|
local c = {}
|
||||||
c.name = name
|
c.name = name
|
||||||
local sThread=multi.integration.THREAD
|
local sThread = multi.integration.THREAD
|
||||||
local GLOBAL=multi.integration.GLOBAL
|
local GLOBAL = multi.integration.GLOBAL
|
||||||
function c:init()
|
function c:init()
|
||||||
_G.__Needs_Multi = true
|
_G.__Needs_Multi = true
|
||||||
local multi = require("multi")
|
local multi = require("multi")
|
||||||
if multi:getPlatform()=="love2d" then
|
if multi:getPlatform() == "love2d" then
|
||||||
GLOBAL=_G.GLOBAL
|
GLOBAL = _G.GLOBAL
|
||||||
sThread=_G.sThread
|
sThread = _G.sThread
|
||||||
end
|
end
|
||||||
local cc={}
|
local cc = {}
|
||||||
if multi.isMainThread then
|
if multi.isMainThread then
|
||||||
if GLOBAL["__SYSTEM_CONSOLE__"] then
|
if GLOBAL["__SYSTEM_CONSOLE__"] then
|
||||||
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
||||||
else
|
else
|
||||||
cc.stream = multi:newSystemThreadedQueue("__SYSTEM_CONSOLE__"):init()
|
cc.stream = multi:newSystemThreadedQueue("__SYSTEM_CONSOLE__"):init()
|
||||||
multi:newLoop(function()
|
multi:newLoop(
|
||||||
local data = cc.stream:pop()
|
function()
|
||||||
if data then
|
local data = cc.stream:pop()
|
||||||
local dat = table.remove(data,1)
|
if data then
|
||||||
if dat=="w" then
|
local dat = table.remove(data, 1)
|
||||||
io.write(unpack(data))
|
if dat == "w" then
|
||||||
elseif dat=="p" then
|
io.write(unpack(data))
|
||||||
print(unpack(data))
|
elseif dat == "p" then
|
||||||
|
print(unpack(data))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end):setName("ST.consoleSyncer")
|
):setName("ST.consoleSyncer")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
||||||
end
|
end
|
||||||
function cc:write(msg)
|
function cc:write(msg)
|
||||||
self.stream:push({"w",tostring(msg)})
|
self.stream:push({"w", tostring(msg)})
|
||||||
end
|
end
|
||||||
function cc:print(...)
|
function cc:print(...)
|
||||||
local tab = {...}
|
local tab = {...}
|
||||||
for i=1,#tab do
|
for i = 1, #tab do
|
||||||
tab[i]=tostring(tab[i])
|
tab[i] = tostring(tab[i])
|
||||||
end
|
end
|
||||||
self.stream:push({"p",unpack(tab)})
|
self.stream:push({"p", unpack(tab)})
|
||||||
end
|
end
|
||||||
return cc
|
return cc
|
||||||
end
|
end
|
||||||
GLOBAL[c.name]=c
|
GLOBAL[c.name] = c
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
-- NEEDS WORK
|
-- NEEDS WORK
|
||||||
function multi:newSystemThreadedTable(name)
|
function multi:newSystemThreadedTable(name)
|
||||||
local c={}
|
local c = {}
|
||||||
c.name=name -- set the name this is important for identifying what is what
|
c.name = name -- set the name this is important for identifying what is what
|
||||||
local sThread=multi.integration.THREAD
|
local sThread = multi.integration.THREAD
|
||||||
local GLOBAL=multi.integration.GLOBAL
|
local GLOBAL = multi.integration.GLOBAL
|
||||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||||
_G.__Needs_Multi = true
|
_G.__Needs_Multi = true
|
||||||
local multi = require("multi")
|
local multi = require("multi")
|
||||||
if multi:getPlatform()=="love2d" then
|
if multi:getPlatform() == "love2d" then
|
||||||
GLOBAL=_G.GLOBAL
|
GLOBAL = _G.GLOBAL
|
||||||
sThread=_G.sThread
|
sThread = _G.sThread
|
||||||
end
|
end
|
||||||
local cc={}
|
local cc = {}
|
||||||
cc.tab={}
|
cc.tab = {}
|
||||||
if multi.isMainThread then
|
if multi.isMainThread then
|
||||||
if not GLOBAL[self.name.."_Tabled_Connection"] then
|
if not GLOBAL[self.name .. "_Tabled_Connection"] then
|
||||||
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
|
cc.conn = multi:newSystemThreadedConnection(self.name .. "_Tabled_Connection"):init()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
|
cc.conn = sThread.waitFor(self.name .. "_Tabled_Connection"):init()
|
||||||
end
|
end
|
||||||
function cc:waitFor(name)
|
function cc:waitFor(name)
|
||||||
repeat multi:uManager() until tab[name]~=nil
|
repeat
|
||||||
|
multi:uManager()
|
||||||
|
until tab[name] ~= nil
|
||||||
return tab[name]
|
return tab[name]
|
||||||
end
|
end
|
||||||
local link = cc
|
local link = cc
|
||||||
cc.conn(function(k,v)
|
cc.conn(
|
||||||
link.tab[k]=v
|
function(k, v)
|
||||||
end)
|
link.tab[k] = v
|
||||||
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
|
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
|
return cc
|
||||||
end
|
end
|
||||||
GLOBAL[c.name]=c
|
GLOBAL[c.name] = c
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
local jobqueuecount = 0
|
local jobqueuecount = 0
|
||||||
local jqueues = {}
|
local jqueues = {}
|
||||||
function multi:newSystemThreadedJobQueue(a,b)
|
function multi:newSystemThreadedJobQueue(a, b)
|
||||||
jobqueuecount=jobqueuecount+1
|
jobqueuecount = jobqueuecount + 1
|
||||||
local GLOBAL=multi.integration.GLOBAL
|
local GLOBAL = multi.integration.GLOBAL
|
||||||
local sThread=multi.integration.THREAD
|
local sThread = multi.integration.THREAD
|
||||||
local c = {}
|
local c = {}
|
||||||
c.numberofcores = 4
|
c.numberofcores = 4
|
||||||
c.idle = nil
|
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
|
-- 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
|
c.name = a
|
||||||
elseif type(a)=="number" and not (b) then
|
elseif type(a) == "number" and not (b) then
|
||||||
c.numberofcores = a
|
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.name = a
|
||||||
c.numberofcores = b
|
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.name = b
|
||||||
c.numberofcores = a
|
c.numberofcores = a
|
||||||
end
|
end
|
||||||
if jqueues[c.name] then
|
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
|
end
|
||||||
jqueues[c.name] = true
|
jqueues[c.name] = true
|
||||||
c.isReady = false
|
c.isReady = false
|
||||||
c.jobnum=1
|
c.jobnum = 1
|
||||||
c.OnJobCompleted = multi:newConnection()
|
c.OnJobCompleted = multi:newConnection()
|
||||||
local queueIN = self:newSystemThreadedQueue("QUEUE_IN_"..c.name):init()
|
local queueIN = self:newSystemThreadedQueue("QUEUE_IN_" .. c.name):init()
|
||||||
local queueCC = self:newSystemThreadedQueue("QUEUE_CC_"..c.name):init()
|
local queueCC = self:newSystemThreadedQueue("QUEUE_CC_" .. c.name):init()
|
||||||
local queueREG = self:newSystemThreadedQueue("QUEUE_REG_"..c.name):init()
|
local queueREG = self:newSystemThreadedQueue("QUEUE_REG_" .. c.name):init()
|
||||||
local queueJD = self:newSystemThreadedQueue("QUEUE_JD_"..c.name):init()
|
local queueJD = self:newSystemThreadedQueue("QUEUE_JD_" .. c.name):init()
|
||||||
local queueDA = self:newSystemThreadedQueue("QUEUE_DA_"..c.name):init()
|
local queueDA = self:newSystemThreadedQueue("QUEUE_DA_" .. c.name):init()
|
||||||
c.OnReady = multi:newConnection()
|
c.OnReady = multi:newConnection()
|
||||||
function c:registerJob(name,func)
|
function c:registerJob(name, func)
|
||||||
for i = 1, self.numberofcores do
|
for i = 1, self.numberofcores do
|
||||||
queueREG:push({name,func})
|
queueREG:push({name, func})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
c.tempQueue = {}
|
c.tempQueue = {}
|
||||||
function c:pushJob(name,...)
|
function c:pushJob(name, ...)
|
||||||
c.idle = os.clock()
|
c.idle = os.clock()
|
||||||
if not self.isReady then
|
if not self.isReady then
|
||||||
table.insert(c.tempQueue,{self.jobnum,name,...})
|
table.insert(c.tempQueue, {self.jobnum, name, ...})
|
||||||
self.jobnum=self.jobnum+1
|
self.jobnum = self.jobnum + 1
|
||||||
return self.jobnum-1
|
return self.jobnum - 1
|
||||||
else
|
else
|
||||||
queueIN:push{self.jobnum,name,...}
|
queueIN:push {self.jobnum, name, ...}
|
||||||
self.jobnum=self.jobnum+1
|
self.jobnum = self.jobnum + 1
|
||||||
return self.jobnum-1
|
return self.jobnum - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function c:doToAll(func)
|
function c:doToAll(func)
|
||||||
local r = multi.randomString(12)
|
local r = multi.randomString(12)
|
||||||
for i = 1, self.numberofcores do
|
for i = 1, self.numberofcores do
|
||||||
queueDA:push{r,func}
|
queueDA:push {r, func}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for i=1,c.numberofcores do
|
for i = 1, c.numberofcores do
|
||||||
multi:newSystemThread(c.name.." Worker Thread #"..i,function(name)
|
multi:newSystemThread(
|
||||||
local multi = require("multi")
|
c.name .. " Worker Thread #" .. i,
|
||||||
if love then -- lets make sure we don't reference up-values if using love2d
|
function(name)
|
||||||
GLOBAL=_G.GLOBAL
|
local multi = require("multi")
|
||||||
sThread=_G.sThread
|
if love then -- lets make sure we don't reference up-values if using love2d
|
||||||
end
|
GLOBAL = _G.GLOBAL
|
||||||
local CC = sThread.waitFor("QUEUE_CC_"..name):init()
|
sThread = _G.sThread
|
||||||
CC:push("ready")
|
end
|
||||||
local FUNCS={}
|
local CC = sThread.waitFor("QUEUE_CC_" .. name):init()
|
||||||
local ids = {}
|
CC:push("ready")
|
||||||
local JQI = sThread.waitFor("QUEUE_IN_"..name):init()
|
local FUNCS = {}
|
||||||
local JD = sThread.waitFor("QUEUE_JD_"..name):init()
|
local ids = {}
|
||||||
local REG = sThread.waitFor("QUEUE_REG_"..name):init()
|
local JQI = sThread.waitFor("QUEUE_IN_" .. name):init()
|
||||||
local DA = sThread.waitFor("QUEUE_DA_"..name):init()
|
local JD = sThread.waitFor("QUEUE_JD_" .. name):init()
|
||||||
local lastjob = os.clock()
|
local REG = sThread.waitFor("QUEUE_REG_" .. name):init()
|
||||||
multi:newLoop(function()
|
local DA = sThread.waitFor("QUEUE_DA_" .. name):init()
|
||||||
local job=JQI:pop()
|
local lastjob = os.clock()
|
||||||
local rd=REG:peek()
|
multi:newLoop(
|
||||||
local da=DA:peek()
|
function()
|
||||||
if rd then
|
local job = JQI:pop()
|
||||||
if not FUNCS[rd[1]] then
|
local rd = REG:peek()
|
||||||
FUNCS[rd[1]]=rd[2]
|
local da = DA:peek()
|
||||||
rd=nil
|
if rd then
|
||||||
REG:pop()
|
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
|
||||||
end
|
)
|
||||||
if da then
|
multi:newLoop(
|
||||||
if not ids[da[1]] then
|
function()
|
||||||
local meh = da[1]
|
if os.clock() - lastjob > 1 then
|
||||||
ids[da[1]]=true
|
sThread.sleep(.1)
|
||||||
da[2](multi)
|
end
|
||||||
da=nil
|
|
||||||
DA:pop()
|
|
||||||
multi:newAlarm(60):OnRing(function(a)
|
|
||||||
ids[meh] = nil
|
|
||||||
a:Destroy()
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
)
|
||||||
|
setmetatable(
|
||||||
|
_G,
|
||||||
|
{
|
||||||
|
__index = function(t, k)
|
||||||
|
return FUNCS[k]
|
||||||
|
end
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if not love then
|
||||||
|
multi:mainloop()
|
||||||
end
|
end
|
||||||
if job then
|
end,
|
||||||
lastjob = os.clock()
|
c.name
|
||||||
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
|
end
|
||||||
local clock = os.clock
|
local clock = os.clock
|
||||||
multi:newThread("JQ-"..c.name.." Manager",function()
|
multi:newThread(
|
||||||
local _count = 0
|
"JQ-" .. c.name .. " Manager",
|
||||||
while _count<c.numberofcores do
|
function()
|
||||||
thread.skip()
|
local _count = 0
|
||||||
if queueCC:pop() then
|
while _count < c.numberofcores do
|
||||||
_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
|
|
||||||
thread.skip()
|
thread.skip()
|
||||||
|
if queueCC:pop() then
|
||||||
|
_count = _count + 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
dat = queueJD:pop()
|
c.isReady = true
|
||||||
if dat then
|
for i = 1, #c.tempQueue do
|
||||||
c.idle = clock()
|
queueIN:push(c.tempQueue[i])
|
||||||
c.OnJobCompleted:Fire(unpack(dat))
|
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
|
end
|
||||||
end)
|
)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
function multi:newSystemThreadedExecute(cmd)
|
function multi:newSystemThreadedExecute(cmd)
|
||||||
local c={}
|
local c = {}
|
||||||
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
|
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 sThread = multi.integration.THREAD -- set up locals incase we are using lanes
|
||||||
local name="Execute_Thread"..multi.randomString(16)
|
local name = "Execute_Thread" .. multi.randomString(16)
|
||||||
c.name=name
|
c.name = name
|
||||||
GLOBAL[name.."CMD"]=cmd
|
GLOBAL[name .. "CMD"] = cmd
|
||||||
multi:newSystemThread(name,function()
|
multi:newSystemThread(
|
||||||
if love then -- lets make sure we don't reference upvalues if using love2d
|
name,
|
||||||
GLOBAL=_G.GLOBAL
|
function()
|
||||||
sThread=_G.sThread
|
if love then -- lets make sure we don't reference upvalues if using love2d
|
||||||
name=__THREADNAME__ -- global data same as the name we used in this functions creation
|
GLOBAL = _G.GLOBAL
|
||||||
end -- Lanes should take the local upvalues ^^^
|
sThread = _G.sThread
|
||||||
cmd=sThread.waitFor(name.."CMD")
|
name = __THREADNAME__ -- global data same as the name we used in this functions creation
|
||||||
local ret=os.execute(cmd)
|
end -- Lanes should take the local upvalues ^^^
|
||||||
GLOBAL[name.."R"]=ret
|
cmd = sThread.waitFor(name .. "CMD")
|
||||||
end)
|
local ret = os.execute(cmd)
|
||||||
c.OnCMDFinished=multi:newConnection()
|
GLOBAL[name .. "R"] = ret
|
||||||
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
|
||||||
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
|
return c
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user