1.9.2 is out!

Added, updated, and fixed stuff
This commit is contained in:
2018-05-17 01:19:56 -04:00
parent 509086295c
commit e32720b687
11 changed files with 556 additions and 171 deletions
+290 -98
View File
@@ -22,6 +22,62 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
require("bin")
multi = {}
multi.Version = "1.9.2"
multi._VERSION = "1.9.2"
multi.stage = "mostly-stable"
multi.__index = multi
multi.Mainloop = {}
multi.Tasks = {}
multi.Tasks2 = {}
multi.Garbage = {}
multi.ender = {}
multi.Children = {}
multi.Paused = {}
multi.Active = true
multi.fps = 60
multi.Id = -1
multi.Type = "mainprocess"
multi.Rest = 0
multi._type = type
multi.Jobs = {}
multi.queue = {}
multi.jobUS = 2
multi.clock = os.clock
multi.time = os.time
multi.LinkedPath = multi
multi.isRunning = false
--Do not change these ever...Any other number will not work (Unless you are using enablePriority2())
multi.Priority_Core = 1
multi.Priority_High = 4
multi.Priority_Above_Normal = 16
multi.Priority_Normal = 64
multi.Priority_Below_Normal = 256
multi.Priority_Low = 1024
multi.Priority_Idle = 4096
multi.PStep = 1
multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
--^^^^
multi.PriorityTick=1 -- Between 1, 2 and 4
multi.Priority=multi.Priority_Core
multi.threshold=256
multi.threstimed=.001
function multi.queuefinal(self)
self:Destroy()
if self.Parent.Mainloop[#self.Parent.Mainloop] then
if self.Parent.Mainloop[#self.Parent.Mainloop].Type=="alarm" then
self.Parent.Mainloop[#self.Parent.Mainloop]:Reset()
self.Parent.Mainloop[#self.Parent.Mainloop].Active=true
else
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
end
else
for i=1,#self.Parent.funcE do
self.Parent.funcE[i](self)
end
self.Parent:Remove()
end
end
if table.unpack then
unpack=table.unpack
end
@@ -45,83 +101,19 @@ function print(...)
_print(...)
end
end
multi = {}
multi.Version="1.9.1"
multi._VERSION="1.9.1"
multi.stage='mostly-stable'
multi.__index = multi
multi.Mainloop={}
multi.Tasks={}
multi.Tasks2={}
multi.Garbage={}
multi.ender={}
multi.Children={}
multi.Paused={}
multi.Active=true
multi.fps=60
multi.Id=-1
multi.Type='mainprocess'
multi.Rest=0
multi._type=type
multi.Jobs={}
multi.queue={}
multi.jobUS=2
multi.clock=os.clock
multi.time=os.time
multi.LinkedPath=multi
multi.isRunning=false
multi.queuefinal=function(self)
self:Destroy()
if self.Parent.Mainloop[#self.Parent.Mainloop] then
if self.Parent.Mainloop[#self.Parent.Mainloop].Type=="alarm" then
self.Parent.Mainloop[#self.Parent.Mainloop]:Reset()
self.Parent.Mainloop[#self.Parent.Mainloop].Active=true
else
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
end
else
for i=1,#self.Parent.funcE do
self.Parent.funcE[i](self)
end
self.Parent:Remove()
_write=io.write
function io.write(...)
if not __SUPPRESSWRITES then
_write(...)
end
end
--Do not change these ever...Any other number will not work (Unless you are using enablePriority2())
multi.Priority_Core=1
multi.Priority_High=4
multi.Priority_Above_Normal=16
multi.Priority_Normal=64
multi.Priority_Below_Normal=256
multi.Priority_Low=1024
multi.Priority_Idle=4096
multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
multi.PStep=1
--^^^^
multi.PriorityTick=1 -- Between 1,2 and 4
multi.Priority=multi.Priority_Core
multi.threshold=256
multi.threstimed=.001
function multi:setThreshold(n)
self.threshold=n or 120
end
function multi:setThrestimed(n)
self.threstimed=n or .001
self.deltaTarget=n or .1
end
function multi:getLoad()
return multi:newFunction(function(self)
multi.scheduler:Pause()
local sample=#multi.Mainloop
local FFloadtest=0
multi:benchMark(multi.threstimed):OnBench(function(_,l3) FFloadtest=l3*(1/multi.threstimed) end)
self:hold(function() return FFloadtest~=0 end)
local val=FFloadtest/sample
multi.scheduler:Resume()
if val>multi.threshold then
return 0
else
return 100-((val/multi.threshold)*100)
end
end)()
if multi.load_updater:isPaused() then multi.load_updater:Resume() return 0 end
local val = math.abs(self.dStepA-self.dStepB)/multi.deltaTarget*100
if val > 100 then return 100 else return val end
end
function multi:setDomainName(name)
self[name]={}
@@ -195,6 +187,7 @@ multi.Condition=multi.condition
function multi:isHeld()
return self.held
end
multi.important={}
multi.IsHeld=multi.isHeld
function multi.executeFunction(name,...)
if type(_G[name])=='function' then
@@ -214,14 +207,39 @@ end
multi.WaitFor=multi.waitFor
function multi:reboot(r)
local before=collectgarbage('count')
self.Mainloop={}
self.Tasks={}
self.Tasks2={}
self.Garbage={}
self.Children={}
self.Paused={}
self.Active=true
self.Id=-1
multi.Mainloop={}
multi.Tasks={}
multi.Tasks2={}
multi.Garbage={}
multi.ender={}
multi.Children={}
multi.Paused={}
multi.Active=true
multi.fps=60
multi.Id=-1
multi.Type='mainprocess'
multi.Rest=0
multi._type=type
multi.Jobs={}
multi.queue={}
multi.jobUS=2
multi.clock=os.clock
multi.time=os.time
multi.LinkedPath=multi
multi.isRunning=false
multi.Priority_Core=1
multi.Priority_High=4
multi.Priority_Above_Normal=16
multi.Priority_Normal=64
multi.Priority_Below_Normal=256
multi.Priority_Low=1024
multi.Priority_Idle=4096
multi.PList={multi.Priority_Core,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Idle}
multi.PStep=1
multi.PriorityTick=1
multi.Priority=multi.Priority_Core
multi.threshold=256
multi.threstimed=.001
if r then
for i,v in pairs(_G) do
if type(i)=='table' then
@@ -280,20 +298,18 @@ function multi:enablePriority()
_G.ID=0
local PS=self
for _D=#Loop,1,-1 do
if Loop[_D] then
if (PS.PList[PS.PStep])%Loop[_D].Priority==0 then
if Loop[_D].Active then
Loop[_D].Id=_D
self.CID=_D
Loop[_D]:Act()
for P=1,7 do
if Loop[_D] then
if (PS.PList[P])%Loop[_D].Priority==0 then
if Loop[_D].Active then
Loop[_D].Id=_D
self.CID=_D
Loop[_D]:Act()
end
end
end
end
end
PS.PStep=PS.PStep+1
if PS.PStep>7 then
PS.PStep=1
end
end
end
function multi:enablePriority2()
@@ -701,6 +717,7 @@ function multi:newProcess(file)
loadstring('local process=multi.Cself '..io.open(file,'rb'):read('*all'))()
end
self:create(c)
--~ c:IngoreObject()
return c
end
function multi:newQueuer(file)
@@ -772,7 +789,8 @@ function multi:newTimer()
end
function multi:newConnection(protect)
local c={}
setmetatable(c,{__call=function(self,...) self:connect(...) end})
c.Parent=self
setmetatable(c,{__call=function(self,...) return self:connect(...) end})
c.Type='connector'
c.func={}
c.ID=0
@@ -780,6 +798,16 @@ function multi:newConnection(protect)
c.connections={}
c.fconnections={}
c.FC=0
function c:holdUT()
self.waiting=true
local id=self:connect(function()
self.waiting=false
end)
repeat
self.Parent:uManager()
until self.waiting==false
id:Destroy()
end
function c:fConnect(func)
local temp=self:connect(func)
table.insert(self.fconnections,temp)
@@ -852,8 +880,9 @@ function multi:newConnection(protect)
end
end
end
end
end,
}
temp.Destroy=temp.Remove
if name then
self.connections[name]=temp
end
@@ -1123,7 +1152,7 @@ function multi:newUpdater(skip)
end
self.pos=self.pos+1
end
function c:setSkip(n)
function c:SetSkip(n)
self.skip=n
end
c.OnUpdate=self.OnMainConnect
@@ -1376,6 +1405,112 @@ function multi:newTStep(start,reset,count,set)
self:create(c)
return c
end
function multi:newTimeStamper()
local c=self:newBase()
c.Type='timestamper'
c.Priority=self.Priority_Idle
c.hour = {}
c.minute = {}
c.second = {}
c.time = {}
c.day = {}
c.month = {}
c.year = {}
function c:Act()
for i=1,#self.hour do
if self.hour[i][1]==os.date("%H") and self.hour[i][3] then
self.hour[i][2](self)
self.hour[i][3]=false
elseif self.hour[i][1]~=os.date("%H") and not self.hour[i][3] then
self.hour[i][3]=true
end
end
for i=1,#self.minute do
if self.minute[i][1]==os.date("%M") and self.minute[i][3] then
self.minute[i][2](self)
self.minute[i][3]=false
elseif self.minute[i][1]~=os.date("%M") and not self.minute[i][3] then
self.minute[i][3]=true
end
end
for i=1,#self.second do
if self.second[i][1]==os.date("%S") and self.second[i][3] then
self.second[i][2](self)
self.second[i][3]=false
elseif self.second[i][1]~=os.date("%S") and not self.second[i][3] then
self.second[i][3]=true
end
end
for i=1,#self.day do
if type(self.day[i][1])=="string" then
if self.day[i][1]==os.date("%a") and self.day[i][3] then
self.day[i][2](self)
self.day[i][3]=false
elseif self.day[i][1]~=os.date("%a") and not self.day[i][3] then
self.day[i][3]=true
end
else
if string.format("%02d",self.day[i][1])==os.date("%d") and self.day[i][3] then
self.day[i][2](self)
self.day[i][3]=false
elseif string.format("%02d",self.day[i][1])~=os.date("%d") and not self.day[i][3] then
self.day[i][3]=true
end
end
end
for i=1,#self.month do
if self.month[i][1]==os.date("%m") and self.month[i][3] then
self.month[i][2](self)
self.month[i][3]=false
elseif self.month[i][1]~=os.date("%m") and not self.month[i][3] then
self.month[i][3]=true
end
end
for i=1,#self.time do
if self.time[i][1]==os.date("%X") and self.time[i][3] then
self.time[i][2](self)
self.time[i][3]=false
elseif self.time[i][1]~=os.date("%X") and not self.time[i][3] then
self.time[i][3]=true
end
end
for i=1,#self.year do
if self.year[i][1]==os.date("%y") and self.year[i][3] then
self.year[i][2](self)
self.year[i][3]=false
elseif self.year[i][1]~=os.date("%y") and not self.year[i][3] then
self.year[i][3]=true
end
end
end
function c:OnTime(hour,minute,second,func)
if type(hour)=="number" then
self.time[#self.time+1]={string.format("%02d:%02d:%02d",hour,minute,second),func,true}
else
self.time[#self.time+1]={hour,minute,true}
end
end
function c:OnHour(hour,func)
self.hour[#self.hour+1]={string.format("%02d",hour),func,true}
end
function c:OnMinute(minute,func)
self.minute[#self.minute+1]={string.format("%02d",minute),func,true}
end
function c:OnSecond(second,func)
self.second[#self.second+1]={string.format("%02d",second),func,true}
end
function c:OnDay(day,func)
self.day[#self.day+1]={day,func,true}
end
function c:OnMonth(month,func)
self.month[#self.month+1]={string.format("%02d",month),func,true}
end
function c:OnYear(year,func)
self.year[#self.year+1]={string.format("%02d",year),func,true}
end
self:create(c)
return c
end
function multi:newWatcher(namespace,name)
local function WatcherObj(ns,n)
if self.Type=='queue' then
@@ -1462,6 +1597,7 @@ function multi:newTBase(name)
c.important={}
c.held=false
c.ToString=multi.ToString
c.ToFile=multi.ToFile
return c
end
function multi:newThread(name,func)
@@ -1992,9 +2128,15 @@ function multi:newThreadedEvent(name,task)
return c
end
-- State Saving Stuff
function multi:IngoreObject()
self.Ingore=true
end
multi.scheduler:IngoreObject()
function multi:ToString()
if self.Ingore then return end
local t=self.Type
local data;
print(t)
if t:sub(-6)=="Thread" then
data={
Type=t,
@@ -2025,9 +2167,7 @@ function multi:ToString()
held=self.held,
}
end
if t=="process" then
--
elseif t=="eventThread" or t=="event" then
if t=="eventThread" or t=="event" then
table.merge(data,{
Task=self.Task,
})
@@ -2095,6 +2235,23 @@ function multi:ToString()
set=self.set,
link=self.link,
})
elseif t=="process" or t=="mainprocess" then
local loop=self.Mainloop
local dat={}
for i=1,#loop do
local ins=loop[i]:ToString()
if ins~=nil then
table.insert(dat,ins)
end
end
local str=bin.new()
str:addBlock({Type=t})
str:addBlock(#dat,4,"n")
for i=1,#dat do
str:addBlock(#dat[i],4,"n")
str:addBlock(dat[i])
end
return str.data
end
for i,v in pairs(self.important) do
data[v]=self[v]
@@ -2109,9 +2266,23 @@ function multi:newFromString(str)
str=str.data
end
end
local data=bin.new(str):getBlock("t")
local handle=bin.new(str)
local data=handle:getBlock("t")
local t=data.Type
if t=="step" then -- GOOD
if t=="mainprocess" then
local objs=handle:getBlock("n",4)
for i=1,objs do
self:newFromString(handle:getBlock("s",(handle:getBlock("n",4))))
end
return self
elseif t=="process" then
local temp=multi:newProcess()
local objs=handle:getBlock("n",4)
for i=1,objs do
temp:newFromString(handle:getBlock("s",(handle:getBlock("n",4))))
end
return temp
elseif t=="step" then -- GOOD
local item=self:newStep()
table.merge(item,data)
return item
@@ -2176,6 +2347,12 @@ end
function multi:Important(varname)
table.insert(important,varname)
end
function multi:ToFile(path)
bin.new(self:ToString()):tofile(path)
end
function multi:fromFile(path)
self:newFromString(bin.load(path))
end
function multi:SetStateFlag(opt)
--
end
@@ -2191,3 +2368,18 @@ end
function multi:setDefualtStateFlag(opt)
--
end
multi.dStepA = 0
multi.dStepB = 0
multi.dSwap = 0
multi.deltaTarget = .05
multi.load_updater = multi:newUpdater(2)
multi.load_updater:Pause()
multi.load_updater:OnUpdate(function(self)
if self.Parent.dSwap == 0 then
self.Parent.dStepA = os.clock()
self.Parent.dSwap = 1
else
self.Parent.dSwap = 0
self.Parent.dStepB = os.clock()
end
end)
+10 -2
View File
@@ -89,7 +89,10 @@ else
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
end
function THREAD.kill() -- trigger the lane destruction
-- coroutine.yield({"_kill_",":)"})
error("Thread was killed!")
end
function THREAD.getName()
return THREAD_NAME
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
@@ -111,7 +114,12 @@ function multi:newSystemThread(name,func,...)
local __self=c
c.name=name
c.Type="sthread"
c.thread=lanes.gen("*", func)(...)
local THREAD_NAME=name
local function func2(...)
_G["THREAD_NAME"]=THREAD_NAME
func()
end
c.thread=lanes.gen("*", func2)(...)
function c:kill()
--self.status:Destroy()
self.thread:cancel()
+13 -11
View File
@@ -9,11 +9,12 @@ multi.integration={}
multi.integration.love2d={}
multi.integration.love2d.ThreadBase=[[
tab={...}
__THREADID__=table.remove(1,tab)
__THREADNAME__=table.remove(1,tab)
__THREADID__=table.remove(tab,1)
__THREADNAME__=table.remove(tab,1)
require("love.filesystem")
require("love.system")
require("love.timer")
require("love.image")
require("multi")
GLOBAL={}
setmetatable(GLOBAL,{
@@ -34,7 +35,7 @@ setmetatable(GLOBAL,{
function __sync__()
local data=__mythread__:pop()
while data do
love.timer.sleep(.001)
love.timer.sleep(.01)
if type(data)=="string" then
local cmd,tp,name,d=data:match("(%S-) (%S-) (%S-) (.+)")
if name=="__DIEPLZ"..__THREADID__.."__" then
@@ -139,6 +140,12 @@ function dump(func)
end
return table.concat(code)
end
function sThread.getName()
return __THREADNAME__
end
function sThread.kill()
error("Thread was killed!")
end
function sThread.set(name,val)
GLOBAL[name]=val
end
@@ -158,14 +165,6 @@ end
function sThread.hold(n)
repeat __sync__() until n()
end
multi:newLoop(function(self)
self:Pause()
local ld=multi:getLoad()
self:Resume()
if ld<80 then
love.timer.sleep(.01)
end
end)
updater=multi:newUpdater()
updater:OnUpdate(__sync__)
func=loadDump([=[INSERT_USER_CODE]=])(unpack(tab))
@@ -190,6 +189,9 @@ setmetatable(GLOBAL,{
})
THREAD={} -- Allow main thread to interact with these objects as well
multi.integration.love2d.mainChannel=love.thread.getChannel("__MainChan__")
function THREAD.getName()
return __THREADNAME__
end
function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
+89 -22
View File
@@ -37,19 +37,62 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
function c:init() -- create an init function so we can mimic on bith love2d and lanes
self.chan=love.thread.getChannel(self.name) -- create channel by the name self.name
function self:push(v) -- push to the channel
self.chan:push({type(v),resolveData(v)})
local tab
if type(v)=="table" then
tab = {}
for i,c in pairs(v) do
if type(c)=="function" then
tab[i]="\1"..string.dump(c)
else
tab[i]=c
end
end
self.chan:push(tab)
else
self.chan:push(c)
end
end
function self:pop() -- pop from the channel
local tab=self.chan:pop()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
local v=self.chan:pop()
if not v then return end
if type(v)=="table" then
tab = {}
for i,c in pairs(v) do
if type(c)=="string" then
if c:sub(1,1)=="\1" then
tab[i]=loadstring(c:sub(2,-1))
else
tab[i]=c
end
else
tab[i]=c
end
end
return tab
else
return self.chan:pop()
end
end
function self:peek()
local tab=self.chan:peek()
--print(tab)
if not tab then return end
return resolveType(tab[1],tab[2])
local v=self.chan:peek()
if not v then return end
if type(v)=="table" then
tab = {}
for i,c in pairs(v) do
if type(c)=="string" then
if c:sub(1,1)=="\1" then
tab[i]=loadstring(c:sub(2,-1))
else
tab[i]=c
end
else
tab[i]=c
end
end
return tab
else
return self.chan:pop()
end
end
GLOBAL[self.name]=self -- send the object to the thread through the global interface
return self -- return the object
@@ -120,7 +163,7 @@ function multi:systemThreadedBenchmark(n,p)
end)
return c
end
function multi:newSystemThreadedTable(name,n)
function multi:newSystemThreadedTable(name,n) -- NEDS FIXING SING SO MUCH WORK!!!
local c={} -- where we will store our object
c.name=name -- set the name this is important for the love2d side
c.cores=n
@@ -212,8 +255,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
c.REG=multi:newSystemThreadedQueue("THREADED_JQ_F_REG"):init()
-- registerJob(name,func)
-- pushJob(...)
c.OnReady=multi:newConnection()
function c:registerJob(name,func)
for i=1,self.cores do
self.REG:push({name,func})
@@ -222,6 +264,7 @@ function multi:newSystemThreadedJobQueue(numOfCores)
function c:pushJob(name,...)
self.queueOUT:push({self.jobnum,name,...})
self.jobnum=self.jobnum+1
return self.jobnum-1
end
local GLOBAL=multi.integration.GLOBAL -- set up locals incase we are using lanes
local sThread=multi.integration.THREAD -- set up locals incase we are using lanes
@@ -232,15 +275,24 @@ function multi:newSystemThreadedJobQueue(numOfCores)
end
end
function c:start()
self:doToAll(function()
_G["__started__"]=true
SFunc()
multi:newEvent(function()
return self.ThreadsLoaded==true
end):OnEvent(function(evnt)
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
self:doToAll(function()
_G["__started__"]=true
SFunc()
end)
evnt:Destroy()
end)
end
GLOBAL["__JQ_COUNT__"]=c.cores
for i=1,c.cores do
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function()
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function(name,ind)
require("multi")
ThreadName=name
__sleep__=.001
if love then -- lets make sure we don't reference upvalues if using love2d
GLOBAL=_G.GLOBAL
@@ -251,10 +303,6 @@ function multi:newSystemThreadedJobQueue(numOfCores)
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
REG=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
sThread.sleep(.1) -- lets wait for things to work out
GLOBAL["THREADED_JQ"]=nil -- remove it
GLOBAL["THREADED_JQO"]=nil -- remove it
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
QALLT={}
FUNCS={}
SFunc=multi:newFunction(function(self)
@@ -287,10 +335,12 @@ function multi:newSystemThreadedJobQueue(numOfCores)
return FUNCS[k]
end
})
lastjob=os.clock()
MainLoop=multi:newLoop(function(self)
if __started__ then
local job=JQI:pop()
if job then
lastjob=os.clock()
local d=QALL:peek()
if d then
if not QALLT[d[1]] then
@@ -311,17 +361,34 @@ function multi:newSystemThreadedJobQueue(numOfCores)
end
end
end)
multi:newThread("Idler",function()
while true do
if os.clock()-lastjob>1 then
sThread.sleep(.1)
end
thread.sleep(.001)
end
end)
JQO:push({"_THREADINIT_",ind})
if not love then
multi:mainloop()
end
end)
end,"Thread<"..i..">",i)
end
c.OnJobCompleted=multi:newConnection()
c.updater=multi:newLoop(function(self)
local data=self.link.queueIN:pop()
while data do
if data then
self.link.OnJobCompleted:Fire(unpack(data))
local a,b=unpack(data)
if a=="_THREADINIT_" then
if b==self.link.cores then
self.link.ThreadsLoaded=true
self.link.OnReady:Fire()
end
else
self.link.OnJobCompleted:Fire(unpack(data))
end
end
data=self.link.queueIN:pop()
end