Fixed mem leaks and new features
This commit is contained in:
parent
564db18933
commit
0476164cf6
77
changes.md
77
changes.md
@ -5,17 +5,82 @@ Update 13.1.0 Bug fixes and some new features (Will upgrade version to 14.0.0 if
|
||||
Added:
|
||||
- Connections:Lock() -- Prevents a connection object form being fired
|
||||
- Connections:Unlock() -- Removes the restriction imposed by conn:Lock()
|
||||
-
|
||||
|
||||
- new fucntion added to the thread namespace
|
||||
-- thread.request(THREAD handle,STRING cmd,VARARGS args) -- allows you to push thread requests from outside the running thread! Extremely powerful.
|
||||
-- thread.exec(FUNCTION func) -- Allows you to push code to run within the thread execution block!
|
||||
- handle = multi:newThread() now returns a thread handle to interact with the object
|
||||
-- handle:Pause()
|
||||
-- handle:Resume()
|
||||
-- handle:Kill()
|
||||
- Basic Thread error checking.
|
||||
When creating a coroutine based thread with multi:newThread(), the library will attempt to scan through the butecode and check for while loops and the use of thread.* This is only done at thread creation. It isn't perfect, but the goal is to print a warning message that the thread may hang the multi library. This also has some flaws, if a thread function calls another function that has code that may hang the program, this quick check does not see that! Perhaps this is something I can tackle in the future.
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi = require("multi")
|
||||
a=0
|
||||
multi:newThread("Test",function()
|
||||
while true do
|
||||
a=a+1
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
-- Output: Warning! The thread created: <Test> contains a while loop which may not be confugured properly with thread.* If your code seems to hang this may be the reason!
|
||||
```
|
||||
Fixed:
|
||||
- Minor bug with multi:newThread() in how names and functions were managed
|
||||
- Major bug with the system thread handler. Saw healthy threads as dead ones
|
||||
-
|
||||
- Major bug with the system thread handler. Saw healthy threads as dead ones
|
||||
- Major bug the thread scheduler was seen creating a massive amount of 'event' causing memory leaks and hard crashes! This has been fixed by changing how the scheduler opperates.
|
||||
|
||||
Changed:
|
||||
- getTasksDetails("t"), the table varaiant, formats threads, and system threads in the same way that tasks are formatted
|
||||
-
|
||||
- getTasksDetails("t"), the table varaiant, formats threads, and system threads in the same way that tasks are formatted. Please see below for the format of the task details
|
||||
- TID has been added to multi objects. They count up from 0 and no 2 objects will have the same number
|
||||
- thread.hold() -- As part of the memory leaks that I had to fix thread.hold() is slightly different. This change shouldn't impact previous code at all, but thread.hold() can not only return at most 7 arguments!
|
||||
- You should notice some faster code execution from threads, the changes improve preformance of threads greatly. They are now much faster than before!
|
||||
|
||||
# Tasks Details Table format
|
||||
```
|
||||
{
|
||||
["Tasks"] = {
|
||||
{
|
||||
["TID"] = 0,
|
||||
["Type"] = scheduler,
|
||||
["Name"] = multi.thread,
|
||||
["Priority"] = Core,
|
||||
["Uptime"] = 6.752
|
||||
["Link"] = tableRef
|
||||
},
|
||||
...
|
||||
} ,
|
||||
|
||||
["Systemthreads"] = {
|
||||
{
|
||||
["Uptime"] = 6.752
|
||||
["Link"] = tableRef
|
||||
["Name"] = threadname
|
||||
["ThreadID"] = 0
|
||||
},
|
||||
...
|
||||
},
|
||||
|
||||
["Threads"] = {
|
||||
{
|
||||
["Uptime"] = 6.752
|
||||
["Link"] = tableRef
|
||||
["Name"] = threadname
|
||||
["ThreadID"] = 0
|
||||
},
|
||||
...
|
||||
},
|
||||
|
||||
["ProcessName"] = multi.root,
|
||||
["CyclesPerSecondPerTask"] = 3560300,
|
||||
["MemoryUsage"] = 1846, in KB returned as a number
|
||||
["ThreadCount"] = 1,
|
||||
["SystemLoad"] = 0, as a % 100 is max 0 is min
|
||||
["PriorityScheme"] = Round-Robin
|
||||
["SystemThreadCount"] = 1
|
||||
}
|
||||
```
|
||||
Update 13.0.0 Added some documentation, and some new features too check it out!
|
||||
-------------
|
||||
**Quick note** on the 13.0.0 update:
|
||||
|
||||
145
multi/init.lua
145
multi/init.lua
@ -342,7 +342,17 @@ function multi:getTasksDetails(t)
|
||||
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],i})
|
||||
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(multi.PausedObjects) do
|
||||
if v.Type~="event" then
|
||||
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
|
||||
end
|
||||
if count == 0 then
|
||||
table.insert(str,{"Currently no processes running!","","",""})
|
||||
@ -369,22 +379,31 @@ function multi:getTasksDetails(t)
|
||||
str = {
|
||||
ProcessName = (self.Name or "Unnamed"),
|
||||
ThreadCount = #multi.scheduler.Threads,
|
||||
MemoryUsage = math.ceil(collectgarbage("count")).." KB",
|
||||
MemoryUsage = math.ceil(collectgarbage("count")),
|
||||
PriorityScheme = priorityTable[multi.defaultSettings.priority or 0],
|
||||
SystemLoad = multi.Round(load,2),
|
||||
CyclesPerSecondPerTask = steps,
|
||||
SystemThreadCount = #multi.SystemThreads
|
||||
SystemThreadCount = multi.SystemThreads and #multi.SystemThreads or 0
|
||||
}
|
||||
str.Tasks = {}
|
||||
str.PausedTasks = {}
|
||||
str.Threads = {}
|
||||
str.Systemthreads = {}
|
||||
for i,v in pairs(self.Mainloop) do
|
||||
str[#str+1]={Link = v, Type=v.Type,Name=v.Name,Uptime=os.clock()-v.creationTime,Priority=self.PriorityResolve[v.Priority],TID = i}
|
||||
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
|
||||
if v.Type~="event" then
|
||||
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
|
||||
end
|
||||
for i=1,#multi.scheduler.Threads do
|
||||
table.insert(str.Threads,{Uptime = os.clock()-multi.scheduler.Threads[i].creationTime,Name = multi.scheduler.Threads[i].Name})
|
||||
table.insert(str.Threads,{Uptime = os.clock()-multi.scheduler.Threads[i].creationTime,Name = multi.scheduler.Threads[i].Name,Link = multi.scheduler.Threads[i],TID = multi.scheduler.Threads[i].TID})
|
||||
end
|
||||
for i=1,#multi.SystemThreads do
|
||||
table.insert(str.Systemthreads,{Uptime = os.clock()-multi.SystemThreads[i].creationTime,Name = multi.SystemThreads[i].Name})
|
||||
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
|
||||
@ -527,6 +546,7 @@ function multi:OnTimerResolved(func)
|
||||
return self
|
||||
end
|
||||
-- Timer stuff done
|
||||
multi.PausedObjects = {}
|
||||
function multi:Pause()
|
||||
if self.Type=='mainprocess' then
|
||||
multi.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()")
|
||||
@ -535,6 +555,7 @@ function multi:Pause()
|
||||
local loop = self.Parent.Mainloop
|
||||
for i=1,#loop do
|
||||
if loop[i] == self then
|
||||
multi.PausedObjects[self] = true
|
||||
table.remove(loop,i)
|
||||
break
|
||||
end
|
||||
@ -552,6 +573,7 @@ function multi:Resume()
|
||||
else
|
||||
if self.Active==false then
|
||||
table.insert(self.Parent.Mainloop,self)
|
||||
multi.PausedObjects[self] = nil
|
||||
self.Active=true
|
||||
end
|
||||
end
|
||||
@ -569,6 +591,7 @@ function multi:Destroy()
|
||||
if self.Parent.Mainloop[i]==self then
|
||||
self.Parent.OnObjectDestroyed:Fire(self)
|
||||
table.remove(self.Parent.Mainloop,i)
|
||||
self.Destroyed = true
|
||||
break
|
||||
end
|
||||
end
|
||||
@ -593,6 +616,7 @@ function multi:setName(name)
|
||||
end
|
||||
multi.SetName = multi.setName
|
||||
--Constructors [CORE]
|
||||
local _tid = 0
|
||||
function multi:newBase(ins)
|
||||
if not(self.Type=='mainprocess' or self.Type=='process' or self.Type=='queue') then error('Can only create an object on multi or an interface obj') return false end
|
||||
local c = {}
|
||||
@ -606,6 +630,7 @@ function multi:newBase(ins)
|
||||
c.funcTM={}
|
||||
c.funcTMR={}
|
||||
c.ender={}
|
||||
c.TID = _tid
|
||||
c.important={}
|
||||
c.Act=function() end
|
||||
c.Parent=self
|
||||
@ -616,6 +641,7 @@ function multi:newBase(ins)
|
||||
else
|
||||
table.insert(self.Mainloop,c)
|
||||
end
|
||||
_tid = _tid + 1
|
||||
return c
|
||||
end
|
||||
function multi:newProcessor(file)
|
||||
@ -1444,19 +1470,38 @@ if os.getOS()=="windows" then
|
||||
else
|
||||
thread.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
thread.requests = {}
|
||||
function thread.request(t,cmd,...)
|
||||
thread.requests[t.thread] = {cmd,{...}}
|
||||
end
|
||||
function thread._Requests()
|
||||
local t = thread.requests[coroutine.running()]
|
||||
thread.requests[coroutine.running()] = nil
|
||||
if t then
|
||||
local cmd,args = t[1],t[2]
|
||||
thread[cmd](unpack(args))
|
||||
end
|
||||
end
|
||||
function thread.exec(func)
|
||||
func()
|
||||
end
|
||||
function thread.sleep(n)
|
||||
thread._Requests()
|
||||
coroutine.yield({"_sleep_",n or 0})
|
||||
end
|
||||
function thread.hold(n)
|
||||
thread._Requests()
|
||||
return coroutine.yield({"_hold_",n or function() return true end})
|
||||
end
|
||||
function thread.skip(n)
|
||||
thread._Requests()
|
||||
coroutine.yield({"_skip_",n or 0})
|
||||
end
|
||||
function thread.kill()
|
||||
coroutine.yield({"_kill_",":)"})
|
||||
end
|
||||
function thread.yeild()
|
||||
thread._Requests()
|
||||
coroutine.yield({"_sleep_",0})
|
||||
end
|
||||
function thread.isThread()
|
||||
@ -1504,20 +1549,52 @@ multi:setDomainName("Threads")
|
||||
multi:setDomainName("Globals")
|
||||
local initT = false
|
||||
local threadCount = 0
|
||||
local threadid = 0
|
||||
function multi:newThread(name,func)
|
||||
local func = func or name
|
||||
if type(name) == "function" then
|
||||
name = "Thread#"..threadCount
|
||||
end
|
||||
local g=string.dump(func)
|
||||
if g:find("K") and not g:find("thread") then
|
||||
print("Warning! The thread created: <"..name.."> contains a while loop which may not be confugured properly with thread.* If your code seems to hang this may be the reason!")
|
||||
else
|
||||
multi.print("Should be safe")
|
||||
end
|
||||
local c={}
|
||||
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.ref.Globals=self:linkDomain("Globals")
|
||||
c._isPaused = false
|
||||
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)
|
||||
self._isPaused = true
|
||||
end
|
||||
end
|
||||
function c:Resume()
|
||||
resumed = true
|
||||
end
|
||||
function c:Kill()
|
||||
thread.request(self,"kill")
|
||||
end
|
||||
c.Destroy = c.Kill
|
||||
function c.ref:send(name,val)
|
||||
ret=coroutine.yield({Name=name,Value=val})
|
||||
self:syncGlobals(ret)
|
||||
@ -1551,6 +1628,8 @@ function multi:newThread(name,func)
|
||||
multi.initThreads()
|
||||
end
|
||||
c.creationTime = os.clock()
|
||||
threadid = threadid + 1
|
||||
return c
|
||||
end
|
||||
function multi.initThreads()
|
||||
initT = true
|
||||
@ -1560,13 +1639,31 @@ function multi.initThreads()
|
||||
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")
|
||||
local holds = {}
|
||||
local skips = {}
|
||||
local t0,t1,t2,t3,t4,t5,t6
|
||||
local ret
|
||||
multi.scheduler:OnLoop(function(self)
|
||||
self.counter=self.counter+1
|
||||
for i=#skips,1,-1 do
|
||||
skips[i].pos = skips[i].pos + 1
|
||||
if skips[i].count==skips[i].pos then
|
||||
skips[i].Link.sleep=0
|
||||
skips[i]=nil
|
||||
end
|
||||
end
|
||||
for i=#holds,1,-1 do
|
||||
if holds[i] then
|
||||
t0,t1,t2,t3,t4,t5,t6 = holds[i].func()
|
||||
if t0 then
|
||||
holds[i].Link.sleep = 0
|
||||
holds[i].Link.returns = {t0,t1,t2,t3,t4,t5,t6}
|
||||
holds[i]=nil
|
||||
end
|
||||
end
|
||||
end
|
||||
for i=#self.Threads,1,-1 do
|
||||
ret={}
|
||||
if coroutine.status(self.Threads[i].thread)=="dead" then
|
||||
table.remove(self.Threads,i)
|
||||
else
|
||||
@ -1591,7 +1688,7 @@ function multi.initThreads()
|
||||
end
|
||||
if ret==true or ret==false then
|
||||
multi.print("Thread Ended!!!")
|
||||
ret={}
|
||||
ret=nil
|
||||
end
|
||||
end
|
||||
if ret then
|
||||
@ -1603,26 +1700,18 @@ function multi.initThreads()
|
||||
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
|
||||
evnt:Destroy()
|
||||
end):setName("multi.thread.skip")
|
||||
table.insert(skips,{
|
||||
Link = self.Threads[i],
|
||||
count = ret[2],
|
||||
pos = 0,
|
||||
})
|
||||
elseif ret[1]=="_hold_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=math.huge
|
||||
local event=multi:newEvent(ret[2])
|
||||
event.returns = nil
|
||||
event.link=self.Threads[i]
|
||||
event:OnEvent(function(evnt)
|
||||
evnt.link.sleep=0
|
||||
evnt.link.returns = evnt.returns
|
||||
multi.nextStep(function()
|
||||
evnt:Destroy()
|
||||
end)
|
||||
end):setName("multi.thread.hold")
|
||||
table.insert(holds,{
|
||||
Link = self.Threads[i],
|
||||
func = ret[2]
|
||||
})
|
||||
elseif ret.Name then
|
||||
self.Globals[ret.Name]=ret.Value
|
||||
end
|
||||
|
||||
133
test.lua
133
test.lua
@ -1,44 +1,99 @@
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi = require("multi")
|
||||
local GLOBAL,THREAD = require("multi.integration.lanesManager").init()
|
||||
nGLOBAL = require("multi.integration.networkManager").init()
|
||||
function table.print(tbl, indent)
|
||||
if type(tbl)~="table" then return end
|
||||
if not indent then indent = 0 end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(' ', indent) .. k .. ': '
|
||||
if type(v) == 'table' then
|
||||
print(formatting)
|
||||
table.print(v, indent+1)
|
||||
else
|
||||
print(formatting .. tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
print(#multi.SystemThreads)
|
||||
multi:newThread("Detail Updater",function()
|
||||
--~ local GLOBAL,THREAD = require("multi.integration.lanesManager").init()
|
||||
--~ nGLOBAL = require("multi.integration.networkManager").init()
|
||||
--~ function table.print(tbl, indent)
|
||||
--~ if type(tbl)~="table" then return end
|
||||
--~ if not indent then indent = 0 end
|
||||
--~ for k, v in pairs(tbl) do
|
||||
--~ formatting = string.rep(' ', indent) .. k .. ': '
|
||||
--~ if type(v) == 'table' then
|
||||
--~ print(formatting)
|
||||
--~ table.print(v, indent+1)
|
||||
--~ else
|
||||
--~ print(formatting .. tostring(v))
|
||||
--~ end
|
||||
--~ end
|
||||
--~ end
|
||||
--~ print(#multi.SystemThreads)
|
||||
--~ multi:newThread("Detail Updater",function()
|
||||
--~ while true do
|
||||
--~ thread.sleep(1)
|
||||
--~ print(multi:getTasksDetails())
|
||||
--~ print("-----")
|
||||
--~ table.print(multi:getTasksDetails("t"))
|
||||
--~ io.read()
|
||||
--~ end
|
||||
--~ end)
|
||||
--~ multi.OnSystemThreadDied(function(...)
|
||||
--~ print("why you say dead?",...)
|
||||
--~ end)
|
||||
--~ multi.OnError(function(...)
|
||||
--~ print(...)
|
||||
--~ end)
|
||||
--~ multi:newSystemThread("TestSystem",function()
|
||||
--~ while true do
|
||||
--~ THREAD.sleep(1)
|
||||
--~ print("I'm alive")
|
||||
--~ end
|
||||
--~ end)
|
||||
--~ print(#multi.SystemThreads)
|
||||
--~ multi:mainloop{
|
||||
--~ protect = false,
|
||||
--~ print = true
|
||||
--~ }
|
||||
--~ function tprint (tbl, indent)
|
||||
--~ if not indent then indent = 0 end
|
||||
--~ for k, v in pairs(tbl) do
|
||||
--~ formatting = string.rep(" ", indent) .. k .. ": "
|
||||
--~ if type(v) == "table" then
|
||||
--~ print(formatting)
|
||||
--~ tprint(v, indent+1)
|
||||
--~ elseif type(v) == 'boolean' then
|
||||
--~ print(formatting .. tostring(v))
|
||||
--~ else
|
||||
--~ print(formatting .. tostring(v))
|
||||
--~ end
|
||||
--~ end
|
||||
--~ end
|
||||
|
||||
--~ t = multi:newThread("test",function()
|
||||
--~ while true do
|
||||
--~ thread.sleep(.5)
|
||||
--~ print("A test!")
|
||||
--~ end
|
||||
--~ end)
|
||||
--~ multi:newAlarm(3):OnRing(function()
|
||||
--~ multi:newAlarm(3):OnRing(function()
|
||||
--~ t:Resume()
|
||||
--~ end)
|
||||
--~ t:Pause()
|
||||
--~ end)
|
||||
--~ multi.OnError(function(...)
|
||||
--~ print(...)
|
||||
--~ end)
|
||||
|
||||
--~ function test()
|
||||
--~ while true do
|
||||
--~ a=a+1
|
||||
--~ end
|
||||
--~ end
|
||||
--~ g=string.dump(test)
|
||||
--~ print(g)
|
||||
--~ if g:find("thread") then
|
||||
--~ print("Valid Thread!")
|
||||
--~ elseif (g:find("K")) and not g:find("thread") then
|
||||
--~ print("Invalid Thread!")
|
||||
--~ else
|
||||
--~ print("Should be safe")
|
||||
--~ end
|
||||
a=0
|
||||
multi:newTLoop(function()
|
||||
a=a+1
|
||||
end,1)
|
||||
multi:newThread("Test",function()
|
||||
while true do
|
||||
thread.sleep(1)
|
||||
print(multi:getTasksDetails())
|
||||
print("-----")
|
||||
table.print(multi:getTasksDetails("t"))
|
||||
io.read()
|
||||
--
|
||||
end
|
||||
end)
|
||||
multi.OnSystemThreadDied(function(...)
|
||||
print("why you say dead?",...)
|
||||
end)
|
||||
multi.OnError(function(...)
|
||||
print(...)
|
||||
end)
|
||||
multi:newSystemThread("TestSystem",function()
|
||||
while true do
|
||||
THREAD.sleep(1)
|
||||
print("I'm alive")
|
||||
end
|
||||
end)
|
||||
print(#multi.SystemThreads)
|
||||
multi:mainloop{
|
||||
protect = false,
|
||||
print = true
|
||||
}
|
||||
multi:mainloop()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user