diff --git a/Documentation.md b/Documentation.md index 4216445..46209b3 100644 --- a/Documentation.md +++ b/Documentation.md @@ -20,6 +20,34 @@ Current Multi Version: 15.0.0
`multi:threadloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running, but prioritizes threads over multi-objects
`multi:uManager([TABLE settings])` — This runs the mainloop, but does not have its own while loop and thus needs to be within a loop of some kind. +# Global Methods + +`multi:init()` — Uesd to initiate the library, should only be called once +`multi.getCurrentProcess()` — Returns currently running Process +`multi.` + +# Processor Methods + +These methods can be called either on the multi namespace or a process returned by `multi:newProcessor()` + +`proc.Stop()` — Stops the main process/child process. **Note:** If the main process is stopped all child processes are stopped as well +`proc:getTasksDetails([STRING: displaytype])` — Gets a table or string of all the running tasks + +Processor Attributes +--- + +| Attribute | Type | Returns | Description | +---|---|---|--- +Start|Method()|self| Starts the process +Stop|Method()|self| Stops the process +OnError|Connection|connection| Allows connection to the process error handler +Type|Member:`string`|"process"| Contains the type of object +Active|Member:`boolean`|variable| If false the process is not active +Name|Member:`string`|variable| The name set at process creation +process|Thread|thread| A handle to a multi thread object + +[Refer to the objects for more methods](#non-actors) + # Multi Settings **Note:** Most settings have been fined tuned to be at the peak of performance already, however preLoop, protect (Which drastically lowers preformance), and stopOnError should be used freely to fit your needs. diff --git a/changes.md b/changes.md index 3f8bef1..aba1cd0 100644 --- a/changes.md +++ b/changes.md @@ -28,11 +28,90 @@ multi:newThread("testing",function() end).OnError(function(...) print(...) end) + +sandbox = multi:newProcessor() +sandbox:newTLoop(function() + print("testing...") +end,1) + +test2 = multi:newTLoop(function() + print("testing2...") +end,1) + +sandbox:newThread("Test Thread",function() + local a = 0 + while true do + thread.sleep(1) + a = a + 1 + print("Thread Test: ".. multi.getCurrentProcess().Name) + if a == 10 then + sandbox.Stop() + end + end +end).OnError(function(...) + print(...) +end) +multi:newThread("Test Thread",function() + while true do + thread.sleep(1) + print("Thread Test: ".. multi.getCurrentProcess().Name) + end +end).OnError(function(...) + print(...) +end) + +sandbox.Start() + +multi:mainloop() ``` Added: --- -- N/A + +## multi:newProcessor(name) + +```lua +package.path = "./?/init.lua;"..package.path +multi,thread = require("multi"):init() + +sandbox = multi:newProcessor() + +sandbox:newTLoop(function() + print("testing...") +end,1) + +sandbox:newThread("Test Thread",function() + local a = 0 + while true do + thread.sleep(1) + a = a + 1 + print("Thread Test: ".. multi.getCurrentProcess().Name) + if a == 10 then + sandbox.Stop() + end + end +end).OnError(function(...) + print(...) +end) + +sandbox.Start() -- Start the process + +multi:mainloop() +``` + +| Attribute | Type | Returns | Description | +---|---|---|--- +Start|Method()|self| Starts the process +Stop|Method()|self| Stops the process +OnError|Connection|connection| Allows connection to the process error handler +Type|Member:`string`|"process"| Contains the type of object +Active|Member:`boolean`|variable| If false the process is not active +Name|Member:`string`|variable| The name set at process creation +process|Thread|thread| A handle to a multi thread object + +**Note:** All tasks/threads created on a process are linked to that process. If a process is stopped all tasks/threads will be halted until the process is started back up. + + Changed: --- diff --git a/multi/init.lua b/multi/init.lua index db24bad..f4c2c6a 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -120,7 +120,7 @@ function multi:getTasksDetails(t) dat2 = dat2.."\n" end end - local load, steps = multi:getLoad() + local load, steps = self:getLoad() if thread.__threads then for i=1,#thread.__threads do dat = dat .. "\n" @@ -130,7 +130,7 @@ function multi:getTasksDetails(t) return "Load on "..ProcessName[self.Type=="process"].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(load,2).."%\nCycles Per Second Per Task: "..steps.."\n\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: 0\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s..dat2 end elseif t == "t" or t == "table" then - local load,steps = multi:getLoad() + local load,steps = self:getLoad() str = { ProcessName = (self.Name or "Unnamed"), ThreadCount = #thread.__threads, @@ -206,9 +206,9 @@ end -- Advance Timer stuff function multi:SetTime(n) if not n then n=3 end - local c=multi:newBase() + local c=self:newBase() c.Type='timemaster' - c.timer=multi:newTimer() + c.timer=self:newTimer() c.timer:Start() c.set=n c.link=self @@ -552,7 +552,7 @@ function multi:newTimer() time=os.clock()-time return self end - self:create(c) + multi:create(c) return c end @@ -580,7 +580,7 @@ function multi:newEvent(task) return self end self:setPriority("core") - self:create(c) + multi:create(c) return c end function multi:newUpdater(skip) @@ -602,7 +602,7 @@ function multi:newUpdater(skip) return self end c.OnUpdate=self.OnMainConnect - self:create(c) + multi:create(c) return c end function multi:newAlarm(set) @@ -642,7 +642,7 @@ function multi:newAlarm(set) self.Parent.Pause(self) return self end - self:create(c) + multi:create(c) return c end function multi:newLoop(func) @@ -662,7 +662,7 @@ function multi:newLoop(func) table.insert(funcs,func) return self end - self:create(c) + multi:create(c) return c end function multi:newFunction(func) @@ -688,7 +688,7 @@ function multi:newFunction(func) return self end setmetatable(c,mt) - self:create(c) + multi:create(c) return c end function multi:newStep(start,reset,count,skip) @@ -759,7 +759,7 @@ function multi:newStep(start,reset,count,skip) self:Resume() return self end - self:create(c) + multi:create(c) return c end function multi:newTLoop(func,set) @@ -795,7 +795,7 @@ function multi:newTLoop(func,set) table.insert(self.func,func) return self end - self:create(c) + multi:create(c) return c end function multi:setTimeout(func,t) @@ -869,7 +869,7 @@ function multi:newTStep(start,reset,count,set) self:Resume() return self end - self:create(c) + multi:create(c) return c end local scheduledjobs = {} @@ -902,6 +902,48 @@ function multi:scheduleJob(time,func) table.insert(scheduledjobs,{time, func,false}) end +local __CurrentProcess = multi +function multi.getCurrentProcess() + return __CurrentProcess +end + +local globalThreads = {} + +local sandcount = 0 +function multi:newProcessor(name) + local c = {} + setmetatable(c,{__index = self}) + local multi,thread = require("multi"):init() -- We need to capture the t in thread + local name = name or "Processor_"..sandcount + sandcount = sandcount + 1 + c.Mainloop = {} + c.Type = "process" + c.Active = false + c.Name = "multi.process<".. (name or "") .. ">" + c.process = self:newThread(c.Name,function() + while true do + thread.hold(function() + return c.Active + end) + __CurrentProcess = c + c:uManager() + __CurrentProcess = self + end + end) + c.OnError = c.process.OnError + function c.Start() + c.Active = true + return self + end + function c.Stop() + c.Active = false + return self + end + c:attachScheduler() + c.initThreads() + return c +end + -- Threading stuff local initT = false local threadCount = 0 @@ -917,6 +959,7 @@ function thread.request(t,cmd,...) thread.requests[t.thread] = {cmd,{...}} end function thread.getRunningThread() + local threads = globalThreads local t = coroutine.running() if t then for i,v in pairs(threads) do @@ -1036,7 +1079,7 @@ function multi.hold(func,no) end local death = false if type(func)=="number" then - multi:newThread("Hold_func",function() + self:newThread("Hold_func",function() thread.sleep(func) death = true end) @@ -1045,7 +1088,7 @@ function multi.hold(func,no) end else local rets - multi:newThread("Hold_func",function() + self:newThread("Hold_func",function() rets = {thread.hold(func)} death = true end) @@ -1057,11 +1100,11 @@ function multi.hold(func,no) end function multi.holdFor(n,func) local temp - multi:newThread(function() + multi.getCurrentProcess():newThread(function() thread.sleep(n) temp = true end) - return multi.hold(function() + return multi.getCurrentProcess().hold(function() if func() then return func() elseif temp then @@ -1103,7 +1146,7 @@ function thread:newFunction(func,holdme) return cleanReturns(rets[1],rets[2],rets[3],rets[4],rets[5],rets[6],rets[7],rets[8],rets[9],rets[10],rets[11],rets[12],rets[13],rets[14],rets[15],rets[16]) end end - local t = multi:newThread("TempThread",func,...) + local t = multi.getCurrentProcess():newThread("TempThread",func,...) t.OnDeath(function(self,status,...) rets = {...} end) t.OnError(function(self,e) err = e end) if holdme then @@ -1127,308 +1170,313 @@ function multi.setEnv(func,env) return chunk end -function multi:newThread(name,func,...) - multi.OnLoad:Fire() - local func = func or name - if type(name) == "function" then - name = "Thread#"..threadCount - end - local c={} - local env = {self=c} - c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} - c.startArgs = {...} - c.ref={} - c.Name=name - c.thread=coroutine.create(func) - c.sleep=1 - c.Type="thread" - c.TID = threadid - c.firstRunDone=false - c.timer=multi:newTimer() - c._isPaused = false - c.returns = {} - c.isError = false - c.OnError = multi:newConnection(true,nil,true) - c.OnDeath = multi:newConnection(true,nil,true) - function c:isPaused() - return self._isPaused - end - local resumed = false - function c:Pause() - if not self._isPaused then - thread.request(self,"exec",function() - thread.hold(function() - return resumed +function multi:attachScheduler() + local threads = {} + self.threadsRef = threads + function self:newThread(name,func,...) + self.OnLoad:Fire() + local func = func or name + if type(name) == "function" then + name = "Thread#"..threadCount + end + local c={} + local env = {self=c} + c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} + c.startArgs = {...} + c.ref={} + c.Name=name + c.thread=coroutine.create(func) + c.sleep=1 + c.Type="thread" + c.TID = threadid + c.firstRunDone=false + c.timer=self:newTimer() + c._isPaused = false + c.returns = {} + c.isError = false + c.OnError = self:newConnection(true,nil,true) + c.OnDeath = self:newConnection(true,nil,true) + function c:isPaused() + return self._isPaused + end + local resumed = false + function c:Pause() + if not self._isPaused then + thread.request(self,"exec",function() + thread.hold(function() + return resumed + end) + resumed = false + self._isPaused = false end) - resumed = false - self._isPaused = false - end) - self._isPaused = true - end - return self - end - function c:Resume() - resumed = true - return self - end - function c:Kill() - thread.request(self,"kill") - return self - end - c.Destroy = c.Kill - function c.ref:send(name,val) - ret=coroutine.yield({Name=name,Value=val}) - end - function c.ref:get(name) - return self.Globals[name] - end - function c.ref:kill() - dRef[1] = "_kill_" - dRef[2] = "I Was killed by You!" - err = coroutine.yield(dRef) - if err then - error("Failed to kill a thread! Exiting...") - end - end - function c.ref:sleep(n) - if type(n)=="function" then - ret=thread.hold(n) - elseif type(n)=="number" then - ret=thread.sleep(tonumber(n) or 0) - else - error("Invalid Type for sleep!") - end - end - function c.ref:syncGlobals(v) - self.Globals=v - end - table.insert(threads,c) - if initT==false then - multi.initThreads() - end - c.creationTime = os.clock() - threadid = threadid + 1 - self:create(c) - return c -end -function multi:newISOThread(name,func,_env,...) - multi.OnLoad:Fire() - local func = func or name - local env = _env or {} - if not env.thread then - env.thread = thread - end - if not env.multi then - env.multi = multi - end - if type(name) == "function" then - name = "Thread#"..threadCount - end - local func = isolateFunction(func,env) - return self:newThread(name,func) -end -function multi.initThreads(justThreads) - initT = true - multi.scheduler=multi:newLoop():setName("multi.thread") - multi.scheduler.Type="scheduler" - function multi.scheduler:setStep(n) - self.skip=tonumber(n) or 24 - end - multi.scheduler.skip=0 - local t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15 - local r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16 - local ret,_ - local function CheckRets(i) - if threads[i] and not(threads[i].isError) then - if not _ then - threads[i].isError = true - threads[i].TempRets[1] = ret - return + self._isPaused = true end - if ret or r1 or r2 or r3 or r4 or r5 or r6 or r7 or r8 or r9 or r10 or r11 or r12 or r13 or r14 or r15 or r16 then - threads[i].TempRets[1] = ret - threads[i].TempRets[2] = r1 - threads[i].TempRets[3] = r2 - threads[i].TempRets[4] = r3 - threads[i].TempRets[5] = r4 - threads[i].TempRets[6] = r5 - threads[i].TempRets[7] = r6 - threads[i].TempRets[8] = r7 - threads[i].TempRets[9] = r8 - threads[i].TempRets[10] = r9 - threads[i].TempRets[11] = r10 - threads[i].TempRets[12] = r11 - threads[i].TempRets[13] = r12 - threads[i].TempRets[14] = r13 - threads[i].TempRets[15] = r14 - threads[i].TempRets[16] = r15 - threads[i].TempRets[17] = r16 + return self + end + function c:Resume() + resumed = true + return self + end + function c:Kill() + thread.request(self,"kill") + return self + end + c.Destroy = c.Kill + function c.ref:send(name,val) + ret=coroutine.yield({Name=name,Value=val}) + end + function c.ref:get(name) + return self.Globals[name] + end + function c.ref:kill() + dRef[1] = "_kill_" + dRef[2] = "I Was killed by You!" + err = coroutine.yield(dRef) + if err then + error("Failed to kill a thread! Exiting...") end end + function c.ref:sleep(n) + if type(n)=="function" then + ret=thread.hold(n) + elseif type(n)=="number" then + ret=thread.sleep(tonumber(n) or 0) + else + error("Invalid Type for sleep!") + end + end + function c.ref:syncGlobals(v) + self.Globals=v + end + table.insert(threads,c) + table.insert(globalThreads,c) + if initT==false then + self.initThreads() + end + c.creationTime = os.clock() + threadid = threadid + 1 + multi:create(c) + return c end - local function holdconn(n) - if type(ret[n])=="table" and ret[n].Type=='connector' then - local letsgo - ret[n](function(...) letsgo = {...} end) - ret[n] = function() - if letsgo then - return unpack(letsgo) + function self:newISOThread(name,func,_env,...) + self.OnLoad:Fire() + local func = func or name + local env = _env or {} + if not env.thread then + env.thread = thread + end + if not env.multi then + env.multi = self + end + if type(name) == "function" then + name = "Thread#"..threadCount + end + local func = isolateFunction(func,env) + return self:newThread(name,func) + end + function self.initThreads(justThreads) + initT = true + self.scheduler=self:newLoop():setName("multi.thread") + self.scheduler.Type="scheduler" + function self.scheduler:setStep(n) + self.skip=tonumber(n) or 24 + end + self.scheduler.skip=0 + local t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15 + local r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16 + local ret,_ + local function CheckRets(i) + if threads[i] and not(threads[i].isError) then + if not _ then + threads[i].isError = true + threads[i].TempRets[1] = ret + return + end + if ret or r1 or r2 or r3 or r4 or r5 or r6 or r7 or r8 or r9 or r10 or r11 or r12 or r13 or r14 or r15 or r16 then + threads[i].TempRets[1] = ret + threads[i].TempRets[2] = r1 + threads[i].TempRets[3] = r2 + threads[i].TempRets[4] = r3 + threads[i].TempRets[5] = r4 + threads[i].TempRets[6] = r5 + threads[i].TempRets[7] = r6 + threads[i].TempRets[8] = r7 + threads[i].TempRets[9] = r8 + threads[i].TempRets[10] = r9 + threads[i].TempRets[11] = r10 + threads[i].TempRets[12] = r11 + threads[i].TempRets[13] = r12 + threads[i].TempRets[14] = r13 + threads[i].TempRets[15] = r14 + threads[i].TempRets[16] = r15 + threads[i].TempRets[17] = r16 end end end - end - local function helper(i) - if type(ret)=="table" then - if ret[1]=="_kill_" then - threads[i].OnDeath:Fire(threads[i],"killed",ret,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16) - multi.setType(threads[i],multi.DestroyedObj) - table.remove(threads,i) - ret = nil - elseif ret[1]=="_sleep_" then - threads[i].sec = ret[2] - threads[i].time = clock() - threads[i].task = "sleep" - threads[i].__ready = false - ret = nil - elseif ret[1]=="_skip_" then - threads[i].count = ret[2] - threads[i].pos = 0 - threads[i].task = "skip" - threads[i].__ready = false - ret = nil - elseif ret[1]=="_hold_" then - holdconn(2) - threads[i].func = ret[2] - threads[i].task = "hold" - threads[i].__ready = false - threads[i].interval = ret[4] or 0 - threads[i].intervalR = clock() - ret = nil - elseif ret[1]=="_holdF_" then - holdconn(3) - threads[i].sec = ret[2] - threads[i].func = ret[3] - threads[i].task = "holdF" - threads[i].time = clock() - threads[i].__ready = false - threads[i].interval = ret[4] or 0 - threads[i].intervalR = clock() - ret = nil - elseif ret[1]=="_holdW_" then - holdconn(3) - threads[i].count = ret[2] - threads[i].pos = 0 - threads[i].func = ret[3] - threads[i].task = "holdW" - threads[i].time = clock() - threads[i].__ready = false - threads[i].interval = ret[4] or 0 - threads[i].intervalR = clock() - ret = nil + local function holdconn(n) + if type(ret[n])=="table" and ret[n].Type=='connector' then + local letsgo + ret[n](function(...) letsgo = {...} end) + ret[n] = function() + if letsgo then + return unpack(letsgo) + end + end end end - CheckRets(i) - end - multi.scheduler:OnLoop(function(self) - for i=#threads,1,-1 do - if threads[i].isError then - if coroutine.status(threads[i].thread)=="dead" then - threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) - multi.setType(threads[i],multi.DestroyedObj) + local function helper(i) + if type(ret)=="table" then + if ret[1]=="_kill_" then + threads[i].OnDeath:Fire(threads[i],"killed",ret,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16) + self.setType(threads[i],self.DestroyedObj) table.remove(threads,i) + ret = nil + elseif ret[1]=="_sleep_" then + threads[i].sec = ret[2] + threads[i].time = clock() + threads[i].task = "sleep" + threads[i].__ready = false + ret = nil + elseif ret[1]=="_skip_" then + threads[i].count = ret[2] + threads[i].pos = 0 + threads[i].task = "skip" + threads[i].__ready = false + ret = nil + elseif ret[1]=="_hold_" then + holdconn(2) + threads[i].func = ret[2] + threads[i].task = "hold" + threads[i].__ready = false + threads[i].interval = ret[4] or 0 + threads[i].intervalR = clock() + ret = nil + elseif ret[1]=="_holdF_" then + holdconn(3) + threads[i].sec = ret[2] + threads[i].func = ret[3] + threads[i].task = "holdF" + threads[i].time = clock() + threads[i].__ready = false + threads[i].interval = ret[4] or 0 + threads[i].intervalR = clock() + ret = nil + elseif ret[1]=="_holdW_" then + holdconn(3) + threads[i].count = ret[2] + threads[i].pos = 0 + threads[i].func = ret[3] + threads[i].task = "holdW" + threads[i].time = clock() + threads[i].__ready = false + threads[i].interval = ret[4] or 0 + threads[i].intervalR = clock() + ret = nil end end - if threads[i] and not threads[i].__started then - if coroutine.running() ~= threads[i].thread then - _,ret,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16=coroutine.resume(threads[i].thread,unpack(threads[i].startArgs)) + CheckRets(i) + end + self.scheduler:OnLoop(function(self) + for i=#threads,1,-1 do + if threads[i].isError then + if coroutine.status(threads[i].thread)=="dead" then + threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) + self.setType(threads[i],self.DestroyedObj) + table.remove(threads,i) + end + end + if threads[i] and not threads[i].__started then + if coroutine.running() ~= threads[i].thread then + _,ret,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16=coroutine.resume(threads[i].thread,unpack(threads[i].startArgs)) + end + threads[i].__started = true + helper(i) + end + if threads[i] and not _ then + threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) + threads[i].isError = true + end + if threads[i] and coroutine.status(threads[i].thread)=="dead" then + local t = threads[i].TempRets or {} + threads[i].OnDeath:Fire(threads[i],"ended",t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15],t[16]) + self.setType(threads[i],self.DestroyedObj) + table.remove(threads,i) + elseif threads[i] and threads[i].task == "skip" then + threads[i].pos = threads[i].pos + 1 + if threads[i].count==threads[i].pos then + threads[i].task = "" + threads[i].__ready = true + end + elseif threads[i] and threads[i].task == "hold" then + if clock() - threads[i].intervalR>=threads[i].interval then + t0,t1,t2,t3,t4,t5,t6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16 = threads[i].func() + if t0 then + if t0==self.NIL then + t0 = nil + end + threads[i].task = "" + threads[i].__ready = true + end + threads[i].intervalR = clock() + end + elseif threads[i] and threads[i].task == "sleep" then + if clock() - threads[i].time>=threads[i].sec then + threads[i].task = "" + threads[i].__ready = true + end + elseif threads[i] and threads[i].task == "holdF" then + if clock() - threads[i].intervalR>=threads[i].interval then + t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15 = threads[i].func() + if t0 then + threads[i].task = "" + threads[i].__ready = true + elseif clock() - threads[i].time>=threads[i].sec then + threads[i].task = "" + threads[i].__ready = true + t0 = nil + t1 = "TIMEOUT" + end + threads[i].intervalR = clock() + end + elseif threads[i] and threads[i].task == "holdW" then + if clock() - threads[i].intervalR>=threads[i].interval then + threads[i].pos = threads[i].pos + 1 + print(threads[i].pos,threads[i].count) + t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15 = threads[i].func() + if t0 then + threads[i].task = "" + threads[i].__ready = true + elseif threads[i].count==threads[i].pos then + threads[i].task = "" + threads[i].__ready = true + t0 = nil + t1 = "TIMEOUT" + end + threads[i].intervalR = clock() + end + end + if threads[i] and threads[i].__ready then + threads[i].__ready = false + if coroutine.running() ~= threads[i].thread then + _,ret,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16=coroutine.resume(threads[i].thread,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15) + CheckRets(i) + end end - threads[i].__started = true helper(i) end - if threads[i] and not _ then - threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) - threads[i].isError = true + end) + if justThreads then + while true do + self.scheduler:Act() end - if threads[i] and coroutine.status(threads[i].thread)=="dead" then - local t = threads[i].TempRets or {} - threads[i].OnDeath:Fire(threads[i],"ended",t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9],t[10],t[11],t[12],t[13],t[14],t[15],t[16]) - multi.setType(threads[i],multi.DestroyedObj) - table.remove(threads,i) - elseif threads[i] and threads[i].task == "skip" then - threads[i].pos = threads[i].pos + 1 - if threads[i].count==threads[i].pos then - threads[i].task = "" - threads[i].__ready = true - end - elseif threads[i] and threads[i].task == "hold" then - if clock() - threads[i].intervalR>=threads[i].interval then - t0,t1,t2,t3,t4,t5,t6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16 = threads[i].func() - if t0 then - if t0==multi.NIL then - t0 = nil - end - threads[i].task = "" - threads[i].__ready = true - end - threads[i].intervalR = clock() - end - elseif threads[i] and threads[i].task == "sleep" then - if clock() - threads[i].time>=threads[i].sec then - threads[i].task = "" - threads[i].__ready = true - end - elseif threads[i] and threads[i].task == "holdF" then - if clock() - threads[i].intervalR>=threads[i].interval then - t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15 = threads[i].func() - if t0 then - threads[i].task = "" - threads[i].__ready = true - elseif clock() - threads[i].time>=threads[i].sec then - threads[i].task = "" - threads[i].__ready = true - t0 = nil - t1 = "TIMEOUT" - end - threads[i].intervalR = clock() - end - elseif threads[i] and threads[i].task == "holdW" then - if clock() - threads[i].intervalR>=threads[i].interval then - threads[i].pos = threads[i].pos + 1 - print(threads[i].pos,threads[i].count) - t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15 = threads[i].func() - if t0 then - threads[i].task = "" - threads[i].__ready = true - elseif threads[i].count==threads[i].pos then - threads[i].task = "" - threads[i].__ready = true - t0 = nil - t1 = "TIMEOUT" - end - threads[i].intervalR = clock() - end - end - if threads[i] and threads[i].__ready then - threads[i].__ready = false - if coroutine.running() ~= threads[i].thread then - _,ret,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16=coroutine.resume(threads[i].thread,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15) - CheckRets(i) - end - end - helper(i) - end - end) - if justThreads then - while true do - multi.scheduler:Act() end end end function multi:newService(func) -- Priority managed threads local c = {} c.Type = "service" - c.OnStopped = multi:newConnection() - c.OnStarted = multi:newConnection() + c.OnStopped = self:newConnection() + c.OnStarted = self:newConnection() local Service_Data = {} local active local time @@ -1438,7 +1486,7 @@ function multi:newService(func) -- Priority managed threads local scheme = 1 function c.Start() if not active then - time = multi:newTimer() + time = self:newTimer() time:Start() active = true c:OnStarted(c,Service_Data) @@ -1453,7 +1501,7 @@ function multi:newService(func) -- Priority managed threads task(ap) return c end - local th = multi:newThread(function() + local th = self:newThread(function() while true do process() end @@ -1537,6 +1585,7 @@ function multi:lightloop(settings) end end function multi:mainloop(settings) + __CurrentProcess = self multi.OnPreLoad:Fire() multi.defaultSettings = settings or multi.defaultSettings self.uManager=self.uManagerRef @@ -1591,8 +1640,10 @@ function multi:mainloop(settings) self.CID=_D if not protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1614,8 +1665,10 @@ function multi:mainloop(settings) self.CID=_D if not protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1646,8 +1699,10 @@ function multi:mainloop(settings) self.CID=_D if not protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1670,6 +1725,7 @@ function multi:mainloop(settings) if not protect then if sRef.solid then sRef:Act() + __CurrentProcess = self solid = true else time = multi.timer(sRef.Act,sRef) @@ -1686,9 +1742,11 @@ function multi:mainloop(settings) else if Loop[_D].solid then Loop[_D]:Act() + __CurrentProcess = self solid = true else time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D]) + __CurrentProcess = self Loop[_D].solid = true solid = false end @@ -1728,8 +1786,10 @@ function multi:mainloop(settings) self.CID=_D if not protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1748,6 +1808,7 @@ function multi:mainloop(settings) end end function multi:uManager(settings) + __CurrentProcess = self multi.OnPreLoad:Fire() multi.defaultSettings = settings or multi.defaultSettings self.t,self.tt = clock(),0 @@ -1786,8 +1847,10 @@ function multi:uManagerRef(settings) self.CID=_D if not multi.defaultSettings.protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1809,8 +1872,10 @@ function multi:uManagerRef(settings) self.CID=_D if not multi.defaultSettings.protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1837,8 +1902,10 @@ function multi:uManagerRef(settings) self.CID=_D if not protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1861,6 +1928,7 @@ function multi:uManagerRef(settings) if not protect then if sRef.solid then sRef:Act() + __CurrentProcess = self solid = true else time = multi.timer(sRef.Act,sRef) @@ -1877,9 +1945,11 @@ function multi:uManagerRef(settings) else if Loop[_D].solid then Loop[_D]:Act() + __CurrentProcess = self solid = true else time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D]) + __CurrentProcess = self Loop[_D].solid = true solid = false end @@ -1919,8 +1989,10 @@ function multi:uManagerRef(settings) self.CID=_D if not multi.defaultSettings.protect then Loop[_D]:Act() + __CurrentProcess = self else local status, err=pcall(Loop[_D].Act,Loop[_D]) + __CurrentProcess = self if err then Loop[_D].error=err self.OnError:Fire(Loop[_D],err) @@ -1987,7 +2059,7 @@ multi.defaultSettings = { function multi:enableLoadDetection() if multi.maxSpd then return end -- here we are going to run a quick benchMark solo - local temp = multi:newProcessor() + local temp = self:newProcessor() temp:Start() local t = os.clock() local stop = false @@ -2006,12 +2078,12 @@ local lastVal = 0 local bb = 0 function multi:getLoad() - if not multi.maxSpd then multi:enableLoadDetection() end + if not multi.maxSpd then self:enableLoadDetection() end if busy then return lastVal end local val = nil if thread.isThread() then local bench - multi:benchMark(.01):OnBench(function(time,steps) + self:benchMark(.01):OnBench(function(time,steps) bench = steps bb = steps end) @@ -2023,12 +2095,12 @@ function multi:getLoad() else busy = true local bench - multi:benchMark(.01):OnBench(function(time,steps) + self:benchMark(.01):OnBench(function(time,steps) bench = steps bb = steps end) while not bench do - multi:uManager() + self:uManager() end bench = bench^1.5 val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100) @@ -2276,4 +2348,5 @@ else multi.m.sentinel = newproxy(true) getmetatable(multi.m.sentinel).__gc = multi.m.onexit end +multi:attachScheduler() return multi diff --git a/test.lua b/test.lua index c73bb5d..2cb56d3 100644 --- a/test.lua +++ b/test.lua @@ -21,50 +21,41 @@ end).OnError(function(...) print(...) end) --- local sandcount = 0 --- function multi:newProcessor(name) --- local c = {} --- setmetatable(c,{__index = self}) --- local multi,thread = require("multi"):init() -- We need to capture the t in thread --- local name = name or "Processor_"..sandcount --- sandcount = sandcount + 1 --- c.Mainloop = {} --- c.Type = "process" --- c.Active = false --- c.OnError = multi:newConnection() --- c.process = self:newThread(name,function() --- while true do --- thread.hold(function() --- return sandbox.Active --- end) --- c:uManager() --- end --- end).OnError(c.OnError) --- function c.Start() --- c.Active = true --- end --- function c.Stop() --- c.Active = false --- end --- return c --- end +sandbox = multi:newProcessor() +for i,v in pairs(sandbox.process) do + print(i,v) +end +io.read() +sandbox:newTLoop(function() + print("testing...") +end,1) --- sandbox = multi:newProcessor() --- sandbox:newTLoop(function() --- print("testing...") --- end,1) +test2 = multi:newTLoop(function() + print("testing2...") +end,1) --- test2 = multi:newTLoop(function() --- print("testing2...") --- end,1) +sandbox:newThread("Test Thread",function() + local a = 0 + while true do + thread.sleep(1) + a = a + 1 + print("Thread Test: ".. multi.getCurrentProcess().Name) + if a == 10 then + sandbox.Stop() + end + end +end).OnError(function(...) + print(...) +end) +multi:newThread("Test Thread",function() + while true do + thread.sleep(1) + print("Thread Test: ".. multi.getCurrentProcess().Name) + end +end).OnError(function(...) + print(...) +end) --- sandbox:newThread("Test Thread",function() --- while true do --- thread.sleep(1) --- print("Thread Test...") --- end --- end) - --- sandbox.Start() +sandbox.Start() multi:mainloop() \ No newline at end of file