mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
13.0.0 Release
Had an issue when managing branches... All fixed now
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
Copyright (c) 2019 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -37,6 +37,7 @@ multi.OnMouseMoved = multi:newConnection()
|
||||
multi.OnDraw = multi:newConnection()
|
||||
multi.OnTextInput = multi:newConnection()
|
||||
multi.OnUpdate = multi:newConnection()
|
||||
multi.OnQuit = multi:newConnection()
|
||||
multi.OnPreLoad(function()
|
||||
local function Hook(func,conn)
|
||||
if love[func]~=nil then
|
||||
@@ -51,6 +52,7 @@ multi.OnPreLoad(function()
|
||||
end
|
||||
end
|
||||
end
|
||||
Hook("quit",multi.OnQuit)
|
||||
Hook("keypressed",multi.OnKeyPressed)
|
||||
Hook("keyreleased",multi.OnKeyReleased)
|
||||
Hook("mousepressed",multi.OnMousePressed)
|
||||
@@ -67,4 +69,8 @@ multi.OnPreLoad(function()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
return multi
|
||||
multi.OnQuit(function()
|
||||
multi.Stop()
|
||||
love.event.quit()
|
||||
end)
|
||||
return multi
|
||||
|
||||
+1154
-1263
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
Copyright (c) 2019 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -32,6 +32,8 @@ end
|
||||
-- Step 1 get lanes
|
||||
lanes=require("lanes").configure()
|
||||
local multi = require("multi") -- get it all and have it on all lanes
|
||||
multi.SystemThreads = {}
|
||||
local thread = thread
|
||||
multi.isMainThread=true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
@@ -39,10 +41,10 @@ end
|
||||
function multi:getPlatform()
|
||||
return "lanes"
|
||||
end
|
||||
-- Step 2 set up the linda objects
|
||||
-- Step 2 set up the Linda objects
|
||||
local __GlobalLinda = lanes.linda() -- handles global stuff
|
||||
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
|
||||
-- For convience a GLOBAL table will be constructed to handle requests
|
||||
-- For convenience a GLOBAL table will be constructed to handle requests
|
||||
local GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
@@ -52,7 +54,7 @@ setmetatable(GLOBAL,{
|
||||
__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={}
|
||||
function THREAD.set(name,val)
|
||||
__GlobalLinda:set(name,val)
|
||||
@@ -82,6 +84,9 @@ end
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
function THREAD.getThreads()
|
||||
return GLOBAL.__THREADS__
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
@@ -93,6 +98,10 @@ end
|
||||
function THREAD.getName()
|
||||
return THREAD_NAME
|
||||
end
|
||||
function THREAD.getID()
|
||||
return THREAD_ID
|
||||
end
|
||||
_G.THREAD_ID = 0
|
||||
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait
|
||||
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
|
||||
however, a linda recieve will in fact be a idle wait! So we use that and wrap it in a nice package]]
|
||||
@@ -109,36 +118,74 @@ function THREAD.hold(n)
|
||||
end
|
||||
local rand = math.random(1,10000000)
|
||||
-- Step 5 Basic Threads!
|
||||
local threads = {}
|
||||
local count = 1
|
||||
local started = false
|
||||
local livingThreads = {}
|
||||
function multi:newSystemThread(name,func,...)
|
||||
multi.InitSystemThreadErrorHandler()
|
||||
rand = math.random(1,10000000)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
c.Name = name
|
||||
c.Id = count
|
||||
livingThreads[count] = {true,name}
|
||||
local THREAD_ID = count
|
||||
count = count + 1
|
||||
c.Type="sthread"
|
||||
c.creationTime = os.clock()
|
||||
local THREAD_NAME=name
|
||||
local function func2(...)
|
||||
local multi = require("multi")
|
||||
_G["THREAD_NAME"]=THREAD_NAME
|
||||
_G["THREAD_ID"]=THREAD_ID
|
||||
math.randomseed(rand)
|
||||
func(...)
|
||||
if _G.__Needs_Multi then
|
||||
multi:mainloop()
|
||||
end
|
||||
THREAD.kill()
|
||||
end
|
||||
c.thread=lanes.gen("*", func2)(...)
|
||||
function c:kill()
|
||||
--self.status:Destroy()
|
||||
self.thread:cancel()
|
||||
print("Thread: '"..self.name.."' has been stopped!")
|
||||
multi.print("Thread: '"..self.name.."' has been stopped!")
|
||||
end
|
||||
c.status=multi:newUpdater(multi.Priority_IDLE)
|
||||
c.status.link=c
|
||||
c.status:OnUpdate(function(self)
|
||||
local v,err,t=self.link.thread:join(.001)
|
||||
if err then
|
||||
multi.OnError:Fire(self.link,err,"Error in systemThread: '"..self.link.name.."' <"..err..">")
|
||||
self:Destroy()
|
||||
end
|
||||
end)
|
||||
table.insert(multi.SystemThreads,c)
|
||||
c.OnError = multi:newConnection()
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
return c
|
||||
end
|
||||
print("Integrated Lanes!")
|
||||
multi.OnSystemThreadDied = multi:newConnection()
|
||||
function multi.InitSystemThreadErrorHandler()
|
||||
if started==true then return end
|
||||
started = true
|
||||
multi:newThread("ThreadErrorHandler",function()
|
||||
local threads = multi.SystemThreads
|
||||
while true do
|
||||
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
|
||||
for i=#threads,1,-1 do
|
||||
local v,err,t=threads[i].thread:join(.001)
|
||||
if err then
|
||||
if err:find("Thread was killed!") then
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
else
|
||||
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
multi.print("Integrated Lanes!")
|
||||
multi.integration={} -- for module creators
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
Copyright (c) 2019 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,6 +34,7 @@ multi.integration.love2d.ThreadBase=[[
|
||||
tab={...}
|
||||
__THREADID__=table.remove(tab,1)
|
||||
__THREADNAME__=table.remove(tab,1)
|
||||
THREAD_ID=table.remove(tab,1)
|
||||
require("love.filesystem")
|
||||
require("love.system")
|
||||
require("love.timer")
|
||||
@@ -167,6 +168,9 @@ end
|
||||
function sThread.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
function sThread.getID()
|
||||
return THREAD_ID
|
||||
end
|
||||
function sThread.kill()
|
||||
error("Thread was killed!")
|
||||
end
|
||||
@@ -195,6 +199,7 @@ func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
|
||||
multi:mainloop()
|
||||
]]
|
||||
GLOBAL={} -- Allow main thread to interact with these objects as well
|
||||
_G.THREAD_ID = 0
|
||||
__proxy__={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
@@ -214,9 +219,13 @@ setmetatable(GLOBAL,{
|
||||
THREAD={} -- Allow main thread to interact with these objects as well
|
||||
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
||||
isMainThread=true
|
||||
multi.SystemThreads = {}
|
||||
function THREAD.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
function THREAD.getID()
|
||||
return THREAD_ID
|
||||
end
|
||||
function ToStr(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
@@ -295,12 +304,19 @@ local function randomString(n)
|
||||
end
|
||||
return str
|
||||
end
|
||||
local count = 1
|
||||
local livingThreads = {}
|
||||
function multi:newSystemThread(name,func,...) -- the main method
|
||||
multi.InitSystemThreadErrorHandler()
|
||||
local c={}
|
||||
c.name=name
|
||||
c.Name = name
|
||||
c.ID=c.name.."<ID|"..randomString(8)..">"
|
||||
c.Id=count
|
||||
count = count + 1
|
||||
livingThreads[count] = {true,name}
|
||||
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
||||
c.thread:start(c.ID,c.name,...)
|
||||
c.thread:start(c.ID,c.name,THREAD_ID,...)
|
||||
function c:kill()
|
||||
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
|
||||
end
|
||||
@@ -308,7 +324,7 @@ function multi:newSystemThread(name,func,...) -- the main method
|
||||
end
|
||||
function love.threaderror( thread, errorstr )
|
||||
multi.OnError:Fire(thread,errorstr)
|
||||
print("Error in systemThread: "..tostring(thread)..": "..errorstr)
|
||||
multi.print("Error in systemThread: "..tostring(thread)..": "..errorstr)
|
||||
end
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
@@ -333,8 +349,7 @@ end
|
||||
__channels__={}
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
updater=multi:newUpdater()
|
||||
updater:OnUpdate(function(self)
|
||||
updater=multi:newLoop(function(self)
|
||||
local data=multi.integration.love2d.mainChannel:pop()
|
||||
while data do
|
||||
if type(data)=="string" then
|
||||
@@ -365,8 +380,37 @@ updater:OnUpdate(function(self)
|
||||
data=multi.integration.love2d.mainChannel:pop()
|
||||
end
|
||||
end)
|
||||
multi.OnSystemThreadDied = multi:newConnection()
|
||||
local started = false
|
||||
function multi.InitSystemThreadErrorHandler()
|
||||
if started==true then return end
|
||||
started = true
|
||||
multi:newThread("ThreadErrorHandler",function()
|
||||
local threads = multi.SystemThreads
|
||||
while true do
|
||||
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
|
||||
for i=#threads,1,-1 do
|
||||
local v,err,t=threads[i].thread:join(.001)
|
||||
if err then
|
||||
if err:find("Thread was killed!") then
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
else
|
||||
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
|
||||
livingThreads[threads[i].Id] = {false,threads[i].Name}
|
||||
multi.OnSystemThreadDied:Fire(threads[i].Id)
|
||||
GLOBAL["__THREADS__"]=livingThreads
|
||||
table.remove(threads,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
require("multi.integration.shared")
|
||||
print("Integrated Love2d!")
|
||||
multi.print("Integrated Love2d!")
|
||||
return {
|
||||
init=function(t)
|
||||
if t then
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
Copyright (c) 2019 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -114,7 +114,7 @@ local function _INIT(luvitThread,timer)
|
||||
luvitThread.start(entry,package.path,name,c.func,...)
|
||||
return c
|
||||
end
|
||||
print("Integrated Luvit!")
|
||||
multi.print("Integrated Luvit!")
|
||||
multi.integration={} -- for module creators
|
||||
multi.integration.GLOBAL=GLOBAL
|
||||
multi.integration.THREAD=THREAD
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
Copyright (c) 2019 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@ SOFTWARE.
|
||||
]]
|
||||
local multi = require("multi")
|
||||
local net = require("net")
|
||||
require("bin")
|
||||
local bin = require("bin")
|
||||
bin.setBitsInterface(infinabits) -- the bits interface does not work so well, another bug to fix
|
||||
|
||||
-- Commands that the master and node will respect, max of 256 commands
|
||||
@@ -42,6 +42,7 @@ local CMD_CONSOLE = 0x0B
|
||||
|
||||
local char = string.char
|
||||
local byte = string.byte
|
||||
-- Process to hold all of the networkManager's muilt objects
|
||||
|
||||
-- Helper for piecing commands
|
||||
local function pieceCommand(cmd,...)
|
||||
@@ -142,17 +143,20 @@ function multi:nodeManager(port)
|
||||
server.OnDataRecieved(function(server,data,cid,ip,port)
|
||||
local cmd = data:sub(1,1)
|
||||
if cmd == "R" then
|
||||
multi:newTLoop(function(loop)
|
||||
if server.timeouts[cid]==true then
|
||||
server.OnNodeRemoved:Fire(server.nodes[cid])
|
||||
server.nodes[cid] = nil
|
||||
server.timeouts[cid] = nil
|
||||
loop:Destroy()
|
||||
return
|
||||
multi:newThread("Node Client Manager",function(loop)
|
||||
while true do
|
||||
if server.timeouts[cid]==true then
|
||||
server.OnNodeRemoved:Fire(server.nodes[cid])
|
||||
server.nodes[cid] = nil
|
||||
server.timeouts[cid] = nil
|
||||
thread.kill()
|
||||
else
|
||||
server.timeouts[cid] = true
|
||||
server:send(cid,"ping")
|
||||
end
|
||||
thread.sleep(1)
|
||||
end
|
||||
server.timeouts[cid] = true
|
||||
server:send(cid,"ping")
|
||||
end,1)
|
||||
end)
|
||||
server.nodes[cid]=data:sub(2,-1)
|
||||
server.OnNodeAdded:Fire(server.nodes[cid])
|
||||
elseif cmd == "G" then
|
||||
@@ -172,6 +176,7 @@ function multi:nodeManager(port)
|
||||
end
|
||||
-- The main driving force of the network manager: Nodes
|
||||
function multi:newNode(settings)
|
||||
multi:enableLoadDetection()
|
||||
settings = settings or {}
|
||||
-- Here we have to use the net library to broadcast our node across the network
|
||||
math.randomseed(os.time())
|
||||
@@ -189,21 +194,21 @@ function multi:newNode(settings)
|
||||
node.hasFuncs = {}
|
||||
node.OnError = multi:newConnection()
|
||||
node.OnError(function(node,err,master)
|
||||
print("ERROR",err,node.name)
|
||||
multi.print("ERROR",err,node.name)
|
||||
local temp = bin.new()
|
||||
temp:addBlock(#node.name,2)
|
||||
temp:addBlock(node.name)
|
||||
temp:addBlock(#err,2)
|
||||
temp:addBlock(err)
|
||||
for i,v in pairs(node.connections) do
|
||||
print(i)
|
||||
multi.print(i)
|
||||
v[1]:send(v[2],char(CMD_ERROR)..temp.data,v[3])
|
||||
end
|
||||
end)
|
||||
if settings.managerDetails then
|
||||
local c = net:newTCPClient(settings.managerDetails[1],settings.managerDetails[2])
|
||||
if not c then
|
||||
print("Cannot connect to the node manager! Ensuring broadcast is enabled!") settings.noBroadCast = false
|
||||
multi.print("Cannot connect to the node manager! Ensuring broadcast is enabled!") settings.noBroadCast = false
|
||||
else
|
||||
c.OnDataRecieved(function(self,data)
|
||||
if data == "ping" then
|
||||
@@ -215,7 +220,7 @@ function multi:newNode(settings)
|
||||
end
|
||||
if not settings.preload then
|
||||
if node.functions:getSize()~=0 then
|
||||
print("We have function(s) to preload!")
|
||||
multi.print("We have function(s) to preload!")
|
||||
local len = node.functions:getBlock("n",1)
|
||||
local name,func
|
||||
while len do
|
||||
@@ -265,14 +270,14 @@ function multi:newNode(settings)
|
||||
node.queue:push(resolveData(dat))
|
||||
elseif cmd == CMD_REG then
|
||||
if not settings.allowRemoteRegistering then
|
||||
print(ip..": has attempted to register a function when it is currently not allowed!")
|
||||
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
|
||||
print("Function already preloaded onto the node!")
|
||||
multi.print("Function already preloaded onto the node!")
|
||||
return
|
||||
end
|
||||
len = temp:getBlock("n",2)
|
||||
@@ -283,7 +288,7 @@ function multi:newNode(settings)
|
||||
local temp = bin.new(dat)
|
||||
local len = temp:getBlock("n",1)
|
||||
local name = temp:getBlock("s",len)
|
||||
len = temp:getBlock("n",2)
|
||||
len = temp:getBlock("n",4)
|
||||
local args = temp:getBlock("s",len)
|
||||
_G[name](unpack(resolveData(args)))
|
||||
elseif cmd == CMD_TASK then
|
||||
@@ -299,13 +304,13 @@ function multi:newNode(settings)
|
||||
node.OnError:Fire(node,err,server)
|
||||
end
|
||||
elseif cmd == CMD_INITNODE then
|
||||
print("Connected with another node!")
|
||||
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
|
||||
print("Connected to the master!",dat)
|
||||
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)
|
||||
@@ -352,7 +357,7 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
if settings.managerDetails then
|
||||
local client = net:newTCPClient(settings.managerDetails[1],settings.managerDetails[2])
|
||||
if not client then
|
||||
print("Cannot connect to the node manager! Ensuring broadcast listening is enabled!") settings.noBroadCast = false
|
||||
multi.print("Warning: Cannot connect to the node manager! Ensuring broadcast listening is enabled!") settings.noBroadCast = false
|
||||
else
|
||||
client.OnDataRecieved(function(client,data)
|
||||
local cmd = data:sub(1,1)
|
||||
@@ -402,7 +407,7 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
temp:addBlock(CMD_CALL,1)
|
||||
temp:addBlock(#name,1)
|
||||
temp:addBlock(name,#name)
|
||||
temp:addBlock(#args,2)
|
||||
temp:addBlock(#args,4)
|
||||
temp:addBlock(args,#args)
|
||||
master:sendTo(node,temp.data)
|
||||
end
|
||||
@@ -436,12 +441,12 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
name = self:getRandomNode()
|
||||
end
|
||||
if name==nil then
|
||||
multi:newTLoop(function(loop)
|
||||
if name~=nil then
|
||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
||||
loop:Desrtoy()
|
||||
end
|
||||
end,.1)
|
||||
multi:newEvent(function() return name~=nil end):OnEvent(function(evnt)
|
||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
||||
evnt:Destroy()
|
||||
end):SetName("DelayedSendTask"):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self)
|
||||
self:Destroy()
|
||||
end)
|
||||
else
|
||||
self:sendTo(name,char(CMD_TASK)..len..aData..len2..fData)
|
||||
end
|
||||
@@ -455,12 +460,12 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
name = "NODE_"..name
|
||||
end
|
||||
if self.connections[name]==nil then
|
||||
multi:newTLoop(function(loop)
|
||||
if self.connections[name]~=nil then
|
||||
self.connections[name]:send(data)
|
||||
loop:Destroy()
|
||||
end
|
||||
end,.1)
|
||||
multi:newEvent(function() return self.connections[name]~=nil end):OnEvent(function(evnt)
|
||||
self.connections[name]:send(data)
|
||||
evnt:Destroy()
|
||||
end):SetName("DelayedSendTask"):SetTime(8):OnTimedOut(function(self)
|
||||
self:Destroy()
|
||||
end)
|
||||
else
|
||||
self.connections[name]:send(data)
|
||||
end
|
||||
@@ -495,16 +500,19 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
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:newTLoop(function(loop)
|
||||
if master.timeouts[name]==true then
|
||||
master.timeouts[name] = nil
|
||||
master.connections[name] = nil
|
||||
loop:Destroy()
|
||||
return
|
||||
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
|
||||
master.timeouts[name] = true
|
||||
master:sendTo(name,char(CMD_PING))
|
||||
end,1)
|
||||
end)
|
||||
end
|
||||
client.name = name
|
||||
client.OnDataRecieved(function(client,data)
|
||||
@@ -542,7 +550,7 @@ function multi:newMaster(settings) -- You will be able to have more than one mas
|
||||
return master
|
||||
end
|
||||
-- The init function that gets returned
|
||||
print("Integrated Network Parallelism")
|
||||
multi.print("Integrated Network Parallelism")
|
||||
return {init = function()
|
||||
return GLOBAL
|
||||
end}
|
||||
|
||||
+134
-66
@@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
Copyright (c) 2019 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -112,30 +112,37 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedConnection(name,protect)
|
||||
local c={}
|
||||
c.name = name or error("You must provide a name for the connection object!")
|
||||
c.protect = protect or false
|
||||
c.idle = nil
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
c.name = name or error("You must supply a name for this object!")
|
||||
c.protect = protect or false
|
||||
c.count = 0
|
||||
multi:newSystemThreadedQueue(name.."THREADED_CALLFIRE"):init()
|
||||
local qsm = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNCM"):init()
|
||||
local qs = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNC"):init()
|
||||
local connSync = multi:newSystemThreadedQueue(c.name.."_CONN_SYNC")
|
||||
local connFire = multi:newSystemThreadedQueue(c.name.."_CONN_FIRE")
|
||||
function c:init()
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local conns = 0
|
||||
local qF = sThread.waitFor(self.name.."THREADED_CALLFIRE"):init()
|
||||
local qSM = sThread.waitFor(self.name.."THREADED_CALLSYNCM"):init()
|
||||
local qS = sThread.waitFor(self.name.."THREADED_CALLSYNC"):init()
|
||||
qSM:push("OK")
|
||||
local conn = {}
|
||||
conn.obj = multi:newConnection(self.protect)
|
||||
setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
|
||||
conn.obj = multi:newConnection()
|
||||
setmetatable(conn,{
|
||||
__call=function(self,...)
|
||||
return self:connect(...)
|
||||
end
|
||||
})
|
||||
local ID = sThread.getID()
|
||||
local sync = sThread.waitFor(self.name.."_CONN_SYNC"):init()
|
||||
local fire = sThread.waitFor(self.name.."_CONN_FIRE"):init()
|
||||
local connections = {}
|
||||
if not multi.isMainThread then
|
||||
connections = {0}
|
||||
end
|
||||
sync:push{"INIT",ID} -- Register this as an active connection!
|
||||
function conn:connect(func)
|
||||
return self.obj(func)
|
||||
end
|
||||
@@ -146,54 +153,98 @@ function multi:newSystemThreadedConnection(name,protect)
|
||||
self.obj:Remove()
|
||||
end
|
||||
function conn:Fire(...)
|
||||
local args = {multi.randomString(8),...}
|
||||
for i = 1, conns do
|
||||
qF:push(args)
|
||||
for i = 1,#connections do
|
||||
fire:push{connections[i],ID,{...}}
|
||||
end
|
||||
end
|
||||
local lastID = ""
|
||||
local lastCount = 0
|
||||
multi:newThread("syncer",function()
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local fire = qF:peek()
|
||||
local count = qS:peek()
|
||||
if fire and fire[1]~=lastID then
|
||||
lastID = fire[1]
|
||||
qF:pop()
|
||||
table.remove(fire,1)
|
||||
conn.obj:Fire(unpack(fire))
|
||||
end
|
||||
if count and count[1]~=lastCount then
|
||||
conns = count[2]
|
||||
lastCount = count[1]
|
||||
qs:pop()
|
||||
function conn:FireTo(to,...)
|
||||
local good = false
|
||||
for i = 1,#connections do
|
||||
if connections[i]==to then
|
||||
good = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
if not good then return multi.print("NonExisting Connection!") end
|
||||
fire:push{to,ID,{...}}
|
||||
end
|
||||
-- FIRE {TO,FROM,{ARGS}}
|
||||
local data
|
||||
local clock = os.clock
|
||||
conn.OnConnectionAdded = multi:newConnection()
|
||||
multi:newLoop(function()
|
||||
data = fire:peek()
|
||||
if type(data)=="table" and data[1]==ID then
|
||||
if data[2]==ID and conn.IgnoreSelf then
|
||||
fire:pop()
|
||||
return
|
||||
end
|
||||
fire:pop()
|
||||
conn.obj:Fire(unpack(data[3]))
|
||||
end
|
||||
data = sync:peek()
|
||||
if data~=nil and data[1]=="SYNCA" and data[2]==ID then
|
||||
sync:pop()
|
||||
multi.nextStep(function()
|
||||
conn.OnConnectionAdded:Fire(data[3])
|
||||
end)
|
||||
table.insert(connections,data[3])
|
||||
end
|
||||
if type(data)=="table" and data[1]=="SYNCR" and data[2]==ID then
|
||||
sync:pop()
|
||||
for i=1,#connections do
|
||||
if connections[i] == data[3] then
|
||||
table.remove(connections,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end):setName("STConn.syncer")
|
||||
return conn
|
||||
end
|
||||
multi:newThread("connSync",function()
|
||||
local cleanUp = {}
|
||||
multi.OnSystemThreadDied(function(ThreadID)
|
||||
for i=1,#syncs do
|
||||
connSync:push{"SYNCR",syncs[i],ThreadID}
|
||||
end
|
||||
cleanUp[ThreadID] = true
|
||||
end)
|
||||
multi:newThread(c.name.." Connection-Handler",function()
|
||||
local data
|
||||
local clock = os.clock
|
||||
local syncs = {}
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local syncIN = qsm:pop()
|
||||
if syncIN then
|
||||
if syncIN=="OK" then
|
||||
c.count = c.count + 1
|
||||
else
|
||||
c.count = c.count - 1
|
||||
if not c.idle then
|
||||
thread.sleep(.5)
|
||||
else
|
||||
if clock() - c.idle >= 15 then
|
||||
c.idle = nil
|
||||
end
|
||||
local rand = math.random(1,1000000)
|
||||
for i = 1, c.count do
|
||||
qs:push({rand,c.count})
|
||||
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)
|
||||
GLOBAL[name]=c
|
||||
GLOBAL[c.name]=c
|
||||
return c
|
||||
end
|
||||
function multi:systemThreadedBenchmark(n)
|
||||
|
||||
function multi:SystemThreadedBenchmark(n)
|
||||
n=n or 1
|
||||
local cores=multi.integration.THREAD.getCores()
|
||||
local queue=multi:newSystemThreadedQueue("THREAD_BENCH_QUEUE"):init()
|
||||
@@ -211,6 +262,7 @@ function multi:systemThreadedBenchmark(n)
|
||||
multi:benchMark(n):OnBench(function(self,count)
|
||||
queue:push(count)
|
||||
sThread.kill()
|
||||
error("Thread was killed!")
|
||||
end)
|
||||
multi:mainloop()
|
||||
end,n)
|
||||
@@ -240,6 +292,7 @@ function multi:newSystemThreadedConsole(name)
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init()
|
||||
_G.__Needs_Multi = true
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
@@ -247,10 +300,10 @@ function multi:newSystemThreadedConsole(name)
|
||||
end
|
||||
local cc={}
|
||||
if multi.isMainThread then
|
||||
if GLOBAL["__SYSTEM_CONSLOE__"] then
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSLOE__"):init()
|
||||
if GLOBAL["__SYSTEM_CONSOLE__"] then
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
||||
else
|
||||
cc.stream = multi:newSystemThreadedQueue("__SYSTEM_CONSLOE__"):init()
|
||||
cc.stream = multi:newSystemThreadedQueue("__SYSTEM_CONSOLE__"):init()
|
||||
multi:newLoop(function()
|
||||
local data = cc.stream:pop()
|
||||
if data then
|
||||
@@ -261,10 +314,10 @@ function multi:newSystemThreadedConsole(name)
|
||||
print(unpack(data))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end):setName("ST.consoleSyncer")
|
||||
end
|
||||
else
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSLOE__"):init()
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSOLE__"):init()
|
||||
end
|
||||
function cc:write(msg)
|
||||
self.stream:push({"w",tostring(msg)})
|
||||
@@ -281,12 +334,14 @@ function multi:newSystemThreadedConsole(name)
|
||||
GLOBAL[c.name]=c
|
||||
return c
|
||||
end
|
||||
-- NEEDS WORK
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c={}
|
||||
c.name=name -- set the name this is important for identifying what is what
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
_G.__Needs_Multi = true
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
@@ -324,14 +379,16 @@ function multi:newSystemThreadedTable(name)
|
||||
return c
|
||||
end
|
||||
local jobqueuecount = 0
|
||||
local jqueues = {}
|
||||
function multi:newSystemThreadedJobQueue(a,b)
|
||||
jobqueuecount=jobqueuecount+1
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local sThread=multi.integration.THREAD
|
||||
local c = {}
|
||||
c.numberofcores = 4
|
||||
c.idle = nil
|
||||
c.name = "SYSTEM_THREADED_JOBQUEUE_"..jobqueuecount
|
||||
-- This is done to keep backwards compatability for older code
|
||||
-- This is done to keep backwards compatibility for older code
|
||||
if type(a)=="string" and not(b) then
|
||||
c.name = a
|
||||
elseif type(a)=="number" and not (b) then
|
||||
@@ -343,6 +400,10 @@ function multi:newSystemThreadedJobQueue(a,b)
|
||||
c.name = b
|
||||
c.numberofcores = a
|
||||
end
|
||||
if jqueues[c.name] then
|
||||
error("A job queue by the name: "..c.name.." already exists!")
|
||||
end
|
||||
jqueues[c.name] = true
|
||||
c.isReady = false
|
||||
c.jobnum=1
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
@@ -359,6 +420,7 @@ function multi:newSystemThreadedJobQueue(a,b)
|
||||
end
|
||||
c.tempQueue = {}
|
||||
function c:pushJob(name,...)
|
||||
c.idle = os.clock()
|
||||
if not self.isReady then
|
||||
table.insert(c.tempQueue,{self.jobnum,name,...})
|
||||
self.jobnum=self.jobnum+1
|
||||
@@ -370,8 +432,9 @@ function multi:newSystemThreadedJobQueue(a,b)
|
||||
end
|
||||
end
|
||||
function c:doToAll(func)
|
||||
local r = multi.randomString(12)
|
||||
for i = 1, self.numberofcores do
|
||||
queueDA:push{multi.randomString(12),func}
|
||||
queueDA:push{r,func}
|
||||
end
|
||||
end
|
||||
for i=1,c.numberofcores do
|
||||
@@ -425,12 +488,9 @@ function multi:newSystemThreadedJobQueue(a,b)
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:newThread("Idler",function()
|
||||
while true do
|
||||
if os.clock()-lastjob>1 then
|
||||
sThread.sleep(.1)
|
||||
end
|
||||
thread.sleep(.001)
|
||||
multi:newLoop(function()
|
||||
if os.clock()-lastjob>1 then
|
||||
sThread.sleep(.1)
|
||||
end
|
||||
end)
|
||||
setmetatable(_G,{
|
||||
@@ -443,11 +503,11 @@ function multi:newSystemThreadedJobQueue(a,b)
|
||||
end
|
||||
end,c.name)
|
||||
end
|
||||
multi:newThread("counter",function()
|
||||
print("thread started")
|
||||
local clock = os.clock
|
||||
multi:newThread("JQ-"..c.name.." Manager",function()
|
||||
local _count = 0
|
||||
while _count<c.numberofcores do
|
||||
thread.skip(1)
|
||||
thread.skip()
|
||||
if queueCC:pop() then
|
||||
_count = _count + 1
|
||||
end
|
||||
@@ -460,9 +520,17 @@ function multi:newSystemThreadedJobQueue(a,b)
|
||||
c.OnReady:Fire(c)
|
||||
local dat
|
||||
while true do
|
||||
thread.skip(1)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user