From a64482e6956b6a2c9afa31b755e07372592ec62c Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 1 Jun 2017 20:49:33 -0400 Subject: [PATCH] Added the files Initial file addition --- alarm.lua | 42 ++ all.lua | 17 + compat/love2d.lua | 104 ++++ docs/changelog.txt | 94 ++++ docs/features.txt | 55 ++ function.lua | 19 + init.lua | 940 ++++++++++++++++++++++++++++++++++ intergration/lanesManager.lua | 127 +++++ loop.lua | 26 + step.lua | 77 +++ task.lua | 4 + threading.lua | 158 ++++++ threading/alarm.lua | 50 ++ threading/all.lua | 6 + threading/event.lua | 43 ++ threading/loop.lua | 42 ++ threading/process.lua | 83 +++ threading/step.lua | 92 ++++ threading/tstep.lua | 91 ++++ tloop.lua | 40 ++ trigger.lua | 17 + tstep.lua | 76 +++ updater.lua | 22 + watcher.lua | 34 ++ 24 files changed, 2259 insertions(+) create mode 100644 alarm.lua create mode 100644 all.lua create mode 100644 compat/love2d.lua create mode 100644 docs/changelog.txt create mode 100644 docs/features.txt create mode 100644 function.lua create mode 100644 init.lua create mode 100644 intergration/lanesManager.lua create mode 100644 loop.lua create mode 100644 step.lua create mode 100644 task.lua create mode 100644 threading.lua create mode 100644 threading/alarm.lua create mode 100644 threading/all.lua create mode 100644 threading/event.lua create mode 100644 threading/loop.lua create mode 100644 threading/process.lua create mode 100644 threading/step.lua create mode 100644 threading/tstep.lua create mode 100644 tloop.lua create mode 100644 trigger.lua create mode 100644 tstep.lua create mode 100644 updater.lua create mode 100644 watcher.lua diff --git a/alarm.lua b/alarm.lua new file mode 100644 index 0000000..dd0c74d --- /dev/null +++ b/alarm.lua @@ -0,0 +1,42 @@ +require("multi") +function multi:newAlarm(set) + local c=self:newBase() + c.Type='alarm' + c.Priority=self.Priority_Low + 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:Act() + if self.timer:Get()>=self.set then + self:Pause() + self.Active=false + for i=1,#self.func do + self.func[i](self) + end + end + end + function c:Resume() + self.Parent.Resume(self) + self.timer:Resume() + end + function c:Reset(n) + if n then self.set=n end + self:Resume() + self.timer:Reset() + end + function c:OnRing(func) + table.insert(self.func,func) + end + function c:Pause() + self.timer:Pause() + self.Parent.Pause(self) + end + self:create(c) + return c +end diff --git a/all.lua b/all.lua new file mode 100644 index 0000000..dd6baa7 --- /dev/null +++ b/all.lua @@ -0,0 +1,17 @@ +require("multi.alarm") +require("multi.function") +require("multi.loop") +require("multi.tloop") +require("multi.step") +require("multi.task") +require("multi.threading") +require("multi.trigger") +require("multi.tstep") +require("multi.updater") +require("multi.watcher") +require("multi.threading.alarm") +require("multi.threading.event") +require("multi.threading.loop") +require("multi.threading.process") +require("multi.threading.step") +require("multi.threading.tstep") diff --git a/compat/love2d.lua b/compat/love2d.lua new file mode 100644 index 0000000..ba59f3e --- /dev/null +++ b/compat/love2d.lua @@ -0,0 +1,104 @@ +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 diff --git a/docs/changelog.txt b/docs/changelog.txt new file mode 100644 index 0000000..c23a5e7 --- /dev/null +++ b/docs/changelog.txt @@ -0,0 +1,94 @@ +History: EventManager,EventManager+,MultiManager <-- Current After 6.3.0 Versioning scheme was altered. A.0.0 +Changelog starts at Version A.0.0 +New in A.0.0 + Nothing really however a changelog will now be recorded! + version.major.minor +New in A.1.0 + Changed: multi:newConnection(protect) method + Changed the way you are able to interact with it by adding the __call metamethod + Old usage: + OnUpdate=multi:newConnection() + OnUpdate:connect(function(...) + print("Updating",...) + end) + OnUpdate:Fire(1,2,3) + New usage: notice that connect is no longer needed! Both ways still work! and always will work :) + OnUpdate=multi:newConnection() + OnUpdate(function(...) + print("Updating",...) + end) + OnUpdate:Fire(1,2,3) +New in A.2.0 (12/31/2016) + Added: + connectionobj.getConnection(name) + returns a list of an instance (or instances) of a single connect made with connectionobj:connect(func,name) or connectionobj(func,name) + if you can orginize data before hand you can route info to certain connections thus saving a lot of cpu time. NOTE: only one name per each connection... you can't have 2 of the same names in a dictonary... the last one will be used + Changed: obj=multi:newConnection() + obj:connect(func,name) and obj(func,name) + Added the name argument to allow indexing specific connection objects... Useful when creating an async library +New in A.3.0 (1/29/2017) + Added: + Load detection! + multi.threshold -- minimum amount of cycles that all mObjs should be allotted before the Manager is considered burdened. Defualt: 256 + multi.threstimed -- amount of time when counting the number of cycles, Greater gives a more accurate view of the load, but takes more time. Defualt: .001 + multi:setThreshold(n) -- method used to set multi.threshold + multi:setThrestimed(n) -- method used to set multi.threstimed + multi:getLoad() -- returns a number between 0 and 100 +New in A.4.0 (3/20/2017) + Added: + multiobj:reallocate(ProcessObj) -- changes the parent process of an object + ProcessObj:getController() -- returns the mThread so you can opperate on it like a multiobj + Example1: + require("multimanager") -- require the library + int1=multi:newProcess() -- create a process + int1.NAME="int1" -- give it a name for example purposes + int2=multi:newProcess() -- create another process to reallocate + int2.NAME="int2" -- name this a different name + step=int1:newTStep(1,10) -- create a TStep so we can slowly see what is going on + step:OnStep(function(p,s) -- connect to the onstep event + print(p,s.Parent.NAME) -- print the position and process name + end) + step:OnEnd(function(s) -- when the step ends lets reallocate it to the other process + if s.Parent.NAME=="int1" then -- lets only do this if it is in the int1 process + s:reallocate(int2) -- send it to int2 + s:Reset() -- reset the object + else + print("We are done!") + os.exit() -- end the program when int2 did its thing + end + end) + int1:Start() -- start process 1 + int2:Start() -- start process 2 + multi:mainloop() -- start the main loop + Fixed/Updated: + queuer=multi:newQueuer([string: file]) + Alarms now preform as they should on a queuer + Example2: + int=multi:newQueuer() + step=int:newTStep(1,10,1,.5) + alarm=int:newAlarm(2) + step2=int:newTStep(1,5,1,.5) + step:OnStep(function(p,s) + print(p) + end) + step2:OnStep(function(p,s) + print(p,"!") + end) + alarm:OnRing(function(a) + print("Ring1!!!") + end) + int:OnQueueCompleted(function(s) + s:Pause() + print("Done!") + os.exit() + end) + int:Start() + multi:mainloop() +New in A.4.1 (4/10/2017) + Change: + small change to the hold method to make it a bit more lightweight + Using a timer instead of an alarm object! + Limits to hold: + cannot hold more than 1 object at a time, and doing so could cause a deadlock! + Upcomming: + Threaded objects wrapped in corutines, so you can hold/sleep without problems! \ No newline at end of file diff --git a/docs/features.txt b/docs/features.txt new file mode 100644 index 0000000..fbf6000 --- /dev/null +++ b/docs/features.txt @@ -0,0 +1,55 @@ +'Current Version: A.4.1 stable +MultiManager has 19 Objects: # indicates most commonly used 1-19 1 being the most used by me ++Events #7 ++Alarms #2 ++Loops #3 ++Steps #4 ++TSteps #6 ++Triggers #16 ++Tasks #12 ++Connections #1 -- This is a rather new feature of this library, but has become the most useful for async handling. Knowing this is already 50% of this library ++Timers #14 -- this was tricky because these make up both Alarms and TSteps, but in purly using this standalone is almost non existent ++Jobs #11 ++Process #10 ++Conditions #15 ++Ranges #8 ++Threads #13 ++Functions #5 ++Queuers #17 ++Updaters #9 ++Watchers #18 ++CustomObjects #19 + +Constructors [Runners] +---------------------- Note: multi is the main Processor Obj It cannot be paused or destroyed (kinda) +ProcessObj=multi:newProcess([string: FILE defualt: nil]) +ProcessObj=multi:newQueuer([string: FILE defualt: nil]) + +NOTE: The multi namespace is also a ProcessObj + + +Constructors [ACTORS] +--------------------- Note: everything is a multiObj! +eventObj=multi:newEvent([function: TASK defualt: function() end]) +alarmObj=multi:newAlarm([number: SET defualt: 0]) +loopObj=multi:newLoop([function: FUNC]) +stepObj=multi:newStep([number: START defualt: 0],[number: RESET defualt: inf],[number: COUNT defualt: 1],[number: SKIP defualt: 0]) +tstepObj=multi:newTStep([number: START defualt: 0],[number: RESET defualt: inf],[number: COUNT defualt: 1],[number: SET defualt: 1]) +updaterObj=multi:newUpdater([number: SKIP defualt: 0]) +watcherObj=multi:newWatcher(table: NAMESPACE,string: NAME) +multiObj=multi:newCustomObject([table: OBJREF],[string: T='process']) + +Constructors [Semi-ACTORS] +-------------------------- +multi:newJob(function: func,[string: name]) +multi:newRange(number: a,number: b,[number: c]) +multi:newCondition(func) +void=multi:newThread(string: name,function: func) + +Constructors [NON-ACTORS] +------------------------- +multi:newTrigger(function: func) +multi:newTask(function: func) +multi:newConnection() +multi:newTimer() +multi:newFunction(function: func) diff --git a/function.lua b/function.lua new file mode 100644 index 0000000..8ddbdf7 --- /dev/null +++ b/function.lua @@ -0,0 +1,19 @@ +require("multi") +function multi:newFunction(func) + local c={} + c.func=func + mt={ + __index=multi, + __call=function(self,...) if self.Active then return self:func(...) end local t={...} return "PAUSED" end + } + c.Parent=self + function c:Pause() + self.Active=false + end + function c:Resume() + self.Active=true + end + setmetatable(c,mt) + self:create(c) + return c +end \ No newline at end of file diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..6a07fb3 --- /dev/null +++ b/init.lua @@ -0,0 +1,940 @@ +if table.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 +_print=print +function print(...) + if not __SUPPRESSPRINTS then + _print(...) + end +end +multi = {} +multi.Version={'A',4,1} +multi.stage='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.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() + end +end +--Do not change these ever...Any other number will not work (Unless you are using enablePriority2() then change can be made. Just ensure that Priority_Idle is the greatest and Priority_Core is 1!) +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 and 4 any greater and problems arise +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 +end +function multi:getLoad() + return multi:newFunction(function(self) + 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 + if val>multi.threshold then + return 0 + else + return 100-((val/multi.threshold)*100) + end + end)() +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()=='high' or s:lower()=='h' then + self.Priority=self.Priority_High + elseif s:lower()=='above' or s:lower()=='an' 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()=='bn' then + self.Priority=self.Priority_Below_Normal + elseif s:lower()=='low' or s:lower()=='l' then + self.Priority=self.Priority_Low + elseif s:lower()=='idle' or s:lower()=='i' then + self.Priority=self.Priority_Idle + end + end +end +-- 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:getParentProcess() + return self.Mainloop[self.CID] +end +function multi:Stop() + self.Active=false +end +function multi:condition(cond) + if not self.CD then + self:Pause() + self.held=true + self.CD=cond.condition + elseif not(cond.condition()) then + self.held=false + self:Resume() + self.CD=nil + return false + end + self.Parent:Do_Order() + return true +end +function multi:isHeld() + return self.held +end +function multi.executeFunction(name,...) + if type(_G[name])=='function' then + _G[name](...) + else + print('Error: Not a function') + end +end +function multi:waitFor(obj) + local value=false + self.__waiting=function() + value=true + end + obj:connectFinal(self.__waiting) + self:hold(function() return value end) +end +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 + if r then + for i,v in pairs(_G) do + if type(i)=='table' then + if i.Parent and i.Id and i.Act then + i={} + end + end + end + end + collectgarbage() + local after=collectgarbage('count') + print([[Before rebooting total Ram used was ]]..before..[[Kb +After rebooting total Ram used is ]]..after..[[ Kb +A total of ]]..(before-after)..[[Kb was cleaned up]]) +end +function multi:getChildren() + return self.Mainloop +end +--Processor +function multi:getError() + if self.error then + return self.error + end +end +function multi:Do_Order() + local Loop=self.Mainloop + _G.ID=0 + for _D=#Loop,1,-1 do + if Loop[_D] then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end +end +function multi:enablePriority() + function self:Do_Order() + local Loop=self.Mainloop + _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() + end + end + end + end + PS.PStep=PS.PStep+1 + if PS.PStep>7 then + PS.PStep=1 + end + end +end +function multi:enablePriority2() + function self:Do_Order() + local Loop=self.Mainloop + _G.ID=0 + local PS=self + for _D=#Loop,1,-1 do + if Loop[_D] then + if (PS.PStep)%Loop[_D].Priority==0 then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end + end + PS.PStep=PS.PStep+1 + if PS.PStep>self.Priority_Idle then + PS.PStep=1 + end + end +end +multi.disablePriority=multi.unProtect +function multi:fromfile(path,int) + int=int or self + local test2={} + local test=bin.load(path) + local tp=test:getBlock('s') + if tp=='event' then + test2=int:newEvent(test:getBlock('f')) + local t=test:getBlock('t') + for i=1,#t do + test2:OnEvent(t[i]) + end + elseif tp=='alarm' then + test2=int:newAlarm(test:getBlock('n')) + elseif tp=='loop' then + test2=int:newLoop(test:getBlock('t')[1]) + elseif tp=='step' or tp=='tstep' then + local func=test:getBlock('t') + local funcE=test:getBlock('t') + local funcS=test:getBlock('t') + local tab=test:getBlock('t') + test2=int:newStep() + table.merge(test2,tab) + test2.funcE=funcE + test2.funcS=funcS + test2.func=func + elseif tp=='trigger' then + test2=int:newTrigger(test:getBlock('f')) + elseif tp=='connector' then + test2=int:newConnection() + test2.func=test:getBlock('t') + elseif tp=='timer' then + test2=int:newTimer() + test2.count=tonumber(test:getBlock('n')) + else + print('Error: The file you selected is not a valid multi file object!') + return false + end + return test2 +end +function multi:benchMark(sec,p) + local temp=self:newLoop(function(t,self) + if self.clock()-self.init>self.sec then + self.tt(self.sec,self.c) + self:Destroy() + else + self.c=self.c+1 + end + end) + temp.Priority=p or 1 + function temp:OnBench(func) + self.tt=func + end + self.tt=function() end + temp.sec=sec + temp.init=self.clock() + temp.c=0 + return temp +end +function multi:tofile(path) + local items=self:getChildren() + io.mkDir(io.getName(path)) + for i=1,#items do + items[i]:tofile(io.getName(path)..'\\item'..item[i]..'.dat') + end + local int=bin.new() + int:addBlock('process') + int:addBlock(io.getName(path)) + int:addBlock(#self.Mainloop) + int:addBlock(self.Active) + int:addBlock(self.Rest) + int:addBlock(self.Jobs) + int:tofile() +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 +function multi:isAnActor() + return ({watcher=true,tstep=true,step=true,updater=true,loop=true,alarm=true,event=true})[self.Type] +end +function multi:OnMainConnect(func) + table.insert(self.func,func) +end +function multi:protect() + function self:Do_Order() + local Loop=self.Mainloop + for _D=#Loop,1,-1 do + if Loop[_D]~=nil then + Loop[_D].Id=_D + self.CID=_D + local status, err=pcall(Loop[_D].Act,Loop[_D]) + if err and not(Loop[_D].error) then + Loop[_D].error=err + self.OnError:Fire(err,Loop[_D]) + end + end + end + end +end +function multi:unProtect() + local Loop=self.Mainloop + _G.ID=0 + for _D=#Loop,1,-1 do + if Loop[_D] then + if Loop[_D].Active then + Loop[_D].Id=_D + self.CID=_D + Loop[_D]:Act() + end + end + end +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:setJobSpeed(n) + self.jobUS=n +end +function multi:hasJobs() + return #self.Jobs>0,#self.Jobs +end +function multi:getJobs() + return #self.Jobs +end +function multi:removeJob(name) + for i=#self.Jobs,1,-1 do + if self.Jobs[i][2]==name then + table.remove(self.Jobs,i) + end + end +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 + print("Warning!!! "..self.Type.." doesn't contain a Final Connection State! Use "..self.Type..":Break(function) to trigger it's final event!") + self:OnBreak(func) + end +end +function multi:Break() + self:Pause() + self.Active=nil + for i=1,#self.ender do + if self.ender[i] then + self.ender[i](self) + end + end +end +function multi:OnBreak(func) + table.insert(self.ender,func) +end +function multi:isPaused() + return not(self.Active) +end +function multi:isActive() + return self.Active +end +function multi:getType() + return self.Type +end +function multi:Sleep(n) + self:hold(n) +end +function multi:Pause() + if self.Type=='mainprocess' then + print("You cannot pause the main process. Doing so will stop all methods and freeze your program! However if you still want to use multi:_Pause()") + else + self.Active=false + if self.Parent.Mainloop[self.Id]~=nil then + table.remove(self.Parent.Mainloop,self.Id) + table.insert(self.Parent.Paused,self) + self.PId=#self.Parent.Paused + end + end +end +function multi:Resume() + if self.Type=='process' or self.Type=='mainprocess' then + self.Active=true + local c=self:getChildren() + for i=1,#c do + c[i]:Resume() + end + else + if self:isPaused() then + table.remove(self.Parent.Paused,self.PId) + table.insert(self.Parent.Mainloop,self) + self.Id=#self.Parent.Mainloop + self.Active=true + end + end +end +function multi:resurrect() + table.insert(self.Parent.Mainloop,self) + self.Active=true +end +function multi:Destroy() + if self.Type=='process' or self.Type=='mainprocess' then + local c=self:getChildren() + for i=1,#c do + self.OnObjectDestroyed:Fire(c[i]) + c[i]:Destroy() + end + else + for i=1,#self.Parent.Mainloop do + if self.Parent.Mainloop[i]==self then + self.Parent.OnObjectDestroyed:Fire(self) + table.remove(self.Parent.Mainloop,i) + break + end + end + self.Active=false + end +end + +function multi:hold(task) + self:Pause() + self.held=true + if type(task)=='number' then + local timer=multi:newTimer() + timer:Start() + while timer:Get()0 then +--~ for i=1,#self.Parent.FC do +--~ self.Parent.FC[i]:Fire(...) +--~ end +--~ end + if self.Parent.protect then + local t=pcall(self.func,...) + if t then + return t + end + else + return self.func(...) + end + end, + remove=function(self) + for i=1,#self.Link do + if self.Link[i][2]~=nil then + if self.Link[i][2]==self.ID then + table.remove(self.Link,i) + self.remove=function() end + self.Link=nil + self.ID=nil + return true + end + end + end + end + } + if name then + self.connections[name]=temp + end + return temp + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.func) + m:tofile(path) + end + return c +end +multi.OnObjectCreated=multi:newConnection() +multi.OnObjectDestroyed=multi:newConnection() +function multi:newJob(func,name) + if not(self.Type=='mainprocess' or self.Type=='process') then error('Can only create an object on multi or an interface obj') return false end + local c = {} + if self.Type=='process' then + setmetatable(c, self.Parent) + else + setmetatable(c, self) + end + c.Active=true + c.func={} + c.Id=0 + c.PId=0 + c.Parent=self + c.Type='job' + c.trigfunc=func or function() end + function c:Act() + self:trigfunc(self) + end + table.insert(self.Jobs,{c,name}) + if self.JobRunner==nil then + self.JobRunner=self:newAlarm(self.jobUS) + self.JobRunner:OnRing(function(self) + if #self.Parent.Jobs>0 then + if self.Parent.Jobs[1] then + self.Parent.Jobs[1][1]:Act() + table.remove(self.Parent.Jobs,1) + end + end + self:Reset(self.Parent.jobUS) + end) + end +end +function multi:newRange() + local selflink=self + local temp={ + getN = function(self) selflink:Do_Order() self.n=self.n+self.c if self.n>self.b then self.Link.held=false self.Link:Resume() return nil end return self.n end, + } + setmetatable(temp,{ + __call=function(self,a,b,c) + self.c=c or 1 + self.n=a-self.c + self.a=a + self.b=b + self.Link=selflink--.Parent.Mainloop[selflink.CID] or + self.Link:Pause() + self.Link.held=true + return function() return self:getN() end + end + }) + self:create(temp) + return temp +end +function multi:newCondition(func) + local c={['condition']=func} + self:create(c) + return c +end +function multi:mainloop() + for i=1,#self.Tasks do + self.Tasks[i](self) + end + rawset(self,'Start',self.clock()) + while self.Active do + self:Do_Order() + end + print("Did you call multi:Stop()? This method should not be used when using multi:mainloop()! You now need to restart the multi, by using multi:reboot() and calling multi:mainloop() again or by using multi:uManager()") +end +function multi._tFunc(self,dt) + for i=1,#self.Tasks do + self.Tasks[i](self) + end + if dt then + self.pump=true + end + self.pumpvar=dt + rawset(self,'Start',self.clock()) +end +function multi:uManager(dt) + if self.Active then + self:oneTime(self._tFunc,self,dt) + function self:uManager(dt) + self:Do_Order() + end + self:Do_Order() + end +end +--Core Actors +function multi:newCustomObject(objRef,t) + local c={} + if t=='process' then + c=self:newBase() + if type(objRef)=='table' then + table.merge(c,objRef) + end + if not c.Act then + function c:Act() + -- Empty function + end + end + else + c=objRef or {} + end + if not c.Type then + c.Type='coustomObject' + end + self:create(c) + return c +end +function multi:newEvent(task) + local c=self:newBase() + c.Type='event' + c.Task=task or function() end + function c:Act() + if self.Task(self) then + self:Pause() + for _E=1,#self.func do + self.func[_E](self) + end + end + 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 + self:create(c) + return c +end diff --git a/intergration/lanesManager.lua b/intergration/lanesManager.lua new file mode 100644 index 0000000..5144e99 --- /dev/null +++ b/intergration/lanesManager.lua @@ -0,0 +1,127 @@ +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.all") -- 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 +function THREAD.waitFor(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 +-- Step 4 change the coroutine threading methods to work the same, but with lanes TODO when the lanes scheduler is ready! +function THREAD.skip(n) + -- Do Nothing +end +function THREAD.kill() -- trigger the lane destruction + -- coroutine.yield({"_kill_",":)"}) +end +--[[ Step 5 We need to get sleeping working so we need a lane 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 when wait is called we can pack the cmd up and send it to +the sleeping thread manager to send the variable for the other thread to consume, sending only after a certain time has passed! +]] +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.sleep(n) + math.randomseed(os.time()) + local randKey=randomString(12) -- generate a random string a-Z and 0-9 + __SleepingLinda:send("tired","SLEEP|"..randKey.."|"..tostring(n)) -- send the data that needs to be managed + local dat=__SleepingLinda:receive(randKey) + return dat +end +function THREAD.hold(n) + while not(n()) do + -- holding + end +end +-- start the time manager lane +--~ lanes.gen("*", function() +--~ local timers={} +--~ while true do -- forever loop! +--~ local data=__SleepingLinda:receive(.001,"tired") -- timeout after .001 seconds and handle the other stuff +--~ if data then -- the .001 is an entarnal timer that keeps this thread from using too much CPU as well! +--~ print(data) +--~ local cmd,key,sec=data:match("(%S-)|(%S-)|(.+)") +--~ if cmd=="SLEEP" then +--~ print("GOT!") +--~ timers[#timers+1]={os.clock()+tonumber(sec),key} +--~ --__SleepingLinda:set() +--~ elseif cmd=="audit" then +--~ -- +--~ end +--~ end +--~ for i=1,#timers do +--~ if os.clock()>=timers[i][1] then +--~ __SleepingLinda:send(timers[i][2],true) +--~ table.remove(timers,i) +--~ end +--~ end +--~ end +--~ end)() -- The global timer is now activated, and it works great! +-- Step 6 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..">",t) + self:Destroy() + end + end) + return c +end +_G["GLOBAL"]=GLOBAL +_G["__GlobalLinda"]=__GlobalLinda diff --git a/loop.lua b/loop.lua new file mode 100644 index 0000000..86fe3a6 --- /dev/null +++ b/loop.lua @@ -0,0 +1,26 @@ +require("multi") +function multi:newLoop(func) + local c=self:newBase() + c.Type='loop' + c.Start=self.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:Act() + for i=1,#self.func do + self.func[i](self.Parent.clock()-self.Start,self) + end + end + function c:OnLoop(func) + table.insert(self.func,func) + end + self:create(c) + return c +end diff --git a/step.lua b/step.lua new file mode 100644 index 0000000..0fd417e --- /dev/null +++ b/step.lua @@ -0,0 +1,77 @@ +require("multi") +function multi:newStep(start,reset,count,skip) + local c=self:newBase() + think=1 + c.Type='step' + 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: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 + 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.Active=nil + 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 + self:create(c) + return c +end \ No newline at end of file diff --git a/task.lua b/task.lua new file mode 100644 index 0000000..3c266bc --- /dev/null +++ b/task.lua @@ -0,0 +1,4 @@ +require("multi") +function multi:newTask(func) + table.insert(self.Tasks,func) +end \ No newline at end of file diff --git a/threading.lua b/threading.lua new file mode 100644 index 0000000..fc4dccb --- /dev/null +++ b/threading.lua @@ -0,0 +1,158 @@ +require("multi.updater") +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() diff --git a/threading/alarm.lua b/threading/alarm.lua new file mode 100644 index 0000000..1e90ea2 --- /dev/null +++ b/threading/alarm.lua @@ -0,0 +1,50 @@ +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 diff --git a/threading/all.lua b/threading/all.lua new file mode 100644 index 0000000..b28e005 --- /dev/null +++ b/threading/all.lua @@ -0,0 +1,6 @@ +require("multi.threading.step") +require("multi.threading.tstep") +require("multi.threading.alarm") +require("multi.threading.loop") +require("multi.threading.event") +require("multi.threading.process") diff --git a/threading/event.lua b/threading/event.lua new file mode 100644 index 0000000..75c6749 --- /dev/null +++ b/threading/event.lua @@ -0,0 +1,43 @@ +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 diff --git a/threading/loop.lua b/threading/loop.lua new file mode 100644 index 0000000..6b3e623 --- /dev/null +++ b/threading/loop.lua @@ -0,0 +1,42 @@ +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 diff --git a/threading/process.lua b/threading/process.lua new file mode 100644 index 0000000..09ff660 --- /dev/null +++ b/threading/process.lua @@ -0,0 +1,83 @@ +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 diff --git a/threading/step.lua b/threading/step.lua new file mode 100644 index 0000000..bfe37d4 --- /dev/null +++ b/threading/step.lua @@ -0,0 +1,92 @@ +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 diff --git a/threading/tstep.lua b/threading/tstep.lua new file mode 100644 index 0000000..9236dcc --- /dev/null +++ b/threading/tstep.lua @@ -0,0 +1,91 @@ +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 diff --git a/tloop.lua b/tloop.lua new file mode 100644 index 0000000..a3d205f --- /dev/null +++ b/tloop.lua @@ -0,0 +1,40 @@ +require("multi") +function multi:newTLoop(func,set) + local c=self:newBase() + c.Type='tloop' + c.set=set or 0 + c.timer=self:newTimer() + c.life=0 + 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:Act() + if self.timer:Get()>=self.set then + self.life=self.life+1 + for i=1,#self.func do + self.func[i](self,self.life) + end + self.timer:Reset() + end + end + function c:Resume() + self.Parent.Resume(self) + self.timer:Resume() + end + function c:Pause() + self.timer:Pause() + self.Parent.Pause(self) + end + function c:OnLoop(func) + table.insert(self.func,func) + end + self:create(c) + return c +end diff --git a/trigger.lua b/trigger.lua new file mode 100644 index 0000000..5bde5c2 --- /dev/null +++ b/trigger.lua @@ -0,0 +1,17 @@ +require("multi") +function multi:newTrigger(func) + local c={} + c.Type='trigger' + c.trigfunc=func or function() end + function c:Fire(...) + self:trigfunc(self,...) + end + function c:tofile(path) + local m=bin.new() + m:addBlock(self.Type) + m:addBlock(self.trigfunc) + m:tofile(path) + end + self:create(c) + return c +end \ No newline at end of file diff --git a/tstep.lua b/tstep.lua new file mode 100644 index 0000000..1c2dde6 --- /dev/null +++ b/tstep.lua @@ -0,0 +1,76 @@ +require("multi") +function multi:newTStep(start,reset,count,set) + local c=self:newBase() + think=1 + c.Type='tstep' + 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=self.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=self.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: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 + 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=self.clock() + self:Resume() + end + self:create(c) + return c +end \ No newline at end of file diff --git a/updater.lua b/updater.lua new file mode 100644 index 0000000..dc39d5f --- /dev/null +++ b/updater.lua @@ -0,0 +1,22 @@ +require("multi") +function multi:newUpdater(skip) + local c=self:newBase() + c.Type='updater' + c.pos=1 + c.skip=skip or 1 + function c:Act() + if self.pos>=self.skip then + self.pos=0 + for i=1,#self.func do + self.func[i](self) + end + end + self.pos=self.pos+1 + end + function c:setSkip(n) + self.skip=n + end + c.OnUpdate=self.OnMainConnect + self:create(c) + return c +end \ No newline at end of file diff --git a/watcher.lua b/watcher.lua new file mode 100644 index 0000000..31a3483 --- /dev/null +++ b/watcher.lua @@ -0,0 +1,34 @@ +require("multi") +function multi:newWatcher(namespace,name) + local function WatcherObj(ns,n) + if self.Type=='queue' then + print("Cannot create a watcher on a queue! Creating on 'multi' instead!") + self=multi + end + local c=self:newBase() + c.Type='watcher' + c.ns=ns + c.n=n + c.cv=ns[n] + function c:OnValueChanged(func) + table.insert(self.func,func) + end + function c:Act() + if self.cv~=self.ns[self.n] then + for i=1,#self.func do + self.func[i](self,self.cv,self.ns[self.n]) + end + self.cv=self.ns[self.n] + end + end + self:create(c) + return c + end + if type(namespace)~='table' and type(namespace)=='string' then + return WatcherObj(_G,namespace) + elseif type(namespace)=='table' and (type(name)=='string' or 'number') then + return WatcherObj(namespace,name) + else + print('Warning, invalid arguments! Nothing returned!') + end +end \ No newline at end of file