diff --git a/README.md b/README.md index 502ca86..01549c0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
-Progress is being made in [v15.2.0](https://github.com/rayaman/multi/tree/v15.2.0) +Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3.0) ---
diff --git a/changes.md b/changes.md index e4c591a..77368e5 100644 --- a/changes.md +++ b/changes.md @@ -8,11 +8,138 @@ Table of contents Full Update Showcase ```lua +package.path = "./?/init.lua;"..package.path +multi, thread = require("multi"):init{print=true} +GLOBAL, THREAD = require("multi.integration.threading"):init() +-- Using a system thread, but both system and local threads support this! +-- Don't worry if you don't have lanes or love2d. PesudoThreading will kick in to emulate the threading features if you do not have access to system threading. +func = THREAD:newFunction(function(count) + print("Starting Status test: ",count) + local a = 0 + while true do + a = a + 1 + THREAD.sleep(.1) + -- Push the status from the currently running threaded function to the main thread + THREAD.pushStatus(a,count) + if a == count then break end + end + return "Done" +end) + +thread:newThread("test",function() + local ret = func(10) + ret.OnStatus(function(part,whole) + print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%") + end) + print("TEST",func(5).wait()) + -- The results from the OnReturn connection is passed by thread.hold + print("Status:",thread.hold(ret.OnReturn)) + print("Function Done!") +end).OnError(function(...) + print("Error:",...) +end) + +local ret = func(10) +local ret2 = func(15) +local ret3 = func(20) +local s1,s2,s3 = 0,0,0 +ret.OnError(function(...) + print("Error:",...) +end) +ret2.OnError(function(...) + print("Error:",...) +end) +ret3.OnError(function(...) + print("Error:",...) +end) +ret.OnStatus(function(part,whole) + s1 = math.ceil((part/whole)*1000)/10 + print(s1) +end) +ret2.OnStatus(function(part,whole) + s2 = math.ceil((part/whole)*1000)/10 + print(s2) +end) +ret3.OnStatus(function(part,whole) + s3 = math.ceil((part/whole)*1000)/10 + print(s3) +end) + +loop = multi:newTLoop() + +function loop:testing() + print("testing haha") +end + +loop:Set(1) +t = loop:OnLoop(function() + print("Looping...") +end):testing() + +local proc = multi:newProcessor("Test") +local proc2 = multi:newProcessor("Test2") +local proc3 = proc2:newProcessor("Test3") +proc.Start() +proc2.Start() +proc3.Start() +proc:newThread("TestThread_1",function() + while true do + thread.sleep(1) + end +end) +proc:newThread("TestThread_2",function() + while true do + thread.sleep(1) + end +end) +proc2:newThread("TestThread_3",function() + while true do + thread.sleep(1) + end +end) + +thread:newThread(function() + thread.sleep(1) + local tasks = multi:getStats() + + for i,v in pairs(tasks) do + print("Process: " ..i.. "\n\tTasks:") + for ii,vv in pairs(v.tasks) do + print("\t\t"..vv:getName()) + end + print("\tThreads:") + for ii,vv in pairs(v.threads) do + print("\t\t"..vv:getName()) + end + end + + thread.sleep(10) -- Wait 10 seconds then kill the process! + os.exit() +end) + +multi:mainloop() ``` Added: --- +- `multi:getStats()` + - Returns a structured table where you can access data on processors there tasks and threads: + ```lua + -- Upon calling multi:getStats() the table below is returned + get_Stats_Table { + proc_1 -- table + proc_2 -- table + ... + proc_n -- table + } + proc_Table { + tasks = {alarms,steps,loops,etc} -- All multi objects + threads = {thread_1,thread_2,thread_3,etc} -- Thread objects + } + -- Refer to the objects documentation to see how you can interact with them + ``` + - Reference the Full update showcase for the method in action - `multi:newProcessor(name, nothread)` - If no thread is true auto sets the processor as Active, so proc.run() will start without the need for proc.Start() @@ -150,10 +277,9 @@ Changed: - multi:newTStep now derives it's functionality from multi:newStep (Cut's down on code length a bit) -- Fixed the getTaskDetails to handle the new format for threads - Removed: --- +- `multi:getTasksDetails()` Remade completely and now called `multi:getStats()` - `multi:getError()` Removed when setting protect was removed - `multi:FreeMainEvent()` The new changes with connections make's this function unnecessary - `multi:OnMainConnect(func)` See above diff --git a/multi/init.lua b/multi/init.lua index 80e0b99..adca53d 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -90,109 +90,22 @@ function multi:getProcessors() return processes end -function multi:getTasksDetails(t) - if not(t) then - str = { - {"Type ","Uptime","Priority","TID"} +function multi:getStats() + local stats = { + [multi.Name] = { + threads = multi:getThreads(), + tasks = multi:getTasks() } - local count = 0 - for i,v in pairs(self.Mainloop) do - local name = v.Name or "" - if name~="" then - name = " <"..name..">" - end - count = count + 1 - table.insert(str,{v.Type:sub(1,1):upper()..v.Type:sub(2,-1)..name,multi.Round(os.clock()-v.creationTime,3),self.PriorityResolve[v.Priority],v.TID}) - end - for v,i in pairs(self.PausedObjects) do - local name = v.Name or "" - if name~="" then - name = " <"..name..">" - end - count = count + 1 - if not v.Type == "destroyed" then - table.insert(str,{v.Type:sub(1,1):upper()..v.Type:sub(2,-1)..name,multi.Round(os.clock()-v.creationTime,3),self.PriorityResolve[v.Priority],v.TID}) - end - end - if count == 0 then - table.insert(str,{"Currently no processes running!","","",""}) - end - local s = multi.AlignTable(str) - dat = "" - dat2 = "" - if multi.SystemThreads then - for i = 1,#multi.SystemThreads do - dat2 = dat2.."\n" - end - end - local load, steps = self:getLoad() - local thread_count = 0 - local process_count = 0 - if globalThreads then - local th_tab = { - {"Thread Name","Uptime","TID","Attached To"} - } - local proc_tab = { - {"Process Name", "Uptime", "PID", "Load", "CpS"} - } - for th,process in pairs(globalThreads) do - if tostring(th.isProcessThread) == "destroyed" then - globalThreads[th] = nil - elseif th.isProcessThread then - load, steps = process:getLoad() - process_count = process_count + 1 - table.insert(proc_tab,{th.Name,os.clock()-th.creationTime,(th.PID or "-1"),load,steps}) - else - thread_count = thread_count + 1 - table.insert(th_tab,{th.Name,os.clock()-th.creationTime,(th.TID or "-1"),process.Name}) - end - end - dat = multi.AlignTable(proc_tab).. "\n" - dat = dat .. "\n" .. multi.AlignTable(th_tab) - return "Load on "..ProcessName[(self.Type=="process" and 1 or 2)].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(load,2).."%\nCycles Per Second Per Task: "..steps.."\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nProcesses Running: "..process_count.."\nThreads Running: "..thread_count.."\nSystemThreads Running: "..#(multi.SystemThreads or {}).."\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or false].."\n\n"..dat..dat2.."\n\n"..s - else - return "Load on "..ProcessName[(self.Type=="process" and 1 or 2)].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(load,2).."%\nCycles Per Second Per Task: "..steps.."\n\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nProcesses Running: "..process_count.."\nThreads Running: 0\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or false].."\n\n"..dat2.."\n\n"..s - end - else - local load,steps = self:getLoad() - str = { - ProcessName = (self.Name or "Unnamed"), - MemoryUsage = math.ceil(collectgarbage("count")), - PriorityScheme = priorityTable[multi.defaultSettings.priority or false], - SystemLoad = multi.Round(load,2), - CyclesPerSecondPerTask = steps, - SystemThreadCount = multi.SystemThreads and #multi.SystemThreads or 0 + } + local procs = multi:getProcessors() + for i = 1, #procs do + local proc = procs[i] + stats[proc:getFullName()] = { + threads = proc:getThreads(), + tasks = proc:getTasks() } - str.Tasks = {} - str.PausedTasks = {} - str.Threads = {} - str.Processes = {} - str.Systemthreads = {} - for i,v in pairs(self.Mainloop) do - table.insert(str.Tasks,{Link = v, Type=v.Type,Name=v.Name,Uptime=os.clock()-v.creationTime,Priority=self.PriorityResolve[v.Priority],TID = v.TID}) - end - for v,i in pairs(multi.PausedObjects) do - table.insert(str.Tasks,{Link = v, Type=v.Type,Name=v.Name,Uptime=os.clock()-v.creationTime,Priority=self.PriorityResolve[v.Priority],TID = v.TID}) - end - for th,process in pairs(globalThreads) do - if tostring(th.isProcessThread) == "destroyed" then - globalThreads[th] = nil - elseif th.isProcessThread then - local load, steps = process:getLoad() - table.insert(str.Processes,{Uptime = os.clock()-th.creationTime, Name = th.Name, Link = th, TID = th.TID,Load = load,Steps = steps}) - else - table.insert(str.Threads,{Uptime = os.clock()-th.creationTime,Name = th.Name,Link = th,TID = th.TID,Attached_to = process}) - end - end - str.ThreadCount = #str.Threads - str.ProcessCount = #str.Processes - if multi.SystemThreads then - for i=1,#multi.SystemThreads do - table.insert(str.Systemthreads,{Uptime = os.clock()-multi.SystemThreads[i].creationTime,Name = multi.SystemThreads[i].Name,Link = multi.SystemThreads[i],TID = multi.SystemThreads[i].count}) - end - end - return str end + return stats end --Helpers @@ -654,6 +567,7 @@ function multi:newEvent(task) end c.OnEvent = self:newConnection() self:setPriority("core") + c:SetName(c.Type) multi:create(c) return c end @@ -675,6 +589,7 @@ function multi:newUpdater(skip) return self end c.OnUpdate = self:newConnection() + c:SetName(c.Type) multi:create(c) return c end @@ -711,6 +626,7 @@ function multi:newAlarm(set) self.Parent.Pause(self) return self end + c:SetName(c.Type) multi:create(c) return c end @@ -738,6 +654,7 @@ function multi:newLoop(func,notime) end multi:create(c) + c:SetName(c.Type) return c end @@ -795,6 +712,7 @@ function multi:newStep(start,reset,count,skip) self:Resume() return self end + c:SetName(c.Type) multi:create(c) return c end @@ -830,6 +748,7 @@ function multi:newTLoop(func,set) if func then c.OnLoop(func) end + c:SetName(c.Type) multi:create(c) return c end @@ -879,6 +798,7 @@ function multi:newTStep(start,reset,count,set) self:Resume() return self end + c:SetName(c.Type) multi:create(c) return c end @@ -957,17 +877,19 @@ function multi:newProcessor(name,nothread) handler() end end) + + c.process.__ignore = true c.process.isProcessThread = true c.process.PID = sandcount c.OnError = c.process.OnError function c:getThreads() - return self.threads + return c.threads end function c:getFullName() - return self.parent:getFullName() .. "." .. self.Name + return c.parent:getFullName() .. "." .. c.Name end function c:getName() @@ -1071,7 +993,13 @@ function multi:getThreads() end function multi:getTasks() - return self.Mainloop + local tasks = {} + for i,v in pairs(self.Mainloop) do + if not v.__ignore then + tasks[#tasks+1] = v + end + end + return tasks end function thread.request(t,cmd,...) @@ -1213,6 +1141,8 @@ function thread.pushStatus(...) t.statusconnector:Fire(...) end +local handler + function thread:newFunctionBase(generator,holdme) return function() local tfunc = {} @@ -1230,10 +1160,11 @@ function thread:newFunctionBase(generator,holdme) return nil, "Function is paused" end local rets, err - local function wait(no) - if thread.isThread() and not (no) then - return multi.hold(function() + local function wait() + if thread.isThread() then + return thread.hold(function() if err then + print("ERROR",err) return multi.NIL, err elseif rets then return cleanReturns((rets[1] or multi.NIL),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]) @@ -1241,7 +1172,7 @@ function thread:newFunctionBase(generator,holdme) end) else while not rets and not err do - multi.scheduler:Act() + handler() end if err then return nil,err @@ -1335,6 +1266,10 @@ function thread:newThread(name,func,...) c.OnError = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true) + function c:getName() + return c.Name + end + function c:isPaused() return self._isPaused end @@ -1560,7 +1495,7 @@ co_status = { _=nil r1=nil r2=nil r3=nil r4=nil r5=nil end, } -local handler = coroutine.wrap(function(self) +handler = coroutine.wrap(function(self) local temp_start while true do for start = #startme, 1, -1 do diff --git a/multi/integration/lanesManager/init.lua b/multi/integration/lanesManager/init.lua index 4b8ac89..043c1ad 100644 --- a/multi/integration/lanesManager/init.lua +++ b/multi/integration/lanesManager/init.lua @@ -126,7 +126,7 @@ function multi.InitSystemThreadErrorHandler() if status == "done" or temp.returns:get("returns") then livingThreads[temp.Id] = {false, temp.Name} temp.alive = false - temp.OnDeath:Fire(temp,nil,unpack(({temp.returns:receive(0, "returns")})[2])) + temp.OnDeath:Fire(unpack(({temp.returns:receive(0, "returns")})[2])) GLOBAL["__THREADS__"] = livingThreads table.remove(threads, i) elseif status == "running" then diff --git a/multi/integration/loveManager/init.lua b/multi/integration/loveManager/init.lua index 1be7d0d..f0af116 100644 --- a/multi/integration/loveManager/init.lua +++ b/multi/integration/loveManager/init.lua @@ -80,11 +80,11 @@ function multi:newSystemThread(name,func,...) -- If the thread is not running let's handle that. local thread_err = c.thread:getError() if thread_err == "Thread Killed!\1" then - c.OnDeath:Fire(c,"Thread Killed!") + c.OnDeath:Fire("Thread Killed!") elseif thread_err then c.OnError:Fire(c,thread_err) elseif c.stab.returns then - c.OnDeath:Fire(c,unpack(c.stab.returns)) + c.OnDeath:Fire(unpack(c.stab.returns)) c.stab.returns = nil end end) diff --git a/test.lua b/test.lua index 8493d69..af0c2cb 100644 --- a/test.lua +++ b/test.lua @@ -2,18 +2,34 @@ package.path = "./?/init.lua;"..package.path multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.threading"):init() +-- Using a system thread, but both system and local threads support this! +-- Don't worry if you don't have lanes or love2d. PesudoThreading will kick in to emulate the threading features if you do not have access to system threading. func = THREAD:newFunction(function(count) print("Starting Status test: ",count) local a = 0 while true do a = a + 1 THREAD.sleep(.1) + -- Push the status from the currently running threaded function to the main thread THREAD.pushStatus(a,count) if a == count then break end end return "Done" end) +thread:newThread("test",function() + local ret = func(10) + ret.OnStatus(function(part,whole) + print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%") + end) + print("TEST",func(5).wait()) + -- The results from the OnReturn connection is passed by thread.hold + print("Status:",thread.hold(ret.OnReturn)) + print("Function Done!") +end).OnError(function(...) + print("Error:",...) +end) + local ret = func(10) local ret2 = func(15) local ret3 = func(20) @@ -40,32 +56,56 @@ ret3.OnStatus(function(part,whole) print(s3) end) --- local proc = multi:newProcessor("Test") --- local proc2 = multi:newProcessor("Test2") --- local proc3 = proc2:newProcessor("Test3") +loop = multi:newTLoop() --- function multi:getTaskStats() --- local stats = { --- [multi.Name] = { --- threads = multi:getThreads(), --- tasks = multi:getTasks() --- } --- } --- local procs = multi:getProcessors() --- for i = 1, #procs do --- local proc = procs[i] --- stats[proc:getFullName()] = { --- threads = proc:getThreads(), --- tasks = proc:getTasks() --- } --- end --- return stats --- end +function loop:testing() + print("testing haha") +end --- local tasks = multi:getTaskStats() +loop:Set(1) +t = loop:OnLoop(function() + print("Looping...") +end):testing() --- for i,v in pairs(tasks) do --- print("Process: "..i) --- end +local proc = multi:newProcessor("Test") +local proc2 = multi:newProcessor("Test2") +local proc3 = proc2:newProcessor("Test3") +proc.Start() +proc2.Start() +proc3.Start() +proc:newThread("TestThread_1",function() + while true do + thread.sleep(1) + end +end) +proc:newThread("TestThread_2",function() + while true do + thread.sleep(1) + end +end) +proc2:newThread("TestThread_3",function() + while true do + thread.sleep(1) + end +end) + +thread:newThread(function() + thread.sleep(1) + local tasks = multi:getStats() + + for i,v in pairs(tasks) do + print("Process: " ..i.. "\n\tTasks:") + for ii,vv in pairs(v.tasks) do + print("\t\t"..vv:getName()) + end + print("\tThreads:") + for ii,vv in pairs(v.threads) do + print("\t\t"..vv:getName()) + end + end + + thread.sleep(10) -- Wait 10 seconds then kill the process! + os.exit() +end) multi:mainloop() \ No newline at end of file