Add utils.lua that contains the helper methods. Keeps the main file cleaner

This commit is contained in:
Ryan Ward 2020-02-21 13:22:41 -05:00
parent 5b401ef455
commit 16b0354c42
5 changed files with 452 additions and 568 deletions

View File

@ -4,7 +4,7 @@ Table of contents
--- ---
[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)</br>[Update 14.0.0 - Consistency, Additions and Stability](#update-1400-consistency-additions-and-stability)</br>[Update 13.1.0 - Bug fixes and features added](#update-1310-bug-fixes-and-features-added)</br>[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300-added-some-documentation-and-some-new-features-too-check-it-out)</br>[Update 12.2.2 - Time for some more bug fixes!](#update-1222-time-for-some-more-bug-fixes)</br>[Update 12.2.1 - Time for some bug fixes!](#update-1221-time-for-some-bug-fixes)</br>[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)</br>[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)</br>[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200-big-update-lots-of-additions-some-changes)</br>[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)</br>[Update: 1.11.0](#update-1110)</br>[Update: 1.10.0](#update-1100)</br>[Update: 1.9.2](#update-192)</br>[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)</br>[Update: 1.9.0](#update-190)</br>[Update: 1.8.7](#update-187)</br>[Update: 1.8.6](#update-186)</br>[Update: 1.8.5](#update-185)</br>[Update: 1.8.4](#update-184)</br>[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)</br>[Update: 1.8.2](#update-182)</br>[Update: 1.8.1](#update-181)</br>[Update: 1.7.6](#update-176)</br>[Update: 1.7.5](#update-175)</br>[Update: 1.7.4](#update-174)</br>[Update: 1.7.3](#update-173)</br>[Update: 1.7.2](#update-172)</br>[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)</br>[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)</br>[Update: 1.6.0](#update-160)</br>[Update: 1.5.0](#update-150)</br>[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)</br>[Update: 1.4.0 (3/20/2017)](#update-140-3202017)</br>[Update: 1.3.0 (1/29/2017)](#update-130-1292017)</br>[Update: 1.2.0 (12.31.2016)](#update-120-12312016)</br>[Update: 1.1.0](#update-110)</br>[Update: 1.0.0](#update-100)</br>[Update: 0.6.3](#update-063)</br>[Update: 0.6.2](#update-062)</br>[Update: 0.6.1-6](#update-061-6)</br>[Update: 0.5.1-6](#update-051-6)</br>[Update: 0.4.1](#update-041)</br>[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)</br>[Update: EventManager 2.0.0](#update-eventmanager-200)</br>[Update: EventManager 1.2.0](#update-eventmanager-120)</br>[Update: EventManager 1.1.0](#update-eventmanager-110)</br>[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</br>[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different) [Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)</br>[Update 14.0.0 - Consistency, Additions and Stability](#update-1400-consistency-additions-and-stability)</br>[Update 13.1.0 - Bug fixes and features added](#update-1310-bug-fixes-and-features-added)</br>[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300-added-some-documentation-and-some-new-features-too-check-it-out)</br>[Update 12.2.2 - Time for some more bug fixes!](#update-1222-time-for-some-more-bug-fixes)</br>[Update 12.2.1 - Time for some bug fixes!](#update-1221-time-for-some-bug-fixes)</br>[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)</br>[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)</br>[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200-big-update-lots-of-additions-some-changes)</br>[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)</br>[Update: 1.11.0](#update-1110)</br>[Update: 1.10.0](#update-1100)</br>[Update: 1.9.2](#update-192)</br>[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)</br>[Update: 1.9.0](#update-190)</br>[Update: 1.8.7](#update-187)</br>[Update: 1.8.6](#update-186)</br>[Update: 1.8.5](#update-185)</br>[Update: 1.8.4](#update-184)</br>[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)</br>[Update: 1.8.2](#update-182)</br>[Update: 1.8.1](#update-181)</br>[Update: 1.7.6](#update-176)</br>[Update: 1.7.5](#update-175)</br>[Update: 1.7.4](#update-174)</br>[Update: 1.7.3](#update-173)</br>[Update: 1.7.2](#update-172)</br>[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)</br>[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)</br>[Update: 1.6.0](#update-160)</br>[Update: 1.5.0](#update-150)</br>[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)</br>[Update: 1.4.0 (3/20/2017)](#update-140-3202017)</br>[Update: 1.3.0 (1/29/2017)](#update-130-1292017)</br>[Update: 1.2.0 (12.31.2016)](#update-120-12312016)</br>[Update: 1.1.0](#update-110)</br>[Update: 1.0.0](#update-100)</br>[Update: 0.6.3](#update-063)</br>[Update: 0.6.2](#update-062)</br>[Update: 0.6.1-6](#update-061-6)</br>[Update: 0.5.1-6](#update-051-6)</br>[Update: 0.4.1](#update-041)</br>[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)</br>[Update: EventManager 2.0.0](#update-eventmanager-200)</br>[Update: EventManager 1.2.0](#update-eventmanager-120)</br>[Update: EventManager 1.1.0](#update-eventmanager-110)</br>[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</br>[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different)
# Update 14.2.0 - State Saving reworked! # Update 14.2.0 - The great pruning
Full Update Showcase Full Update Showcase
--- ---
```lua ```lua
@ -45,9 +45,36 @@ Changed:
--- ---
- Revamped the job system - Revamped the job system
- multi.Jobs:newJob() - multi.Jobs:newJob()
Removed: Removed:
--- ---
- multi:newTrigger() — Connections do everything this thing could do and more. - multi:newTrigger() — Connections do everything this thing could do and more.
- multi:newHyperThreadedProcess(name)*
- multi:newThreadedProcess(name)*
- multi.nextStep(func)* — The new job System can be used instead to achieve this
- multi.queuefinal(self) — An Old method for a feature long gone from the library
- multi:setLoad(n)*
- multi:setThrestimed(n)*
- multi:setDomainName(name)*
- multi:linkDomain(name)*
- multi:_Pause()* — Use multi:Stop() instead!
- multi:isHeld()/multi:IsHeld()* Holding is handled differently so a held variable is no longer needed for chacking.
- multi.executeFunction(name,...)*
- multi:getError()* — Errors are nolonger gotten like that, multi.OnError(func) is the way to go
- multi.startFPSMonitior()*
- multi.doFPS(s)*
-
-
-
-
-
-
-
-
-
-
*Many features have become outdated/redundant with new features and additions that have been added to the library
# Update 14.1.0 - A whole new world of possibilities # Update 14.1.0 - A whole new world of possibilities
Full Update Showcase Full Update Showcase
--- ---

View File

@ -22,13 +22,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]] ]]
local multi = {} local multi = {}
local mainloopActive = false
local isRunning = false
local clock = os.clock local clock = os.clock
local thread = {} local thread = {}
if not _G["$multi"] then if not _G["$multi"] then
_G["$multi"] = {multi=multi,thread=thread} _G["$multi"] = {multi=multi,thread=thread}
end end
multi.Version = "14.2.0" multi.Version = "14.2.0"
multi._VERSION = "14.2.0"
multi.stage = "stable" multi.stage = "stable"
multi.__index = multi multi.__index = multi
multi.Name = "multi.root" multi.Name = "multi.root"
@ -37,7 +38,6 @@ multi.Garbage = {}
multi.ender = {} multi.ender = {}
multi.Children = {} multi.Children = {}
multi.Active = true multi.Active = true
multi.fps = 60
multi.Type = "mainprocess" multi.Type = "mainprocess"
multi.Rest = 0 multi.Rest = 0
multi._type = type multi._type = type
@ -46,28 +46,6 @@ multi.clock = os.clock
multi.time = os.time multi.time = os.time
multi.LinkedPath = multi multi.LinkedPath = multi
multi.lastTime = clock() multi.lastTime = clock()
multi.DestroyedObj = {
Type = "destroyed",
}
local function uni()
return multi.DestroyedObj
end
setmetatable(multi.DestroyedObj, {
__index = function(t,k)
return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni})
end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni
})
math.randomseed(os.time())
local mainloopActive = false
local isRunning = false
local next
local ncount = 0
multi.defaultSettings = {
priority = 0,
protect = false,
}
multi.Priority_Core = 1 multi.Priority_Core = 1
multi.Priority_Very_High = 4 multi.Priority_Very_High = 4
multi.Priority_High = 16 multi.Priority_High = 16
@ -94,266 +72,18 @@ multi.PriorityTick=1
multi.Priority=multi.Priority_High multi.Priority=multi.Priority_High
multi.threshold=256 multi.threshold=256
multi.threstimed=.001 multi.threstimed=.001
function multi.init() function multi.init()
multi.NIL = {Type="NIL"} multi.NIL = {Type="NIL"}
return _G["$multi"].multi,_G["$multi"].thread return _G["$multi"].multi,_G["$multi"].thread
end end
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 and not unpack then
unpack=table.unpack
end
function table.merge(t1, t2)
for k,v in pairs(t2) do
if type(v) == 'table' then
if type(t1[k] or false) == 'table' then
table.merge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
function multi:setThrestimed(n)
self.deltaTarget=n or .1
end
function multi:enableLoadDetection()
if multi.maxSpd then return end
-- here we are going to run a quick benchMark solo
local temp = multi:newProcessor()
temp:Start()
local t = os.clock()
local stop = false
temp:benchMark(.01):OnBench(function(time,steps)
stop = steps
end)
while not stop do
temp:uManager()
end
temp:Destroy()
multi.maxSpd = stop
end
local MaxLoad = nil
function multi:setLoad(n)
MaxLoad = n
end
local busy = false
local lastVal = 0
local bb = 0
function multi:getLoad()
if not multi.maxSpd then multi:enableLoadDetection() end
if busy then return lastVal end
local val = nil
if thread.isThread() then
local bench
multi:benchMark(.01):OnBench(function(time,steps)
bench = steps
bb = steps
end)
thread.hold(function()
return bench
end)
bench = bench^1.5
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
else
busy = true
local bench
multi:benchMark(.01):OnBench(function(time,steps)
bench = steps
bb = steps
end)
while not bench do
multi:uManager()
end
bench = bench^1.5
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
busy = false
end
if val<0 then val = 0 end
if val > 100 then val = 100 end
lastVal = val
return val,bb*100
end
function multi:setDomainName(name)
self[name]={}
end
function multi:linkDomain(name)
return self[name]
end
function multi:_Pause()
self.Active=false
end
function multi:setPriority(s)
if type(s)==number then
self.Priority=s
elseif type(s)=='string' then
if s:lower()=='core' or s:lower()=='c' then
self.Priority=self.Priority_Core
elseif s:lower()=="very high" or s:lower()=="vh" then
self.Priority=self.Priority_Very_High
elseif s:lower()=='high' or s:lower()=='h' then
self.Priority=self.Priority_High
elseif s:lower()=='above' or s:lower()=='a' then
self.Priority=self.Priority_Above_Normal
elseif s:lower()=='normal' or s:lower()=='n' then
self.Priority=self.Priority_Normal
elseif s:lower()=='below' or s:lower()=='b' then
self.Priority=self.Priority_Below_Normal
elseif s:lower()=='low' or s:lower()=='l' then
self.Priority=self.Priority_Low
elseif s:lower()=="very low" or s:lower()=="vl" then
self.Priority=self.Priority_Very_Low
elseif s:lower()=='idle' or s:lower()=='i' then
self.Priority=self.Priority_Idle
end
self.solid = true
end
if not self.PrioritySet then
self.defPriority = self.Priority
self.PrioritySet = true
end
end
function multi:ResetPriority()
self.Priority = self.defPriority
end
-- System -- System
function os.getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
else
return 'unix'
end
end
if os.getOS()=='windows' then
function os.sleep(n)
if n > 0 then os.execute('ping -n ' .. tonumber(n+1) .. ' localhost > NUL') end
end
else
function os.sleep(n)
os.execute('sleep ' .. tonumber(n))
end
end
function multi.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 multi:getParentProcess()
return self.Mainloop[self.CID]
end
multi.GetParentProcess=multi.getParentProcess
function multi.Stop() function multi.Stop()
mainloopActive=false mainloopActive=false
end end
function multi:isHeld()
return self.held
end
multi.important={}
multi.IsHeld=multi.isHeld
function multi.executeFunction(name,...)
if type(_G[name])=='function' then
_G[name](...)
else
multi.print('Error: Not a function')
end
end
function multi:getChildren()
return self.Mainloop
end
function multi:getVersion()
return multi.Version
end
function multi:getPlatform()
if love then
if love.thread then
return "love2d"
end
else
return "lanes"
end
end
function multi:canSystemThread()
return false
end
--Processor --Processor
function multi:getError()
if self.error then
return self.error
end
end
function multi:benchMark(sec,p,pt)
local c = 0
local temp=self:newLoop(function(self,t)
if t>sec then
if pt then
multi.print(pt.." "..c.." Steps in "..sec.." second(s)!")
end
self.tt(sec,c)
self:Destroy()
else
c=c+1
end
end)
temp:setPriority(p or 1)
function temp:OnBench(func)
self.tt=func
end
self.tt=function() end
return temp
end
function multi.Round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
function multi.AlignTable(tab)
local longest = {}
local columns = #tab[1]
local rows = #tab
for i=1, columns do
longest[i] = -math.huge
end
for i = 1,rows do
for j = 1,columns do
tab[i][j] = tostring(tab[i][j])
if #tab[i][j]>longest[j] then
longest[j] = #tab[i][j]
end
end
end
for i = 1,rows do
for j = 1,columns do
if tab[i][j]~=nil and #tab[i][j]<longest[j] then
tab[i][j]=tab[i][j]..string.rep(" ",longest[j]-#tab[i][j])
end
end
end
local str = {}
for i = 1,rows do
str[#str+1] = table.concat(tab[i]," ")
end
return table.concat(str,"\n")
end
local priorityTable = {[0]="Round-Robin",[1]="Just-Right",[2]="Top-heavy",[3]="Timed-Based-Balancer"} local priorityTable = {[0]="Round-Robin",[1]="Just-Right",[2]="Top-heavy",[3]="Timed-Based-Balancer"}
local ProcessName = {[true]="SubProcessor",[false]="MainProcessor"} local ProcessName = {[true]="SubProcessor",[false]="MainProcessor"}
function multi:getTasksDetails(t) function multi:getTasksDetails(t)
@ -430,63 +160,8 @@ function multi:getTasksDetails(t)
return str return str
end end
end end
function multi:endTask(TID)
self.Mainloop[TID]:Destroy()
return self
end
function multi.startFPSMonitior()
if not multi.runFPS then
multi.doFPS(s)
multi.runFPS=true
end
end
function multi.doFPS(s)
multi:benchMark(1):OnBench(doFPS)
if s then
multi.fps=s
end
end
--Helpers --Helpers
function multi.timer(func,...)
local timer=multi:newTimer()
timer:Start()
args={func(...)}
local t = timer:Get()
timer = nil
return t,unpack(args)
end
function multi:IsAnActor()
return self.Act~=nil
end
function multi:OnMainConnect(func)
table.insert(self.func,func)
return self
end
function multi:reallocate(o,n)
n=n or #o.Mainloop+1
local int=self.Parent
self:Destroy()
self.Parent=o
table.insert(o.Mainloop,n,self)
self.Active=true
end
multi.Reallocate=multi.Reallocate
function multi:FreeMainEvent()
self.func={}
end
function multi:connectFinal(func)
if self.Type=='event' then
self:OnEvent(func)
elseif self.Type=='alarm' then
self:OnRing(func)
elseif self.Type=='step' or self.Type=='tstep' then
self:OnEnd(func)
else
multi.print("Warning!!! "..self.Type.." doesn't contain a Final Connection State! Use "..self.Type..":Break(func) to trigger it's final event!")
self:OnBreak(func)
end
end
multi.ConnectFinal=multi.connectFinal
function multi:Break() function multi:Break()
self:Pause() self:Pause()
self.Active=nil self.Active=nil
@ -496,21 +171,23 @@ function multi:Break()
end end
end end
end end
function multi:OnBreak(func) function multi:OnBreak(func)
table.insert(self.ender,func) table.insert(self.ender,func)
end end
function multi:isPaused() function multi:isPaused()
return not(self.Active) return not(self.Active)
end end
multi.IsPaused=multi.isPaused
function multi:isActive() function multi:isActive()
return self.Active return self.Active
end end
multi.IsActive=multi.isActive
function multi:getType() function multi:getType()
return self.Type return self.Type
end end
multi.GetType=multi.getType
-- Advance Timer stuff -- Advance Timer stuff
function multi:SetTime(n) function multi:SetTime(n)
if not n then n=3 end if not n then n=3 end
@ -533,7 +210,7 @@ function multi:SetTime(n)
end end
return self return self
end end
multi.ResetTime=multi.SetTime
function multi:ResolveTimer(...) function multi:ResolveTimer(...)
self._timer:Pause() self._timer:Pause()
for i=1,#self.funcTMR do for i=1,#self.funcTMR do
@ -542,14 +219,17 @@ function multi:ResolveTimer(...)
self:Pause() self:Pause()
return self return self
end end
function multi:OnTimedOut(func) function multi:OnTimedOut(func)
self.funcTM[#self.funcTM+1]=func self.funcTM[#self.funcTM+1]=func
return self return self
end end
function multi:OnTimerResolved(func) function multi:OnTimerResolved(func)
self.funcTMR[#self.funcTMR+1]=func self.funcTMR[#self.funcTMR+1]=func
return self return self
end end
-- Timer stuff done -- Timer stuff done
multi.PausedObjects = {} multi.PausedObjects = {}
function multi:Pause() function multi:Pause()
@ -568,6 +248,7 @@ function multi:Pause()
end end
return self return self
end end
function multi:Resume() function multi:Resume()
if self.Type=='process' or self.Type=='mainprocess' then if self.Type=='process' or self.Type=='mainprocess' then
self.Active=true self.Active=true
@ -584,6 +265,7 @@ function multi:Resume()
end end
return self return self
end end
function multi:Destroy() function multi:Destroy()
if self.Type=='process' or self.Type=='mainprocess' then if self.Type=='process' or self.Type=='mainprocess' then
local c=self:getChildren() local c=self:getChildren()
@ -600,26 +282,29 @@ function multi:Destroy()
break break
end end
end end
self.Active=false multi.setType(self,multi.DestroyedObj)
end end
return self return self
end end
function multi:Reset(n) function multi:Reset(n)
self:Resume() self:Resume()
return self return self
end end
function multi:isDone() function multi:isDone()
return self.Active~=true return self.Active~=true
end end
multi.IsDone=multi.isDone
function multi:create(ref) function multi:create(ref)
multi.OnObjectCreated:Fire(ref,self) multi.OnObjectCreated:Fire(ref,self)
end end
function multi:setName(name) function multi:setName(name)
self.Name = name self.Name = name
return self return self
end end
multi.SetName = multi.setName
--Constructors [CORE] --Constructors [CORE]
local _tid = 0 local _tid = 0
function multi:newBase(ins) function multi:newBase(ins)
@ -636,10 +321,8 @@ function multi:newBase(ins)
c.funcTMR={} c.funcTMR={}
c.ender={} c.ender={}
c.TID = _tid c.TID = _tid
c.important={}
c.Act=function() end c.Act=function() end
c.Parent=self c.Parent=self
c.held=false
c.creationTime = os.clock() c.creationTime = os.clock()
if ins then if ins then
table.insert(self.Mainloop,ins,c) table.insert(self.Mainloop,ins,c)
@ -823,6 +506,7 @@ function multi:newConnection(protect,func,kill)
end end
multi.OnObjectCreated=multi:newConnection() multi.OnObjectCreated=multi:newConnection()
multi.OnObjectDestroyed=multi:newConnection() multi.OnObjectDestroyed=multi:newConnection()
multi.OnLoad = multi:newConnection(nil,nil,true)
ignoreconn = false ignoreconn = false
function multi:newProcessor(file) function multi:newProcessor(file)
if not(self.Type=='mainprocess') then error('Can only create an interface on the multi obj') return false end if not(self.Type=='mainprocess') then error('Can only create an interface on the multi obj') return false end
@ -924,14 +608,7 @@ function multi:newTimer()
self:create(c) self:create(c)
return c return c
end end
function multi.nextStep(func)
ncount = ncount+1
if not next then
next = {func}
else
next[#next+1] = func
end
end
--Core Actors --Core Actors
function multi:newEvent(task) function multi:newEvent(task)
local c=self:newBase() local c=self:newBase()
@ -1277,30 +954,9 @@ function multi:scheduleJob(time,func)
end end
table.insert(scheduledjobs,{time, func,false}) table.insert(scheduledjobs,{time, func,false})
end end
-- Special Events
local _os = os.exit
function os.exit(n)
multi.OnExit:Fire(n or 0)
_os(n)
end
multi.OnPreLoad = multi:newConnection()
multi.OnLoad = multi:newConnection(nil,nil,true)
multi.OnExit = multi:newConnection(nil,nil,true)
multi.m = {onexit = function() multi.OnExit:Fire() end}
if _VERSION >= "Lua 5.2" then
setmetatable(multi.m, {__gc = multi.m.onexit})
else
multi.m.sentinel = newproxy(true)
getmetatable(multi.m.sentinel).__gc = multi.m.onexit
end
-- Threading stuff -- Threading stuff
multi.GlobalVariables={} 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
thread.requests = {}
local dFunc = function() return true end local dFunc = function() return true end
local dRef = {nil,nil,nil} local dRef = {nil,nil,nil}
function thread.request(t,cmd,...) function thread.request(t,cmd,...)
@ -1619,6 +1275,7 @@ function multi.initThreads(justThreads)
if type(ret)=="table" then if type(ret)=="table" then
if ret[1]=="_kill_" then if ret[1]=="_kill_" then
threads[i].OnDeath:Fire(threads[i],"killed",ret,r1,r2,r3,r4,r5,r6) threads[i].OnDeath:Fire(threads[i],"killed",ret,r1,r2,r3,r4,r5,r6)
multi.setType(threads[i],multi.DestroyedObj)
table.remove(threads,i) table.remove(threads,i)
ret = nil ret = nil
elseif ret[1]=="_sleep_" then elseif ret[1]=="_sleep_" then
@ -1670,6 +1327,7 @@ function multi.initThreads(justThreads)
for i=#threads,1,-1 do for i=#threads,1,-1 do
if threads[i].isError then if threads[i].isError then
threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets))
multi.setType(threads[i],multi.DestroyedObj)
table.remove(threads,i) table.remove(threads,i)
end end
if threads[i] and not threads[i].__started then if threads[i] and not threads[i].__started then
@ -1685,6 +1343,7 @@ function multi.initThreads(justThreads)
end end
if threads[i] and coroutine.status(threads[i].thread)=="dead" then if threads[i] and coroutine.status(threads[i].thread)=="dead" then
threads[i].OnDeath:Fire(threads[i],"ended",unpack(threads[i].TempRets or {})) threads[i].OnDeath:Fire(threads[i],"ended",unpack(threads[i].TempRets or {}))
multi.setType(threads[i],multi.DestroyedObj)
table.remove(threads,i) table.remove(threads,i)
elseif threads[i] and threads[i].task == "skip" then elseif threads[i] and threads[i].task == "skip" then
threads[i].pos = threads[i].pos + 1 threads[i].pos = threads[i].pos + 1
@ -1749,7 +1408,6 @@ end
function multi:threadloop() function multi:threadloop()
multi.initThreads(true) multi.initThreads(true)
end end
multi.OnError=multi:newConnection()
function multi:newService(func) -- Priority managed threads function multi:newService(func) -- Priority managed threads
local c = {} local c = {}
c.Type = "service" c.Type = "service"
@ -1778,11 +1436,16 @@ function multi:newService(func) -- Priority managed threads
task(ap) task(ap)
return c return c
end end
multi:newThread(function() local th = multi:newThread(function()
while true do while true do
process() process()
end end
end).OnError = c.OnError -- use the threads onerror as our own end)
th.OnError = c.OnError -- use the threads onerror as our own
function c.Destroy()
th:kill()
multi.setType(c,multi.DestroyedObj)
end
function c:SetScheme(n) function c:SetScheme(n)
if type(self)=="number" then n = self end if type(self)=="number" then n = self end
scheme = n scheme = n
@ -1862,187 +1525,11 @@ multi.Jobs.OnStarted(function(self,jobs)
end) end)
multi.Jobs.SetPriority(multi.Priority_Normal) multi.Jobs.SetPriority(multi.Priority_Normal)
multi.Jobs.Start() multi.Jobs.Start()
function multi:newThreadedProcess(name)
local c = {}
local holding = false
local kill = false
setmetatable(c, multi)
function c:newBase(ins)
local ct = {}
ct.Active=true
ct.func={}
ct.ender={}
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.Type='threadedprocess'
c.Mainloop={}
c.Garbage={}
c.Children={}
c.Active=true
c.Rest=0
c.updaterate=.01
c.restRate=.1
c.queue={}
c.rest=false
function c:getController()
return nil
end
function c:Start()
self.rest=false
return self
end
function c:Resume()
self.rest=false
return self
end
function c:Pause()
self.rest=true
return self
end
function c:Remove()
self.ref:kill()
return self
end
function c:Kill()
kill = true
return self
end
function c:Sleep(n)
holding = true
if type(n)=="number" then
multi:newAlarm(n):OnRing(function(a)
holding = false
a:Destroy()
end):setName("multi.TPSleep")
elseif type(n)=="function" then
multi:newEvent(n):OnEvent(function(e)
holding = false
e:Destroy()
end):setName("multi.TPHold")
end
return self
end
c.Hold=c.Sleep
multi:newThread(name,function(ref)
while true do
thread.hold(function()
return not(holding)
end)
c:uManager()
end
end)
multi:create(c)
return c
end
function multi:newHyperThreadedProcess(name)
if not name then error("All threads must have a name!") end
local c = {}
setmetatable(c, multi)
local ind = 0
local holding = true
local kill = false
function c:newBase(ins)
local ct = {}
ct.Active=true
ct.func={}
ct.ender={}
ct.Act=function() end
ct.Parent=self
ct.held=false
ct.ref=self.ref
ind = ind + 1
multi:newThread("Proc <"..name.."> #"..ind,function()
while true do
thread.hold(function()
return not(holding)
end)
if kill then
err=coroutine.yield({"_kill_"})
if err then
error("Failed to kill a thread! Exiting...")
end
end
ct:Act()
end
end)
return ct
end
c.Parent=self
c.Active=true
c.func={}
c.Type='hyperthreadedprocess'
c.Mainloop={}
c.Garbage={}
c.Children={}
c.Active=true
c.Rest=0
c.updaterate=.01
c.restRate=.1
c.queue={}
c.rest=false
function c:getController()
return nil
end
function c:Start()
holding = false
return self
end
function c:Resume()
holding = false
return self
end
function c:Pause()
holding = true
return self
end
function c:Remove()
self.ref:kill()
return self
end
function c:Kill()
kill = true
return self
end
function c:Sleep(b)
holding = true
if type(b)=="number" then
local t = os.clock()
multi:newAlarm(b):OnRing(function(a)
holding = false
a:Destroy()
end):setName("multi.HTPSleep")
elseif type(b)=="function" then
multi:newEvent(b):OnEvent(function(e)
holding = false
e:Destroy()
end):setName("multi.HTPHold")
end
return self
end
c.Hold=c.Sleep
multi:create(c)
return c
end
-- Multi runners -- Multi runners
function multi:lightloop() function multi:lightloop()
if not isRunning then if not isRunning then
local Loop=self.Mainloop local Loop=self.Mainloop
while true do while true do
if next then
local DD = table.remove(next,1)
while DD do
DD()
DD = table.remove(next,1)
end
end
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
if Loop[_D].Active then if Loop[_D].Active then
self.CID=_D self.CID=_D
@ -2100,13 +1587,6 @@ function multi:mainloop(settings)
local cc=0 local cc=0
multi.OnLoad:Fire() multi.OnLoad:Fire()
while mainloopActive do while mainloopActive do
if next then
local DD = table.remove(next,1)
while DD do
DD()
DD = table.remove(next,1)
end
end
if priority == 1 then if priority == 1 then
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
for P=1,7 do for P=1,7 do
@ -2300,13 +1780,6 @@ function multi:uManager(settings)
end end
function multi:uManagerRef(settings) function multi:uManagerRef(settings)
if self.Active then if self.Active then
if next then
local DD = table.remove(next,1)
while DD do
DD()
DD = table.remove(next,1)
end
end
local Loop=self.Mainloop local Loop=self.Mainloop
local PS=self local PS=self
if multi.defaultSettings.priority==1 then if multi.defaultSettings.priority==1 then
@ -2464,4 +1937,5 @@ function multi:uManagerRef(settings)
end end
end end
end end
require("multi.utils").init(multi,thread)
return multi return multi

339
multi/utils.lua Normal file
View File

@ -0,0 +1,339 @@
function table.merge(t1, t2)
for k,v in pairs(t2) do
if type(v) == 'table' then
if type(t1[k] or false) == 'table' then
table.merge(t1[k] or {}, t2[k] or {})
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
local function init(multi,thread)
if table.unpack and not unpack then
unpack=table.unpack
end
multi.DestroyedObj = {
Type = "destroyed",
}
local function uni()
return multi.DestroyedObj
end
local function uniN() end
function multi.setType(obj,t)
if t == multi.DestroyedObj then
for i,v in pairs(obj) do
obj[i] = nil
end
setmetatable(obj, {
__index = function(t,k)
return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni})
end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni
})
end
end
setmetatable(multi.DestroyedObj, {
__index = function(t,k)
return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni})
end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni
})
math.randomseed(os.time())
multi.defaultSettings = {
priority = 0,
protect = false,
}
function multi:enableLoadDetection()
if multi.maxSpd then return end
-- here we are going to run a quick benchMark solo
local temp = multi:newProcessor()
temp:Start()
local t = os.clock()
local stop = false
temp:benchMark(.01):OnBench(function(time,steps)
stop = steps
end)
while not stop do
temp:uManager()
end
temp:Destroy()
multi.maxSpd = stop
end
local busy = false
local lastVal = 0
local bb = 0
function multi:getLoad()
if not multi.maxSpd then multi:enableLoadDetection() end
if busy then return lastVal end
local val = nil
if thread.isThread() then
local bench
multi:benchMark(.01):OnBench(function(time,steps)
bench = steps
bb = steps
end)
thread.hold(function()
return bench
end)
bench = bench^1.5
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
else
busy = true
local bench
multi:benchMark(.01):OnBench(function(time,steps)
bench = steps
bb = steps
end)
while not bench do
multi:uManager()
end
bench = bench^1.5
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
busy = false
end
if val<0 then val = 0 end
if val > 100 then val = 100 end
lastVal = val
return val,bb*100
end
function multi:setPriority(s)
if type(s)==number then
self.Priority=s
elseif type(s)=='string' then
if s:lower()=='core' or s:lower()=='c' then
self.Priority=self.Priority_Core
elseif s:lower()=="very high" or s:lower()=="vh" then
self.Priority=self.Priority_Very_High
elseif s:lower()=='high' or s:lower()=='h' then
self.Priority=self.Priority_High
elseif s:lower()=='above' or s:lower()=='a' then
self.Priority=self.Priority_Above_Normal
elseif s:lower()=='normal' or s:lower()=='n' then
self.Priority=self.Priority_Normal
elseif s:lower()=='below' or s:lower()=='b' then
self.Priority=self.Priority_Below_Normal
elseif s:lower()=='low' or s:lower()=='l' then
self.Priority=self.Priority_Low
elseif s:lower()=="very low" or s:lower()=="vl" then
self.Priority=self.Priority_Very_Low
elseif s:lower()=='idle' or s:lower()=='i' then
self.Priority=self.Priority_Idle
end
self.solid = true
end
if not self.PrioritySet then
self.defPriority = self.Priority
self.PrioritySet = true
end
end
function multi:ResetPriority()
self.Priority = self.defPriority
end
function os.getOS()
if package.config:sub(1,1)=='\\' then
return 'windows'
else
return 'unix'
end
end
if os.getOS()=='windows' then
function os.sleep(n)
if n > 0 then os.execute('ping -n ' .. tonumber(n+1) .. ' localhost > NUL') end
end
else
function os.sleep(n)
os.execute('sleep ' .. tonumber(n))
end
end
function multi.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 multi:getParentProcess()
return self.Mainloop[self.CID]
end
function multi:getChildren()
return self.Mainloop
end
function multi:getVersion()
return multi.Version
end
function multi:getPlatform()
if love then
if love.thread then
return "love2d"
end
else
return "lanes"
end
end
function multi:canSystemThread()
return false
end
function multi:getError()
if self.error then
return self.error
end
end
function multi:benchMark(sec,p,pt)
local c = 0
local temp=self:newLoop(function(self,t)
if t>sec then
if pt then
multi.print(pt.." "..c.." Steps in "..sec.." second(s)!")
end
self.tt(sec,c)
self:Destroy()
else
c=c+1
end
end)
temp:setPriority(p or 1)
function temp:OnBench(func)
self.tt=func
end
self.tt=function() end
return temp
end
function multi.Round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
function multi.AlignTable(tab)
local longest = {}
local columns = #tab[1]
local rows = #tab
for i=1, columns do
longest[i] = -math.huge
end
for i = 1,rows do
for j = 1,columns do
tab[i][j] = tostring(tab[i][j])
if #tab[i][j]>longest[j] then
longest[j] = #tab[i][j]
end
end
end
for i = 1,rows do
for j = 1,columns do
if tab[i][j]~=nil and #tab[i][j]<longest[j] then
tab[i][j]=tab[i][j]..string.rep(" ",longest[j]-#tab[i][j])
end
end
end
local str = {}
for i = 1,rows do
str[#str+1] = table.concat(tab[i]," ")
end
return table.concat(str,"\n")
end
function multi:endTask(TID)
self.Mainloop[TID]:Destroy()
return self
end
function multi:IsAnActor()
return self.Act~=nil
end
function multi:reallocate(o,n)
n=n or #o.Mainloop+1
local int=self.Parent
self:Destroy()
self.Parent=o
table.insert(o.Mainloop,n,self)
self.Active=true
end
function multi.timer(func,...)
local timer=multi:newTimer()
timer:Start()
args={func(...)}
local t = timer:Get()
timer = nil
return t,unpack(args)
end
function multi:OnMainConnect(func)
table.insert(self.func,func)
return self
end
function multi:FreeMainEvent()
self.func={}
end
function multi:connectFinal(func)
if self.Type=='event' then
self:OnEvent(func)
elseif self.Type=='alarm' then
self:OnRing(func)
elseif self.Type=='step' or self.Type=='tstep' then
self:OnEnd(func)
else
multi.print("Warning!!! "..self.Type.." doesn't contain a Final Connection State! Use "..self.Type..":Break(func) to trigger it's final event!")
self:OnBreak(func)
end
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
thread.requests = {}
multi.GetType=multi.getType
multi.IsPaused=multi.isPaused
multi.IsActive=multi.isActive
multi.Reallocate=multi.Reallocate
multi.GetParentProcess=multi.getParentProcess
multi.ConnectFinal=multi.connectFinal
multi.ResetTime=multi.SetTime
multi.IsDone=multi.isDone
multi.SetName = multi.setName
-- Special Events
local _os = os.exit
function os.exit(n)
multi.OnExit:Fire(n or 0)
_os(n)
end
multi.OnError=multi:newConnection()
multi.OnPreLoad = multi:newConnection()
multi.OnExit = multi:newConnection(nil,nil,true)
multi.m = {onexit = function() multi.OnExit:Fire() end}
if _VERSION >= "Lua 5.2" then
setmetatable(multi.m, {__gc = multi.m.onexit})
else
multi.m.sentinel = newproxy(true)
getmetatable(multi.m.sentinel).__gc = multi.m.onexit
end
end
return {init=init}

View File

@ -0,0 +1,35 @@
package = "multi"
version = "14.2-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "v14.2.0",
}
description = {
summary = "Lua Multi tasking library",
detailed = [[
This library contains many methods for multi tasking. Features non coroutine based multitasking, coroutine based multi tasking, and system threading (Requires use of an integration). Check github for how to use.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1",
"lanes",
}
build = {
type = "builtin",
modules = {
["multi"] = "multi/init.lua",
["multi.utils"] = "multi/utils.lua",
["multi.compat.love2d"] = "multi/compat/love2d.lua",
["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua",
["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua",
["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua",
["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua",
["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua",
["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua",
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
--["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
--["multi.integration.shared"] = "multi/integration/shared.lua"
}
}

View File

@ -1,10 +1,19 @@
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
--local sterilizer = require("multi.integration.sterilization") --local sterilizer = require("multi.integration.sterilization")
local multi,thread = require("multi"):init() local multi,thread = require("multi"):init()
bin = multi.DestroyedObj local test = multi:newThread(function()
local file = bin.new() while true do
local data = file:getData() thread.sleep(1)
print(data) print("Hello!")
end
end)
local alarm = multi:newAlarm(4):OnRing(function(a)
print(a.Type)
a:Destroy()
print(a.Type)
test:Destroy()
end)
multi:lightloop()
-- function pushJobs() -- function pushJobs()
-- multi.Jobs:newJob(function() -- multi.Jobs:newJob(function()
-- print("job called") -- print("job called")