V15.2.0 #33

Merged
rayaman merged 75 commits from v15.2.0 into master 2022-04-19 18:45:52 -04:00
6 changed files with 239 additions and 138 deletions
Showing only changes of commit 2cc2a57a46 - Show all commits

View File

@ -10,7 +10,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
</br> </br>
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)
--- ---
</br> </br>

View File

@ -8,11 +8,138 @@ Table of contents
Full Update Showcase Full Update Showcase
```lua ```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: 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)` - `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() - 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) - 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: Removed:
--- ---
- `multi:getTasksDetails()` Remade completely and now called `multi:getStats()`
- `multi:getError()` Removed when setting protect was removed - `multi:getError()` Removed when setting protect was removed
- `multi:FreeMainEvent()` The new changes with connections make's this function unnecessary - `multi:FreeMainEvent()` The new changes with connections make's this function unnecessary
- `multi:OnMainConnect(func)` See above - `multi:OnMainConnect(func)` See above

View File

@ -90,109 +90,22 @@ function multi:getProcessors()
return processes return processes
end end
function multi:getTasksDetails(t) function multi:getStats()
if not(t) then local stats = {
str = { [multi.Name] = {
{"Type <Identifier>","Uptime","Priority","TID"} threads = multi:getThreads(),
tasks = multi:getTasks()
} }
local count = 0 }
for i,v in pairs(self.Mainloop) do local procs = multi:getProcessors()
local name = v.Name or "" for i = 1, #procs do
if name~="" then local proc = procs[i]
name = " <"..name..">" stats[proc:getFullName()] = {
end threads = proc:getThreads(),
count = count + 1 tasks = proc:getTasks()
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.."<SystemThread: "..multi.SystemThreads[i].Name.." | "..os.clock()-multi.SystemThreads[i].creationTime..">\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
} }
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 end
return stats
end end
--Helpers --Helpers
@ -654,6 +567,7 @@ function multi:newEvent(task)
end end
c.OnEvent = self:newConnection() c.OnEvent = self:newConnection()
self:setPriority("core") self:setPriority("core")
c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
end end
@ -675,6 +589,7 @@ function multi:newUpdater(skip)
return self return self
end end
c.OnUpdate = self:newConnection() c.OnUpdate = self:newConnection()
c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
end end
@ -711,6 +626,7 @@ function multi:newAlarm(set)
self.Parent.Pause(self) self.Parent.Pause(self)
return self return self
end end
c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
end end
@ -738,6 +654,7 @@ function multi:newLoop(func,notime)
end end
multi:create(c) multi:create(c)
c:SetName(c.Type)
return c return c
end end
@ -795,6 +712,7 @@ function multi:newStep(start,reset,count,skip)
self:Resume() self:Resume()
return self return self
end end
c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
end end
@ -830,6 +748,7 @@ function multi:newTLoop(func,set)
if func then if func then
c.OnLoop(func) c.OnLoop(func)
end end
c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
end end
@ -879,6 +798,7 @@ function multi:newTStep(start,reset,count,set)
self:Resume() self:Resume()
return self return self
end end
c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
end end
@ -957,17 +877,19 @@ function multi:newProcessor(name,nothread)
handler() handler()
end end
end) end)
c.process.__ignore = true
c.process.isProcessThread = true c.process.isProcessThread = true
c.process.PID = sandcount c.process.PID = sandcount
c.OnError = c.process.OnError c.OnError = c.process.OnError
function c:getThreads() function c:getThreads()
return self.threads return c.threads
end end
function c:getFullName() function c:getFullName()
return self.parent:getFullName() .. "." .. self.Name return c.parent:getFullName() .. "." .. c.Name
end end
function c:getName() function c:getName()
@ -1071,7 +993,13 @@ function multi:getThreads()
end end
function multi:getTasks() 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 end
function thread.request(t,cmd,...) function thread.request(t,cmd,...)
@ -1213,6 +1141,8 @@ function thread.pushStatus(...)
t.statusconnector:Fire(...) t.statusconnector:Fire(...)
end end
local handler
function thread:newFunctionBase(generator,holdme) function thread:newFunctionBase(generator,holdme)
return function() return function()
local tfunc = {} local tfunc = {}
@ -1230,10 +1160,11 @@ function thread:newFunctionBase(generator,holdme)
return nil, "Function is paused" return nil, "Function is paused"
end end
local rets, err local rets, err
local function wait(no) local function wait()
if thread.isThread() and not (no) then if thread.isThread() then
return multi.hold(function() return thread.hold(function()
if err then if err then
print("ERROR",err)
return multi.NIL, err return multi.NIL, err
elseif rets then 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]) 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) end)
else else
while not rets and not err do while not rets and not err do
multi.scheduler:Act() handler()
end end
if err then if err then
return nil,err return nil,err
@ -1335,6 +1266,10 @@ function thread:newThread(name,func,...)
c.OnError = multi:newConnection(true,nil,true) c.OnError = multi:newConnection(true,nil,true)
c.OnDeath = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true)
function c:getName()
return c.Name
end
function c:isPaused() function c:isPaused()
return self._isPaused return self._isPaused
end end
@ -1560,7 +1495,7 @@ co_status = {
_=nil r1=nil r2=nil r3=nil r4=nil r5=nil _=nil r1=nil r2=nil r3=nil r4=nil r5=nil
end, end,
} }
local handler = coroutine.wrap(function(self) handler = coroutine.wrap(function(self)
local temp_start local temp_start
while true do while true do
for start = #startme, 1, -1 do for start = #startme, 1, -1 do

View File

@ -126,7 +126,7 @@ function multi.InitSystemThreadErrorHandler()
if status == "done" or temp.returns:get("returns") then if status == "done" or temp.returns:get("returns") then
livingThreads[temp.Id] = {false, temp.Name} livingThreads[temp.Id] = {false, temp.Name}
temp.alive = false 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 GLOBAL["__THREADS__"] = livingThreads
table.remove(threads, i) table.remove(threads, i)
elseif status == "running" then elseif status == "running" then

View File

@ -80,11 +80,11 @@ function multi:newSystemThread(name,func,...)
-- If the thread is not running let's handle that. -- If the thread is not running let's handle that.
local thread_err = c.thread:getError() local thread_err = c.thread:getError()
if thread_err == "Thread Killed!\1" then if thread_err == "Thread Killed!\1" then
c.OnDeath:Fire(c,"Thread Killed!") c.OnDeath:Fire("Thread Killed!")
elseif thread_err then elseif thread_err then
c.OnError:Fire(c,thread_err) c.OnError:Fire(c,thread_err)
elseif c.stab.returns then elseif c.stab.returns then
c.OnDeath:Fire(c,unpack(c.stab.returns)) c.OnDeath:Fire(unpack(c.stab.returns))
c.stab.returns = nil c.stab.returns = nil
end end
end) end)

