mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
Added intergration loveManager
Adds multi.intergrations.loveManager,lua Created an example file for you to look at
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
require("multi")
|
||||
require("multi.threading")
|
||||
require("multi.threading.alarm")
|
||||
require("multi.threading.event")
|
||||
require("multi.threading.loop")
|
||||
require("multi.threading.process")
|
||||
require("multi.threading.step")
|
||||
require("multi.threading.tstep")
|
||||
require("multi.threading.updater")
|
||||
@@ -0,0 +1,305 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
multi.OnObjectCreated(function(obj)
|
||||
if obj.Type=="loop" then
|
||||
function obj:Act()
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.Parent.clock()-self.Start,self)
|
||||
end
|
||||
end
|
||||
elseif obj.Type=="step" then
|
||||
function obj:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
elseif obj.Type=="tstep" then
|
||||
function c:Act()
|
||||
if self.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
if thread then
|
||||
function multi:newThreadedLoop(name,func)
|
||||
local c=self:newTBase()
|
||||
c.Type='loopThread'
|
||||
c.Start=os.clock()
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate)
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](os.clock()-self.Start,c)
|
||||
end
|
||||
thread.sleep(c.updaterate)
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
function multi:newThreadedStep(name,start,reset,count,skip)
|
||||
local c=self:newTBase()
|
||||
local think=1
|
||||
c.Type='stepThread'
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.Reset=c.Resume
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.rest=true
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
c.updaterate=0
|
||||
c.restRate=.1
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:sleep(c.restRate)
|
||||
else
|
||||
if c~=nil then
|
||||
if c.spos==0 then
|
||||
if c.pos==c.start then
|
||||
for fe=1,#c.funcS do
|
||||
c.funcS[fe](c)
|
||||
end
|
||||
end
|
||||
for i=1,#c.func do
|
||||
c.func[i](c.pos,c)
|
||||
end
|
||||
c.pos=c.pos+c.count
|
||||
if c.pos-c.count==c.endAt then
|
||||
c:Pause()
|
||||
for fe=1,#c.funcE do
|
||||
c.funcE[fe](c)
|
||||
end
|
||||
c.pos=c.start
|
||||
end
|
||||
end
|
||||
end
|
||||
c.spos=c.spos+1
|
||||
if c.spos>=c.skip then
|
||||
c.spos=0
|
||||
end
|
||||
ref:sleep(c.updaterate)
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
function multi:newThreadedTStep(name,start,reset,count,set)
|
||||
local c=self:newTBase()
|
||||
local think=1
|
||||
c.Type='tstepThread'
|
||||
c.Priority=self.Priority_Low
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
c.updaterate=0
|
||||
c.restRate=0
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate)
|
||||
else
|
||||
if os.clock()-c.timer>=c.set then
|
||||
c:Reset()
|
||||
if c.pos==c.start then
|
||||
for fe=1,#c.funcS do
|
||||
c.funcS[fe](c)
|
||||
end
|
||||
end
|
||||
for i=1,#c.func do
|
||||
c.func[i](c.pos,c)
|
||||
end
|
||||
c.pos=c.pos+c.count
|
||||
if c.pos-c.count==c.endAt then
|
||||
c:Pause()
|
||||
for fe=1,#c.funcE do
|
||||
c.funcE[fe](c)
|
||||
end
|
||||
c.pos=c.start
|
||||
end
|
||||
end
|
||||
thread.skip(c.updaterate)
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,127 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.all")
|
||||
os.sleep=love.timer.sleep
|
||||
--~ function bin.load(file,s,r)
|
||||
--~ content, size = love.filesystem.read(file)
|
||||
--~ local temp=bin.new(content)
|
||||
--~ temp.filepath=file
|
||||
--~ return temp
|
||||
--~ end
|
||||
--~ function bin:tofile(filename)
|
||||
--~ if not(filename) or self.Stream then return nil end
|
||||
--~ love.filesystem.write(filename,self.data)
|
||||
--~ end
|
||||
--~ function bin.stream(file,l)
|
||||
--~ error("Sorry streaming is not available when using love2d :(, I am looking for a solution though :)")
|
||||
--~ end
|
||||
function love.run()
|
||||
if love.math then
|
||||
love.math.setRandomSeed(os.time())
|
||||
end
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
end
|
||||
if love.load then love.load(arg) end
|
||||
if love.timer then love.timer.step() end
|
||||
local dt = 0
|
||||
while true do
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
if multi.boost then
|
||||
for i=1,multi.boost-1 do
|
||||
multi:uManager(dt)
|
||||
end
|
||||
end
|
||||
multi:uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
end
|
||||
multi.drawF={}
|
||||
function multi:dManager()
|
||||
for ii=1,#multi.drawF do
|
||||
multi.drawF[ii]()
|
||||
end
|
||||
end
|
||||
function multi:onDraw(func,i)
|
||||
i=i or 1
|
||||
table.insert(self.drawF,i,func)
|
||||
end
|
||||
function multi:lManager()
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
multi:uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=='\\' then
|
||||
return 'windows'
|
||||
else
|
||||
return 'unix'
|
||||
end
|
||||
end
|
||||
-- Step 1 get lanes
|
||||
lanes=require("lanes").configure()
|
||||
package.path="lua/?/init.lua;lua/?.lua;"..package.path
|
||||
require("multi.updater") -- get it all and have it on all lanes
|
||||
local multi=multi
|
||||
-- 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
|
||||
local GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
return __GlobalLinda:get(k)
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
__GlobalLinda:set(k,v)
|
||||
end,
|
||||
})
|
||||
-- Step 3 rewrite the thread methods to use lindas
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
__GlobalLinda:set(name,val)
|
||||
end
|
||||
function THREAD.get(name)
|
||||
__GlobalLinda:get(name)
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
local function wait()
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(.001,randomString(12))
|
||||
end
|
||||
repeat wait() until __GlobalLinda:get(name)
|
||||
return __GlobalLinda:get(name)
|
||||
end
|
||||
function THREAD.testFor(name,val,sym)
|
||||
--
|
||||
end
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function THREAD.kill() -- trigger the lane destruction
|
||||
-- coroutine.yield({"_kill_",":)"})
|
||||
end
|
||||
--[[ 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]]
|
||||
function THREAD.sleep(n)
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(n,randomString(12))
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
local function wait()
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(.001,randomString(12))
|
||||
end
|
||||
repeat wait() until n()
|
||||
end
|
||||
-- Step 5 Basic Threads!
|
||||
function multi:newSystemThread(name,func)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
c.thread=lanes.gen("*", func)()
|
||||
function c:kill()
|
||||
self.status:Destroy()
|
||||
self.thread:cancel()
|
||||
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
|
||||
print("Error in thread: '"..self.link.name.."' <"..err..">")
|
||||
self:Destroy()
|
||||
end
|
||||
end)
|
||||
return c
|
||||
end
|
||||
print("Intergrated Lanes!")
|
||||
multi.intergration={} -- for module creators
|
||||
multi.intergration.lanes={} -- for module creators
|
||||
multi.intergration.lanes.GLOBAL=GLOBAL -- for module creators
|
||||
multi.intergration.lanes.THREAD=THREAD -- for module creators
|
||||
return {init=function() return GLOBAL,THREAD end}
|
||||
@@ -0,0 +1,339 @@
|
||||
multi.intergration={}
|
||||
multi.intergration.love2d={}
|
||||
multi.intergration.love2d.ThreadBase=[[
|
||||
require("love.filesystem")
|
||||
require("love.system")
|
||||
require("love.timer")
|
||||
require("multi.all")
|
||||
GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
return __proxy__[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
__proxy__[k]=v
|
||||
if type(v)=="userdata" then
|
||||
__MainChan__:push(v)
|
||||
else
|
||||
__MainChan__:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
||||
end
|
||||
end,
|
||||
})
|
||||
function ToStr(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
if type(name) == "string" then
|
||||
tmp = tmp .. "[\""..name.."\"] = "
|
||||
else
|
||||
tmp = tmp .. "["..(name or "").."] = "
|
||||
end
|
||||
end
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
|
||||
end
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
elseif type(val) == "string" then
|
||||
tmp = tmp .. string.format("%q", val)
|
||||
elseif type(val) == "boolean" then
|
||||
tmp = tmp .. (val and "true" or "false")
|
||||
else
|
||||
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
||||
end
|
||||
return tmp
|
||||
end
|
||||
function resolveType(tp,d)
|
||||
if tp=="number" then
|
||||
return tonumber(d)
|
||||
elseif tp=="bool" then
|
||||
return (d=="true")
|
||||
elseif tp=="function" then
|
||||
return loadDump(d)
|
||||
elseif tp=="table" then
|
||||
return loadstring("return "..d)()
|
||||
else
|
||||
return d
|
||||
end
|
||||
end
|
||||
function resolveData(v)
|
||||
local data=""
|
||||
if type(v)=="table" then
|
||||
data=ToStr(v)
|
||||
elseif type(v)=="function" then
|
||||
data=dump(v)
|
||||
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" then
|
||||
data=tostring(v)
|
||||
end
|
||||
return data
|
||||
end
|
||||
sThread={}
|
||||
local function randomString(n)
|
||||
local c=os.clock()
|
||||
local a=0
|
||||
while os.clock()<c+.1 do
|
||||
a=a+1
|
||||
end
|
||||
math.randomseed(a)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
__proxy__={} -- this is where the actual globals will live
|
||||
__MainChan__=love.thread.getChannel("__MainChan__")
|
||||
__mythreadname__=randomString(16)
|
||||
__mythread__=love.thread.getChannel(__mythreadname__)
|
||||
__MainChan__:push("NEWTHREAD "..__mythreadname__.." | |")
|
||||
function loadDump(d)
|
||||
local s={}
|
||||
for p in d:gmatch("(%d-)\\") do
|
||||
s[#s+1]=string.char(tonumber(p))
|
||||
end
|
||||
return loadstring(table.concat(s))
|
||||
end
|
||||
function dump(func)
|
||||
local code,t={},string.dump(func)
|
||||
for i=1,#t do
|
||||
code[#code+1]=string.byte(t:sub(i,i)).."\\"
|
||||
end
|
||||
return table.concat(code)
|
||||
end
|
||||
function sThread.set(name,val)
|
||||
GLOBAL[name]=val
|
||||
end
|
||||
function sThread.get(name)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function sThread.waitFor(name)
|
||||
--print("Waiting:",__mythreadname__)
|
||||
local function wait()
|
||||
local data=__mythread__:pop()
|
||||
while data do
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
__proxy__[name]=resolveType(tp,d)
|
||||
end
|
||||
else
|
||||
__proxy__[name]=data
|
||||
end
|
||||
data=__mythread__:pop()
|
||||
end
|
||||
end
|
||||
repeat wait() until GLOBAL[name]
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function sThread.getCores()
|
||||
return love.system.getProcessorCount()
|
||||
end
|
||||
function sThread.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
function sThread.hold(n)
|
||||
local function wait()
|
||||
local data=__mythread__:pop()
|
||||
while data do
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
__proxy__[name]=resolveType(tp,d)
|
||||
end
|
||||
else
|
||||
__proxy__[name]=data
|
||||
end
|
||||
data=__mythread__:pop()
|
||||
end
|
||||
end
|
||||
repeat wait() until n()
|
||||
end
|
||||
updater=multi:newUpdater()
|
||||
updater:OnUpdate(function(self)
|
||||
local data=__mythread__:pop()
|
||||
while data do
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
__proxy__[name]=resolveType(tp,d)
|
||||
end
|
||||
else
|
||||
__proxy__[name]=data
|
||||
end
|
||||
data=__mythread__:pop()
|
||||
end
|
||||
end)
|
||||
func=loadDump([=[INSERT_USER_CODE]=])()
|
||||
multi:mainloop()
|
||||
]]
|
||||
GLOBAL={} -- Allow main thread to interact with these objects as well
|
||||
__proxy__={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
return __proxy__[k]
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
__proxy__[k]=v
|
||||
for i=1,#__channels__ do
|
||||
if type(v)=="userdata" then
|
||||
__channels__[i]:push(v)
|
||||
else
|
||||
__channels__[i]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
THREAD={} -- Allow main thread to interact with these objects as well
|
||||
multi.intergration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
|
||||
function ToStr(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
if type(name) == "string" then
|
||||
tmp = tmp .. "[\""..name.."\"] = "
|
||||
else
|
||||
tmp = tmp .. "["..(name or "").."] = "
|
||||
end
|
||||
end
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and " " or "")
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. ToStr(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and " " or "")
|
||||
end
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
elseif type(val) == "string" then
|
||||
tmp = tmp .. string.format("%q", val)
|
||||
elseif type(val) == "boolean" then
|
||||
tmp = tmp .. (val and "true" or "false")
|
||||
else
|
||||
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
||||
end
|
||||
return tmp
|
||||
end
|
||||
function resolveType(tp,d)
|
||||
if tp=="number" then
|
||||
return tonumber(d)
|
||||
elseif tp=="bool" then
|
||||
return (d=="true")
|
||||
elseif tp=="function" then
|
||||
return loadDump(d)
|
||||
elseif tp=="table" then
|
||||
return loadstring("return "..d)()
|
||||
else
|
||||
return d
|
||||
end
|
||||
end
|
||||
function resolveData(v)
|
||||
local data=""
|
||||
if type(v)=="table" then
|
||||
data=ToStr(v)
|
||||
elseif type(v)=="function" then
|
||||
data=dump(v)
|
||||
elseif type(v)=="string" or type(v)=="number" or type(v)=="bool" then
|
||||
data=tostring(v)
|
||||
end
|
||||
return data
|
||||
end
|
||||
function loadDump(d)
|
||||
local s={}
|
||||
for p in d:gmatch("(%d-)\\") do
|
||||
s[#s+1]=string.char(tonumber(p))
|
||||
end
|
||||
return loadstring(table.concat(s))
|
||||
end
|
||||
function dump(func)
|
||||
local code,t={},string.dump(func)
|
||||
for i=1,#t do
|
||||
code[#code+1]=string.byte(t:sub(i,i)).."\\"
|
||||
end
|
||||
return table.concat(code)
|
||||
end
|
||||
function multi:newSystemThread(name,func) -- the main method
|
||||
local c={}
|
||||
c.name=name
|
||||
c.thread=love.thread.newThread(multi.intergration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
|
||||
c.thread:start()
|
||||
return c
|
||||
end
|
||||
function love.threaderror( thread, errorstr )
|
||||
print("Error in "..tostring(thread)..": "..errorstr)
|
||||
end
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
GLOBAL[name]=val
|
||||
end
|
||||
function THREAD.get(name)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
multi.OBJ_REF:Pause()
|
||||
repeat multi:lManager() until GLOBAL[name]
|
||||
multi.OBJ_REF:Resume()
|
||||
return GLOBAL[name]
|
||||
end
|
||||
function THREAD.getCores()
|
||||
return love.system.getProcessorCount()
|
||||
end
|
||||
function THREAD.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
multi.OBJ_REF:Pause()
|
||||
repeat multi:lManager() until n()
|
||||
multi.OBJ_REF:Resume()
|
||||
end
|
||||
__channels__={}
|
||||
updater=multi:newUpdater()
|
||||
updater:OnUpdate(function(self)
|
||||
local data=multi.intergration.love2d.mainChannel:pop()
|
||||
while data do
|
||||
--print("MAIN:",data)
|
||||
if type(data)=="string" then
|
||||
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
|
||||
if cmd=="SYNC" then
|
||||
__proxy__[name]=resolveType(tp,d)
|
||||
for i=1,#__channels__ do
|
||||
-- send data to other threads
|
||||
if type(v)=="userdata" then
|
||||
__channels__[i]:push(v)
|
||||
else
|
||||
__channels__[i]:push("SYNC "..tp.." "..name.." "..d)
|
||||
end
|
||||
end
|
||||
elseif cmd=="NEWTHREAD" then
|
||||
__channels__[#__channels__+1]=love.thread.getChannel(tp)
|
||||
for k,v in pairs(__proxy__) do -- sync the global with each new thread
|
||||
if type(v)=="userdata" then
|
||||
__channels__[#__channels__]:push(v)
|
||||
else
|
||||
__channels__[#__channels__]:push("SYNC "..type(v).." "..k.." "..resolveData(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
__proxy__[name]=data
|
||||
end
|
||||
data=multi.intergration.love2d.mainChannel:pop()
|
||||
end
|
||||
end)
|
||||
return {
|
||||
init=function(t)
|
||||
if t then
|
||||
if t.threadNamespace then
|
||||
multi.intergration.love2d.ThreadBase:gsub("sThread",t.threadNamespace)
|
||||
end
|
||||
if t.globalNamespace then
|
||||
multi.intergration.love2d.ThreadBase:gsub("GLOBAL",t.globalNamespace)
|
||||
end
|
||||
end
|
||||
return GLOBAL,THREAD
|
||||
end
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi")
|
||||
thread={}
|
||||
multi.GlobalVariables={}
|
||||
if os.getOS()=="windows" then
|
||||
thread.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
thread.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function thread.sleep(n)
|
||||
coroutine.yield({"_sleep_",n})
|
||||
end
|
||||
function thread.hold(n)
|
||||
coroutine.yield({"_hold_",n})
|
||||
end
|
||||
function thread.skip(n)
|
||||
coroutine.yield({"_skip_",n})
|
||||
end
|
||||
function thread.kill()
|
||||
coroutine.yield({"_kill_",":)"})
|
||||
end
|
||||
function thread.yeild()
|
||||
coroutine.yield({"_sleep_",0})
|
||||
end
|
||||
function thread.getCores()
|
||||
return thread.__CORES
|
||||
end
|
||||
function thread.set(name,val)
|
||||
multi.GlobalVariables[name]=val
|
||||
return true
|
||||
end
|
||||
function thread.get(name)
|
||||
return multi.GlobalVariables[name]
|
||||
end
|
||||
function thread.waitFor(name)
|
||||
thread.hold(function() return thread.get(name)~=nil end)
|
||||
return thread.get(name)
|
||||
end
|
||||
function thread.testFor(name,val,sym)
|
||||
thread.hold(function() return thread.get(name)~=nil end)
|
||||
return thread.get(name)
|
||||
end
|
||||
function multi:newTBase(ins)
|
||||
local c = {}
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.ender={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Parent=self
|
||||
c.held=false
|
||||
return c
|
||||
end
|
||||
function multi:newThread(name,func)
|
||||
local c={}
|
||||
c.ref={}
|
||||
c.Name=name
|
||||
c.thread=coroutine.create(func)
|
||||
c.sleep=1
|
||||
c.firstRunDone=false
|
||||
c.timer=multi.scheduler:newTimer()
|
||||
c.ref.Globals=self:linkDomain("Globals")
|
||||
function c.ref:send(name,val)
|
||||
ret=coroutine.yield({Name=name,Value=val})
|
||||
self:syncGlobals(ret)
|
||||
end
|
||||
function c.ref:get(name)
|
||||
return self.Globals[name]
|
||||
end
|
||||
function c.ref:kill()
|
||||
err=coroutine.yield({"_kill_"})
|
||||
if err then
|
||||
error("Failed to kill a thread! Exiting...")
|
||||
end
|
||||
end
|
||||
function c.ref:sleep(n)
|
||||
if type(n)=="function" then
|
||||
ret=coroutine.yield({"_hold_",n})
|
||||
self:syncGlobals(ret)
|
||||
elseif type(n)=="number" then
|
||||
n = tonumber(n) or 0
|
||||
ret=coroutine.yield({"_sleep_",n})
|
||||
self:syncGlobals(ret)
|
||||
else
|
||||
error("Invalid Type for sleep!")
|
||||
end
|
||||
end
|
||||
function c.ref:syncGlobals(v)
|
||||
self.Globals=v
|
||||
end
|
||||
table.insert(self:linkDomain("Threads"),c)
|
||||
if not multi.scheduler:isActive() then
|
||||
multi.scheduler:Resume()
|
||||
end
|
||||
end
|
||||
multi:setDomainName("Threads")
|
||||
multi:setDomainName("Globals")
|
||||
multi.scheduler=multi:newUpdater()
|
||||
multi.scheduler.Type="scheduler"
|
||||
function multi.scheduler:setStep(n)
|
||||
self.skip=tonumber(n) or 24
|
||||
end
|
||||
multi.scheduler.skip=0
|
||||
multi.scheduler.counter=0
|
||||
multi.scheduler.Threads=multi:linkDomain("Threads")
|
||||
multi.scheduler.Globals=multi:linkDomain("Globals")
|
||||
multi.scheduler:OnUpdate(function(self)
|
||||
self.counter=self.counter+1
|
||||
for i=#self.Threads,1,-1 do
|
||||
ret={}
|
||||
if coroutine.status(self.Threads[i].thread)=="dead" then
|
||||
table.remove(self.Threads,i)
|
||||
else
|
||||
if self.Threads[i].timer:Get()>=self.Threads[i].sleep then
|
||||
if self.Threads[i].firstRunDone==false then
|
||||
self.Threads[i].firstRunDone=true
|
||||
self.Threads[i].timer:Start()
|
||||
_,ret=coroutine.resume(self.Threads[i].thread,self.Threads[i].ref)
|
||||
else
|
||||
_,ret=coroutine.resume(self.Threads[i].thread,self.Globals)
|
||||
end
|
||||
if ret==true or ret==false then
|
||||
print("Thread Ended!!!")
|
||||
ret={}
|
||||
end
|
||||
end
|
||||
if ret then
|
||||
if ret[1]=="_kill_" then
|
||||
table.remove(self.Threads,i)
|
||||
elseif ret[1]=="_sleep_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=ret[2]
|
||||
elseif ret[1]=="_skip_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=math.huge
|
||||
local event=multi:newEvent(function(evnt) return multi.scheduler.counter>=evnt.counter end)
|
||||
event.link=self.Threads[i]
|
||||
event.counter=self.counter+ret[2]
|
||||
event:OnEvent(function(evnt)
|
||||
evnt.link.sleep=0
|
||||
end)
|
||||
elseif ret[1]=="_hold_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=math.huge
|
||||
local event=multi:newEvent(ret[2])
|
||||
event.link=self.Threads[i]
|
||||
event:OnEvent(function(evnt)
|
||||
evnt.link.sleep=0
|
||||
end)
|
||||
elseif ret.Name then
|
||||
self.Globals[ret.Name]=ret.Value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi.scheduler:setStep()
|
||||
multi.scheduler:Pause()
|
||||
multi.OnError=multi:newConnection()
|
||||
@@ -0,0 +1,73 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedAlarm(name,set)
|
||||
local c=self:newTBase()
|
||||
c.Type='alarmThread'
|
||||
c.timer=self:newTimer()
|
||||
c.set=set or 0
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.set)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
self.timer:Resume()
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.rest=false
|
||||
self.timer:Reset(n)
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Pause()
|
||||
self.timer:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=multi.Priority_Low -- skips
|
||||
c.restRate=0 -- secs
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if c.timer:Get()>=c.set then
|
||||
c:Pause()
|
||||
for i=1,#c.func do
|
||||
c.func[i](c)
|
||||
end
|
||||
end
|
||||
thread.skip(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
require("multi.threading.step")
|
||||
require("multi.threading.tstep")
|
||||
require("multi.threading.updater")
|
||||
require("multi.threading.alarm")
|
||||
require("multi.threading.loop")
|
||||
require("multi.threading.event")
|
||||
require("multi.threading.process")
|
||||
@@ -0,0 +1,66 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedEvent(name,task)
|
||||
local c=self:newTBase()
|
||||
c.Type='eventThread'
|
||||
c.Task=task or function() end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.Task)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=1
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if c.Task(self) then
|
||||
for _E=1,#c.func do
|
||||
c.func[_E](c)
|
||||
end
|
||||
c:Pause()
|
||||
end
|
||||
ref:sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedLoop(name,func)
|
||||
local c=self:newTBase()
|
||||
c.Type='loopThread'
|
||||
c.Start=os.clock()
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](os.clock()-self.Start,c)
|
||||
end
|
||||
thread.sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,106 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedProcess(name)
|
||||
local c = {}
|
||||
setmetatable(c, multi)
|
||||
function c:newBase(ins)
|
||||
local ct = {}
|
||||
setmetatable(ct, self.Parent)
|
||||
ct.Active=true
|
||||
ct.func={}
|
||||
ct.ender={}
|
||||
ct.Id=0
|
||||
ct.PId=0
|
||||
ct.Act=function() end
|
||||
ct.Parent=self
|
||||
ct.held=false
|
||||
ct.ref=self.ref
|
||||
table.insert(self.Mainloop,ct)
|
||||
return ct
|
||||
end
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Type='process'
|
||||
c.Mainloop={}
|
||||
c.Tasks={}
|
||||
c.Tasks2={}
|
||||
c.Garbage={}
|
||||
c.Children={}
|
||||
c.Paused={}
|
||||
c.Active=true
|
||||
c.Id=-1
|
||||
c.Rest=0
|
||||
c.updaterate=.01
|
||||
c.restRate=.1
|
||||
c.Jobs={}
|
||||
c.queue={}
|
||||
c.jobUS=2
|
||||
c.rest=false
|
||||
function c:getController()
|
||||
return nil
|
||||
end
|
||||
function c:Start()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:Remove()
|
||||
self.ref:kill()
|
||||
end
|
||||
function c:kill()
|
||||
err=coroutine.yield({"_kill_"})
|
||||
if err then
|
||||
error("Failed to kill a thread! Exiting...")
|
||||
end
|
||||
end
|
||||
function c:sleep(n)
|
||||
if type(n)=="function" then
|
||||
ret=coroutine.yield({"_hold_",n})
|
||||
elseif type(n)=="number" then
|
||||
n = tonumber(n) or 0
|
||||
ret=coroutine.yield({"_sleep_",n})
|
||||
else
|
||||
error("Invalid Type for sleep!")
|
||||
end
|
||||
end
|
||||
c.hold=c.sleep
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:Sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
c:uManager()
|
||||
ref:sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,115 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedStep(name,start,reset,count,skip)
|
||||
local c=self:newTBase()
|
||||
local think=1
|
||||
c.Type='stepThread'
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.Reset=c.Resume
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.rest=true
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
c.updaterate=0
|
||||
c.restRate=.1
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if c~=nil then
|
||||
if c.spos==0 then
|
||||
if c.pos==c.start then
|
||||
for fe=1,#c.funcS do
|
||||
c.funcS[fe](c)
|
||||
end
|
||||
end
|
||||
for i=1,#c.func do
|
||||
c.func[i](c.pos,c)
|
||||
end
|
||||
c.pos=c.pos+c.count
|
||||
if c.pos-c.count==c.endAt then
|
||||
c:Pause()
|
||||
for fe=1,#c.funcE do
|
||||
c.funcE[fe](c)
|
||||
end
|
||||
c.pos=c.start
|
||||
end
|
||||
end
|
||||
end
|
||||
c.spos=c.spos+1
|
||||
if c.spos>=c.skip then
|
||||
c.spos=0
|
||||
end
|
||||
ref:sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedTLoop(name,func,n)
|
||||
local c=self:newTBase()
|
||||
c.Type='tloopThread'
|
||||
c.restN=n or 1
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](c)
|
||||
end
|
||||
thread.sleep(c.restN) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,114 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedTStep(name,start,reset,count,set)
|
||||
local c=self:newTBase()
|
||||
local think=1
|
||||
c.Type='tstepThread'
|
||||
c.Priority=self.Priority_Low
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
c.updaterate=0--multi.Priority_Low -- skips
|
||||
c.restRate=0
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if os.clock()-c.timer>=c.set then
|
||||
c:Reset()
|
||||
if c.pos==c.start then
|
||||
for fe=1,#c.funcS do
|
||||
c.funcS[fe](c)
|
||||
end
|
||||
end
|
||||
for i=1,#c.func do
|
||||
c.func[i](c.pos,c)
|
||||
end
|
||||
c.pos=c.pos+c.count
|
||||
if c.pos-c.count==c.endAt then
|
||||
c:Pause()
|
||||
for fe=1,#c.funcE do
|
||||
c.funcE[fe](c)
|
||||
end
|
||||
c.pos=c.start
|
||||
end
|
||||
end
|
||||
thread.skip(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,55 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 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
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("multi.threading")
|
||||
function multi:newThreadedUpdater(name,skip)
|
||||
local c=self:newTBase()
|
||||
c.Type='updaterThread'
|
||||
c.pos=1
|
||||
c.skip=skip or 1
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.OnUpdate=self.OnMainConnect
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](c)
|
||||
end
|
||||
c.pos=c.pos+1
|
||||
thread.skip(c.skip)
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
Reference in New Issue
Block a user