working on 1.14.4

This commit is contained in:
Ryan Ward 2019-10-29 23:57:04 -04:00
parent 296d56d233
commit 689133e71f
8 changed files with 1279 additions and 944 deletions

View File

@ -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:

View File

@ -21,7 +21,7 @@ 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)
@ -38,14 +38,18 @@ 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(
function()
local function Hook(func, conn) local function Hook(func, conn)
if love[func] ~= nil then if love[func] ~= nil then
love[func] = Library.convert(love[func]) love[func] = Library.convert(love[func])
love[func]:inject(function(...) love[func]:inject(
function(...)
conn:Fire(...) conn:Fire(...)
return {...} return {...}
end,1) end,
1
)
elseif love[func] == nil then elseif love[func] == nil then
love[func] = function(...) love[func] = function(...)
conn:Fire(...) conn:Fire(...)
@ -62,15 +66,20 @@ multi.OnPreLoad(function()
Hook("draw", multi.OnDraw) Hook("draw", multi.OnDraw)
Hook("textinput", multi.OnTextInput) Hook("textinput", multi.OnTextInput)
Hook("update", multi.OnUpdate) Hook("update", multi.OnUpdate)
multi.OnDraw(function() multi.OnDraw(
function()
for i = 1, #multi.drawF do for i = 1, #multi.drawF do
love.graphics.setColor(255, 255, 255, 255) love.graphics.setColor(255, 255, 255, 255)
multi.drawF[i]() multi.drawF[i]()
end end
end) end
end) )
multi.OnQuit(function() end
)
multi.OnQuit(
function()
multi.Stop() multi.Stop()
love.event.quit() love.event.quit()
end) end
)
return multi return multi

View File

@ -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
if threads[i].__ready then end
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

View File

@ -22,18 +22,24 @@ 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
@ -46,14 +52,17 @@ 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(
GLOBAL,
{
__index = function(t, k) __index = function(t, k)
return __GlobalLinda:get(k) return __GlobalLinda:get(k)
end, end,
__newindex = function(t, k, v) __newindex = function(t, k, v)
__GlobalLinda:set(k, v) __GlobalLinda:set(k, v)
end, 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)
@ -63,10 +72,10 @@ 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
@ -75,7 +84,9 @@ function THREAD.waitFor(name)
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)
@ -114,7 +125,9 @@ function THREAD.hold(n)
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!
@ -161,9 +174,13 @@ function multi:newSystemThread(name,func,...)
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(
"ThreadErrorHandler",
function()
local threads = multi.SystemThreads local threads = multi.SystemThreads
while true do while true do
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough. 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.
@ -189,11 +206,16 @@ function multi.InitSystemThreadErrorHandler()
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
}

View File