View File

@ -2,18 +2,34 @@ package.path = "./?/init.lua;"..package.path
multi, thread = require("multi"):init{print=true} multi, thread = require("multi"):init{print=true}
GLOBAL, THREAD = require("multi.integration.threading"):init() 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) func = THREAD:newFunction(function(count)
print("Starting Status test: ",count) print("Starting Status test: ",count)
local a = 0 local a = 0
while true do while true do
a = a + 1 a = a + 1
THREAD.sleep(.1) THREAD.sleep(.1)
-- Push the status from the currently running threaded function to the main thread
THREAD.pushStatus(a,count) THREAD.pushStatus(a,count)
if a == count then break end if a == count then break end
end end
return "Done" return "Done"
end) 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 ret = func(10)
local ret2 = func(15) local ret2 = func(15)
local ret3 = func(20) local ret3 = func(20)
@ -40,32 +56,56 @@ ret3.OnStatus(function(part,whole)
print(s3) print(s3)
end) end)
-- local proc = multi:newProcessor("Test") loop = multi:newTLoop()
-- local proc2 = multi:newProcessor("Test2")
-- local proc3 = proc2:newProcessor("Test3")
-- function multi:getTaskStats() function loop:testing()
-- local stats = { print("testing haha")
-- [multi.Name] = { end
-- 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
-- local tasks = multi:getTaskStats() loop:Set(1)
t = loop:OnLoop(function()
print("Looping...")
end):testing()
-- for i,v in pairs(tasks) do local proc = multi:newProcessor("Test")
-- print("Process: "..i) local proc2 = multi:newProcessor("Test2")
-- end 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() multi:mainloop()