@ -22,6 +22,13 @@ 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
@ -30,7 +37,8 @@ function multi:getPlatform()
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)
@ -201,7 +209,9 @@ 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(
GLOBAL,
{
__index = function(t, k) __index = function(t, k)
return __proxy__[k] return __proxy__[k]
end, end,
@ -214,8 +224,9 @@ setmetatable(GLOBAL,{
__channels__[i]:push("SYNC " .. type(v) .. " " .. k .. " " .. resolveData(v)) __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
@ -232,7 +243,7 @@ function ToStr(val, name, skipnewlines, depth)
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
@ -252,7 +263,7 @@ function ToStr(val, name, skipnewlines, depth)
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
@ -297,10 +308,10 @@ function dump(func)
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
@ -328,7 +339,11 @@ function multi:newSystemThread(name,func,...) -- the main method
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(
threads[i],
err,
"Error in systemThread: '" .. livingThreads[thread].name .. "' <" .. errorstr .. ">"
)
multi.print("Error in systemThread: " .. tostring(thread) .. ": " .. errorstr) multi.print("Error in systemThread: " .. tostring(thread) .. ": " .. errorstr)
end end
local THREAD = {} local THREAD = {}
@ -339,7 +354,9 @@ 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,12 +366,16 @@ 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 =
multi:newLoop(
function(self)
local data = multi.integration.love2d.mainChannel:pop() local data = multi.integration.love2d.mainChannel:pop()
while data do while data do
if type(data) == "string" then if type(data) == "string" then
@ -384,13 +405,18 @@ updater=multi:newLoop(function(self)
end end
data = multi.integration.love2d.mainChannel:pop() data = multi.integration.love2d.mainChannel:pop()
end 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(
"ThreadErrorHandler",
function()
local threads = multi.SystemThreads local threads = multi.SystemThreads
while true do while true do
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough. 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.
@ -412,7 +438,8 @@ function multi.InitSystemThreadErrorHandler()
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!")

View File

@ -21,21 +21,20 @@ 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
@ -46,15 +45,18 @@ local function _INIT(luvitThread,timer)
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(
GLOBAL,
{
__index = function(t, k) __index = function(t, k)
--print("No Global table when using luvit integration!") --print("No Global table when using luvit integration!")
return nil return nil
end, end,
__newindex = function(t, k, v) __newindex = function(t, k, v)
--print("No Global table when using luvit integration!") --print("No Global table when using luvit integration!")
end, end
}) }
)
local THREAD = {} local THREAD = {}
function THREAD.set(name, val) function THREAD.set(name, val)
--print("No Global table when using luvit integration!") --print("No Global table when using luvit integration!")
@ -63,10 +65,73 @@ local function _INIT(luvitThread,timer)
--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 = {
"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
@ -96,8 +161,8 @@ local function _INIT(luvitThread,timer)
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
@ -120,9 +185,16 @@ local function _INIT(luvitThread,timer)
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 =
timer.setInterval(
1,
function()
multi:uManager() multi:uManager()
end) 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}

View File

@ -21,7 +21,7 @@ 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
@ -119,7 +119,9 @@ end
-- internal global system -- internal global system
local GLOBAL = {} local GLOBAL = {}
local PROXY = {} local PROXY = {}
setmetatable(GLOBAL,{ setmetatable(
GLOBAL,
{
__index = function(t, k) __index = function(t, k)
return PROXY[k] return PROXY[k]
end, end,
@ -128,7 +130,8 @@ setmetatable(GLOBAL,{
PROXY[k] = v PROXY[k] = v
multi.OnGUpdate:Fire(k, packData(v)) multi.OnGUpdate:Fire(k, packData(v))
end 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,10 +143,13 @@ 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(
function(server, data, cid, ip, port)
local cmd = data:sub(1, 1) local cmd = data:sub(1, 1)
if cmd == "R" then if cmd == "R" then
multi:newThread("Node Client Manager",function(loop) multi:newThread(
"Node Client Manager",
function(loop)
while true do while true do
if server.timeouts[cid] == true then if server.timeouts[cid] == true then
server.OnNodeRemoved:Fire(server.nodes[cid]) server.OnNodeRemoved:Fire(server.nodes[cid])
@ -156,23 +162,29 @@ function multi:nodeManager(port)
end end
thread.sleep(1) thread.sleep(1)
end end
end) end
)
server.nodes[cid] = data:sub(2, -1) server.nodes[cid] = data:sub(2, -1)
server.OnNodeAdded:Fire(server.nodes[cid]) server.OnNodeAdded:Fire(server.nodes[cid])
elseif cmd == "G" then elseif cmd == "G" then
server.OnNodeAdded(function(node) server.OnNodeAdded(
function(node)
server:send(cid, node) server:send(cid, node)
end) end
server.OnNodeRemoved(function(node) )
server.OnNodeRemoved(
function(node)
server:send(cid, "R" .. node:match("(.-)|")) server:send(cid, "R" .. node:match("(.-)|"))
end) end
)
for i, v in pairs(server.nodes) do for i, v in pairs(server.nodes) do
server:send(cid, v) server:send(cid, v)
end end
elseif cmd == "P" then elseif cmd == "P" then
server.timeouts[cid] = nil server.timeouts[cid] = nil
end 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,9 +195,11 @@ 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(
function(i, error)
node.OnError:Fire(node, error, node.server) node.OnError:Fire(node, error, node.server)
end) 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
@ -193,7 +207,8 @@ function multi:newNode(settings)
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(
function(node, err, master)
multi.print("ERROR", err, node.name) multi.print("ERROR", err, node.name)
local temp = bin.new() local temp = bin.new()
temp:addBlock(#node.name, 2) temp:addBlock(#node.name, 2)
@ -204,17 +219,21 @@ function multi:newNode(settings)
multi.print(i) multi.print(i)
v[1]:send(v[2], char(CMD_ERROR) .. temp.data, v[3]) 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(
function(self, data)
if data == "ping" then if data == "ping" then
self:send("P") 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
@ -230,7 +249,9 @@ function multi:newNode(settings)
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
@ -261,7 +282,8 @@ function multi:newNode(settings)
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(
function(server, data, cid, ip, port)
local cmd = byte(data:sub(1, 1)) -- the first byte is the command 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 local dat = data:sub(2, -1) -- the data that you want to read
if cmd == CMD_PING then if cmd == CMD_PING then
@ -306,25 +328,35 @@ function multi:newNode(settings)
elseif cmd == CMD_INITNODE then elseif cmd == CMD_INITNODE then
multi.print("Connected with another node!") multi.print("Connected with another node!")
node.connections[dat] = {server, ip, port} node.connections[dat] = {server, ip, port}
multi.OnGUpdate(function(k,v) multi.OnGUpdate(
function(k, v)
server:send(ip, table.concat {char(CMD_GLOBAL), k, "|", v}, port) server:send(ip, table.concat {char(CMD_GLOBAL), k, "|", v}, port)
end)-- set this up end
)
-- set this up
elseif cmd == CMD_INITMASTER then elseif cmd == CMD_INITMASTER then
multi.print("Connected to the master!", dat) multi.print("Connected to the master!", dat)
node.connections[dat] = {server, ip, port} node.connections[dat] = {server, ip, port}
multi.OnGUpdate(function(k,v) multi.OnGUpdate(
function(k, v)
server:send(ip, table.concat {char(CMD_GLOBAL), k, "|", v}, port) server:send(ip, table.concat {char(CMD_GLOBAL), k, "|", v}, port)
end)-- set this up end
multi:newTLoop(function() )
-- set this up
multi:newTLoop(
function()
server:send(ip, char(CMD_LOAD) .. node.name .. "|" .. multi:getLoad(), port) server:send(ip, char(CMD_LOAD) .. node.name .. "|" .. multi:getLoad(), port)
end,node.loadRate) end,
node.loadRate
)
server:send(ip, char(CMD_LOAD) .. node.name .. "|" .. multi:getLoad(), port) server:send(ip, char(CMD_LOAD) .. node.name .. "|" .. multi:getLoad(), port)
server:send(ip, char(CMD_INITNODE) .. node.name, port) server:send(ip, char(CMD_INITNODE) .. node.name, port)
elseif cmd == CMD_GLOBAL then elseif cmd == CMD_GLOBAL then
local k, v = dat:match("(.-)|(.+)") local k, v = dat:match("(.-)|(.+)")
PROXY[k] = resolveData(v) PROXY[k] = resolveData(v)
end 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])
@ -350,27 +382,34 @@ 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 =
multi:newFunction(
function(self, node)
master.OnFirstNodeConnected:Fire(node) master.OnFirstNodeConnected:Fire(node)
self:Pause() self:Pause()
end) 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(
function(client, data)
local cmd = data:sub(1, 1) local cmd = data:sub(1, 1)
if cmd == "N" then if cmd == "N" then
print(data) print(data)
local name, ip, port = data:match("(.-)|(.-)|(.+)") local name, ip, port = data:match("(.-)|(.-)|(.+)")
local c = net:newUDPClient(ip, port) local c = net:newUDPClient(ip, port)
net.OnCastedClientInfo:Fire(c,name,ip,port)master.connections[name]=c net.OnCastedClientInfo:Fire(c, name, ip, port)
master.connections[name] = c
elseif cmd == "R" then elseif cmd == "R" then
local name = data:sub(2, -1) local name = data:sub(2, -1)
master.connections[name] = nil 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
@ -441,12 +480,20 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
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(
function()
return name ~= nil
end
):OnEvent(
function(evnt)
self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData) self:sendTo(name, char(CMD_TASK) .. len .. aData .. len2 .. fData)
evnt:Destroy() evnt:Destroy()
end):SetName("DelayedSendTask"):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self) end
):SetName("DelayedSendTask"):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(
function(self)
self:Destroy() self:Destroy()
end) 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
@ -460,12 +507,20 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
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(
function()
return self.connections[name] ~= nil
end
):OnEvent(
function(evnt)
self.connections[name]:send(data) self.connections[name]:send(data)
evnt:Destroy() evnt:Destroy()
end):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self) end
):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(
function(self)
self:Destroy() self:Destroy()
end) end
)
else else
self.connections[name]:send(data) self.connections[name]:send(data)
end end
@ -489,18 +544,24 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
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)
multi.OnGUpdate(
function(k, v)
client:send(table.concat {char(CMD_GLOBAL), k, "|", v}) client:send(table.concat {char(CMD_GLOBAL), k, "|", v})
end) end
)
local nodename local nodename
for i, v in pairs(master.connections) do for i, v in pairs(master.connections) do
nodename = i nodename = i
end end
client.OnClientReady(function() client.OnClientReady(
function()
client:send(char(CMD_INITMASTER) .. master.name) -- Tell the node that you are a master trying to connect client:send(char(CMD_INITMASTER) .. master.name) -- Tell the node that you are a master trying to connect
if not settings.managerDetails then if not settings.managerDetails then
multi:newThread("Node Data Link Controller",function(loop) multi:newThread(
"Node Data Link Controller",
function(loop)
while true do while true do
if master.timeouts[name] == true then if master.timeouts[name] == true then
master.timeouts[name] = nil master.timeouts[name] = nil
@ -512,10 +573,12 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
end end
thread.sleep(1) thread.sleep(1)
end end
end) end
)
end end
client.name = name client.name = name
client.OnDataRecieved(function(client,data) client.OnDataRecieved(
function(client, data)
local cmd = byte(data:sub(1, 1)) -- the first byte is the command 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 local dat = data:sub(2, -1) -- the data that you want to read
master.trigger(nodename) master.trigger(nodename)
@ -541,9 +604,12 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
local name, load = dat:match("(.-)|(.+)") local name, load = dat:match("(.-)|(.+)")
master.loads[name] = tonumber(load) master.loads[name] = tonumber(load)
end end
end) end
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 {
init = function()
return GLOBAL return GLOBAL
end} end
}

View File

@ -21,7 +21,7 @@ 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
@ -47,7 +47,9 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
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
return
end
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
@ -68,7 +70,9 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
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
return
end
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
@ -130,11 +134,14 @@ function multi:newSystemThreadedConnection(name,protect)
end end
local conn = {} local conn = {}
conn.obj = multi:newConnection() conn.obj = multi:newConnection()
setmetatable(conn,{ setmetatable(
conn,
{
__call = function(self, ...) __call = function(self, ...)
return self:connect(...) return self:connect(...)
end 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()
@ -165,14 +172,17 @@ function multi:newSystemThreadedConnection(name,protect)
break break
end end
end end
if not good then return multi.print("NonExisting Connection!") end if not good then
return multi.print("NonExisting Connection!")
end
fire:push {to, ID, {...}} 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(
function()
data = fire:peek() data = fire:peek()
if type(data) == "table" and data[1] == ID then if type(data) == "table" and data[1] == ID then
if data[2] == ID and conn.IgnoreSelf then if data[2] == ID and conn.IgnoreSelf then
@ -185,9 +195,11 @@ function multi:newSystemThreadedConnection(name,protect)
data = sync:peek() data = sync:peek()
if data ~= nil and data[1] == "SYNCA" and data[2] == ID then if data ~= nil and data[1] == "SYNCA" and data[2] == ID then
sync:pop() sync:pop()
multi.nextStep(function() multi.nextStep(
function()
conn.OnConnectionAdded:Fire(data[3]) conn.OnConnectionAdded:Fire(data[3])
end) 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
@ -198,17 +210,22 @@ function multi:newSystemThreadedConnection(name,protect)
end end
end end
end end
end):setName("STConn.syncer") end
):setName("STConn.syncer")
return conn return conn
end end
local cleanUp = {} local cleanUp = {}
multi.OnSystemThreadDied(function(ThreadID) multi.OnSystemThreadDied(
function(ThreadID)
for i = 1, #syncs do for i = 1, #syncs do
connSync:push {"SYNCR", syncs[i], ThreadID} connSync:push {"SYNCR", syncs[i], ThreadID}
end end
cleanUp[ThreadID] = true cleanUp[ThreadID] = true
end) end
multi:newThread(c.name.." Connection-Handler",function() )
multi:newThread(
c.name .. " Connection-Handler",
function()
local data local data
local clock = os.clock local clock = os.clock
local syncs = {} local syncs = {}
@ -234,12 +251,15 @@ function multi:newSystemThreadedConnection(name,protect)
if data ~= nil and cleanUp[data[1]] then if data ~= nil and cleanUp[data[1]] then
local meh = data[1] local meh = data[1]
connFire:pop() -- lets remove dead thread stuff connFire:pop() -- lets remove dead thread stuff
multi:newAlarm(15):OnRing(function(a) multi:newAlarm(15):OnRing(
function(a)
cleanUp[meh] = nil cleanUp[meh] = nil
end) end
)
end end
end end
end) end
)
GLOBAL[c.name] = c GLOBAL[c.name] = c
return c return c
end end
@ -252,22 +272,30 @@ function multi:SystemThreadedBenchmark(n)
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(
"STHREAD_BENCH",
function(n)
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 -- we cannot have upvalues... in love2d globals, not locals must be used end -- we cannot have upvalues... in love2d globals, not locals must be used
queue = sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread! queue = sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
multi:benchMark(n):OnBench(function(self,count) multi:benchMark(n):OnBench(
function(self, count)
queue:push(count) queue:push(count)
sThread.kill() sThread.kill()
error("Thread was killed!") error("Thread was killed!")
end)
multi:mainloop()
end,n)
end end
multi:newThread("THREAD_BENCH",function() )
multi:mainloop()
end,
n
)
end
multi:newThread(
"THREAD_BENCH",
function()
local count = 0 local count = 0
local cc = 0 local cc = 0
while true do while true do
@ -282,7 +310,8 @@ function multi:SystemThreadedBenchmark(n)
end end
end end
end end
end) end
)
c.OnBench = multi:newConnection() c.OnBench = multi:newConnection()
return c return c
end end
@ -304,7 +333,8 @@ function multi:newSystemThreadedConsole(name)
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(
function()
local data = cc.stream:pop() local data = cc.stream:pop()
if data then if data then
local dat = table.remove(data, 1) local dat = table.remove(data, 1)
@ -314,7 +344,8 @@ function multi:newSystemThreadedConsole(name)
print(unpack(data)) print(unpack(data))
end end
end end
end):setName("ST.consoleSyncer") end
):setName("ST.consoleSyncer")
end end
else else
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init() cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
@ -357,14 +388,20 @@ function multi:newSystemThreadedTable(name)
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(
function(k, v)
link.tab[k] = v link.tab[k] = v
end) end
setmetatable(cc,{ )
setmetatable(
cc,
{
__index = function(t, k) __index = function(t, k)
return t.tab[k] return t.tab[k]
end, end,
@ -372,7 +409,8 @@ function multi:newSystemThreadedTable(name)
t.tab[k] = v t.tab[k] = v
t.conn:Fire(k, v) t.conn:Fire(k, v)
end end
}) }
)
return cc return cc
end end
GLOBAL[c.name] = c GLOBAL[c.name] = c
@ -438,7 +476,9 @@ function multi:newSystemThreadedJobQueue(a,b)
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(
c.name .. " Worker Thread #" .. i,
function(name)
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
@ -453,7 +493,8 @@ function multi:newSystemThreadedJobQueue(a,b)
local REG = sThread.waitFor("QUEUE_REG_" .. name):init() local REG = sThread.waitFor("QUEUE_REG_" .. name):init()
local DA = sThread.waitFor("QUEUE_DA_" .. name):init() local DA = sThread.waitFor("QUEUE_DA_" .. name):init()
local lastjob = os.clock() local lastjob = os.clock()
multi:newLoop(function() multi:newLoop(
function()
local job = JQI:pop() local job = JQI:pop()
local rd = REG:peek() local rd = REG:peek()
local da = DA:peek() local da = DA:peek()
@ -471,10 +512,12 @@ function multi:newSystemThreadedJobQueue(a,b)
da[2](multi) da[2](multi)
da = nil da = nil
DA:pop() DA:pop()
multi:newAlarm(60):OnRing(function(a) multi:newAlarm(60):OnRing(
function(a)
ids[meh] = nil ids[meh] = nil
a:Destroy() a:Destroy()
end) end
)
end end
end end
if job then if job then
@ -487,24 +530,34 @@ function multi:newSystemThreadedJobQueue(a,b)
JD:push({ID, FUNCS:waitFor(_name)(unpack(job))}) JD:push({ID, FUNCS:waitFor(_name)(unpack(job))})
end end
end end
end) end
multi:newLoop(function() )
multi:newLoop(
function()
if os.clock() - lastjob > 1 then if os.clock() - lastjob > 1 then
sThread.sleep(.1) sThread.sleep(.1)
end end
end) end
setmetatable(_G,{ )
setmetatable(
_G,
{
__index = function(t, k) __index = function(t, k)
return FUNCS[k] return FUNCS[k]
end end
}) }
)
if not love then if not love then
multi:mainloop() multi:mainloop()
end end
end,c.name) end,
c.name
)
end end
local clock = os.clock local clock = os.clock
multi:newThread("JQ-"..c.name.." Manager",function() multi:newThread(
"JQ-" .. c.name .. " Manager",
function()
local _count = 0 local _count = 0
while _count < c.numberofcores do while _count < c.numberofcores do
thread.skip() thread.skip()
@ -534,7 +587,8 @@ function multi:newSystemThreadedJobQueue(a,b)
c.OnJobCompleted:Fire(unpack(dat)) 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)
@ -544,7 +598,9 @@ function multi:newSystemThreadedExecute(cmd)
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(
name,
function()
if love then -- lets make sure we don't reference upvalues if using love2d if love then -- lets make sure we don't reference upvalues if using love2d
GLOBAL = _G.GLOBAL GLOBAL = _G.GLOBAL
sThread = _G.sThread sThread = _G.sThread
@ -553,15 +609,19 @@ function multi:newSystemThreadedExecute(cmd)
cmd = sThread.waitFor(name .. "CMD") cmd = sThread.waitFor(name .. "CMD")
local ret = os.execute(cmd) local ret = os.execute(cmd)
GLOBAL[name .. "R"] = ret GLOBAL[name .. "R"] = ret
end) end
)
c.OnCMDFinished = multi:newConnection() c.OnCMDFinished = multi:newConnection()
c.looper=multi:newLoop(function(self) c.looper =
multi:newLoop(
function(self)
local ret = GLOBAL[self.link.name .. "R"] local ret = GLOBAL[self.link.name .. "R"]
if ret then if ret then
self.link.OnCMDFinished:Fire(ret) self.link.OnCMDFinished:Fire(ret)
self:Destroy() self:Destroy()
end end
end) end
)
c.looper.link = c c.looper.link = c
return c return c
end end