bug fixes and features

This commit is contained in:
Ryan Ward 2019-02-03 22:43:52 -05:00
parent 28a4e37c51
commit 11cd2805c8
7 changed files with 99 additions and 24 deletions

View File

@ -316,6 +316,7 @@ All of these functions are found on actors
`self = multiObj:OnTimedOut(func)` -- If ResolveTimer was not called in time this event will be triggered. The function connected to it get a refrence of the original object that the timer was created on as the first argument. `self = multiObj:OnTimedOut(func)` -- If ResolveTimer was not called in time this event will be triggered. The function connected to it get a refrence of the original object that the timer was created on as the first argument.
`self = multiObj:OnTimerResolved(func)` -- This event is triggered when the timer gets resolved. Same argument as above is passed, but the variable arguments that are accepted in resolvetimer are also passed as well. `self = multiObj:OnTimerResolved(func)` -- This event is triggered when the timer gets resolved. Same argument as above is passed, but the variable arguments that are accepted in resolvetimer are also passed as well.
`self = multiObj:Reset(n)` -- In the cases where it isn't obvious what it does, it acts as Resume() `self = multiObj:Reset(n)` -- In the cases where it isn't obvious what it does, it acts as Resume()
`self = multiObj:SetName(STRING name)`
Actor: Events Actor: Events
------ ------
@ -822,6 +823,7 @@ ST - THREAD namespace
`THREAD.getName()` -- Returns the name of the working thread `THREAD.getName()` -- Returns the name of the working thread
`THREAD.sleep(NUMBER n)` -- Sleeps for an amount of time stopping the current thread `THREAD.sleep(NUMBER n)` -- Sleeps for an amount of time stopping the current thread
`THREAD.hold(FUNCTION func)` -- Holds the current thread until a condition is met `THREAD.hold(FUNCTION func)` -- Holds the current thread until a condition is met
`THREAD.getID()` -- returns a unique ID for the current thread. This varaiable is visible to the main thread as well by accessing it through the returned thread object. OBJ.Id
ST - GLOBAL namespace ST - GLOBAL namespace
--------------------- ---------------------
@ -835,6 +837,7 @@ ST - System Threads
------------------- -------------------
`systemThread = multi:newSystemThread(STRING thread_name,FUNCTION spawned_function,ARGUMENTS ...)` -- Spawns a thread with a certain name. `systemThread = multi:newSystemThread(STRING thread_name,FUNCTION spawned_function,ARGUMENTS ...)` -- Spawns a thread with a certain name.
`systemThread:kill()` -- kills a thread; can only be called in the main thread! `systemThread:kill()` -- kills a thread; can only be called in the main thread!
`systemThread.OnError(FUNCTION(systemthread,errMsg,errorMsgWithThreadName))`
System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below: System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below:

View File

@ -1,6 +1,6 @@
#Changes #Changes
[TOC] [TOC]
Update 13.0.0 So you documented it, finally! If I had a dollar for each time I found a bug working on 13.0.0 I'd be quite wealthy by now. How much lag could one expect when I've been coding with my own library wrong this entire time? Update 13.0.0 So you documented it, finally! If I had a dollar for each time I found a bug working on 13.0.0 I'd be quite wealthy by now.
------------- -------------
**Quick note** on the 13.0.0 update: **Quick note** on the 13.0.0 update:
This update I went all in finding bugs and improving proformance within the library. I added some new features and the new task manager, which I used as a way to debug the library was a great help, so much so thats it is now a permanent feature. It's been about half a year since my last update, but so much work needed to be done. I hope you can find a use in your code to use my library. I am extremely proud of my work; 7 years of development, I learned so much about lua and programming through the creation of this library. It was fun, but there will always be more to add and bugs crawling there way in. I can't wait to see where this library goes in the future! This update I went all in finding bugs and improving proformance within the library. I added some new features and the new task manager, which I used as a way to debug the library was a great help, so much so thats it is now a permanent feature. It's been about half a year since my last update, but so much work needed to be done. I hope you can find a use in your code to use my library. I am extremely proud of my work; 7 years of development, I learned so much about lua and programming through the creation of this library. It was fun, but there will always be more to add and bugs crawling there way in. I can't wait to see where this library goes in the future!
@ -10,6 +10,7 @@ Changed:
- A few things, to make concepts in the library more clear. - A few things, to make concepts in the library more clear.
- The way functions returned paused status. Before it would return "PAUSED" now it returns nil, true if paused - The way functions returned paused status. Before it would return "PAUSED" now it returns nil, true if paused
- Modified the connection object to allow for some more syntaxial suger! - Modified the connection object to allow for some more syntaxial suger!
- System threads now trigger an OnError connection that is a member of the object itself. multi.OnError() is no longer triggered for a system thread that crashes!
Connection Example: Connection Example:
```lua ```lua
@ -65,6 +66,7 @@ Added:
- multi:getTasksDetails(STRING format) -- Simple function, will get massive updates in the future, as of right now It will print out the current processes that are running; listing their type, uptime, and priority. More useful additions will be added in due time. Format can be either a string "s" or "t" see below for the table format - multi:getTasksDetails(STRING format) -- Simple function, will get massive updates in the future, as of right now It will print out the current processes that are running; listing their type, uptime, and priority. More useful additions will be added in due time. Format can be either a string "s" or "t" see below for the table format
- multi:endTask(TID) -- Use multi:getTasksDetails("t") to get the tid of a task - multi:endTask(TID) -- Use multi:getTasksDetails("t") to get the tid of a task
- multi:enableLoadDetection() -- Reworked how load detection works. It gives better values now, but it still needs some work before I am happy with it - multi:enableLoadDetection() -- Reworked how load detection works. It gives better values now, but it still needs some work before I am happy with it
- THREAD.getID() -- returns a unique ID for the current thread. This varaiable is visible to the main thread as well by accessing it through the returned thread object. OBJ.Id Do not confuse this with thread.* this refers to the system threading interface
```lua ```lua
package.path="?/init.lua;?.lua;"..package.path package.path="?/init.lua;?.lua;"..package.path
@ -109,7 +111,6 @@ Table format for getTasksDetails(STRING format)
**Going forward:** **Going forward:**
- Add something - Add something
Update 12.2.2 Time for some more bug fixes! Update 12.2.2 Time for some more bug fixes!
------------- -------------
Fixed: multi.Stop() not actually stopping due to the new pirority management scheme and preformance boost changes. Fixed: multi.Stop() not actually stopping due to the new pirority management scheme and preformance boost changes.

View File

@ -28,6 +28,7 @@ multi.Version = "13.0.0"
multi._VERSION = "13.0.0" multi._VERSION = "13.0.0"
multi.stage = "stable" multi.stage = "stable"
multi.__index = multi multi.__index = multi
multi.Name = "multi.Root"
multi.Mainloop = {} multi.Mainloop = {}
multi.Garbage = {} multi.Garbage = {}
multi.ender = {} multi.ender = {}
@ -159,7 +160,7 @@ function multi:getLoad()
return bench return bench
end) end)
bench = bench^1.5 bench = bench^1.5
val = math.ceil((1-(bench/(multi.maxSpd/1.5)))*100) val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
else else
busy = true busy = true
local bench local bench
@ -170,7 +171,7 @@ function multi:getLoad()
multi:uManager() multi:uManager()
end end
bench = bench^1.5 bench = bench^1.5
val = math.ceil((1-(bench/(multi.maxSpd/1.5)))*100) val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
busy = false busy = false
end end
if val<0 then val = 0 end if val<0 then val = 0 end
@ -330,11 +331,11 @@ function multi.AlignTable(tab)
return table.concat(str,"\n") return table.concat(str,"\n")
end end
local priorityTable = {[0]="Round-Robin",[1]="Just-Right",[2]="Top-heavy",[3]="Timed-Based-Balancer"} local priorityTable = {[0]="Round-Robin",[1]="Just-Right",[2]="Top-heavy",[3]="Timed-Based-Balancer"}
local ProcessName = {[true]="SubProcess",[false]="MainProcess"} local ProcessName = {[true]="SubProcessor",[false]="MainProcessor"}
function multi:getTasksDetails(t) function multi:getTasksDetails(t)
if t == "string" or not t then if t == "string" or not t then
str = { str = {
{"Type","Uptime","Priority","TID"} {"Type <Identifier","Uptime","Priority","TID"}
} }
local count = 0 local count = 0
for i,v in pairs(self.Mainloop) do for i,v in pairs(self.Mainloop) do
@ -350,13 +351,19 @@ function multi:getTasksDetails(t)
end end
local s = multi.AlignTable(str) local s = multi.AlignTable(str)
dat = "" 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
if multi.scheduler then if multi.scheduler then
for i=1,#multi.scheduler.Threads do for i=1,#multi.scheduler.Threads do
dat = dat .. "<THREAD: "..multi.scheduler.Threads[i].Name.." | "..os.clock()-multi.scheduler.Threads[i].creationTime..">\n" dat = dat .. "<THREAD: "..multi.scheduler.Threads[i].Name.." | "..os.clock()-multi.scheduler.Threads[i].creationTime..">\n"
end end
return "Load on "..ProcessName[self.Type=="process"].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(multi:getLoad(),2).."%\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: "..#multi.scheduler.Threads.."\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s.."\n\n"..dat return "Load on "..ProcessName[self.Type=="process"].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(multi:getLoad(),2).."%\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: "..#multi.scheduler.Threads.."\nSystemThreads Running: "..#(multi.SystemThreads or {}).."\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s.."\n\n"..dat..dat2
else else
return "Load on "..({[true]="SubProcess<"..(self.Name or "Unnamed")..">",[false]="MainProcess"})[self.Type=="process"]..": "..multi.Round(multi:getLoad(),2).."%\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: 0\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s return "Load on "..ProcessName[self.Type=="process"].."<"..(self.Name or "Unnamed")..">"..": "..multi.Round(multi:getLoad(),2).."%\nMemory Usage: "..math.ceil(collectgarbage("count")).." KB\nThreads Running: 0\nPriority Scheme: "..priorityTable[multi.defaultSettings.priority or 0].."\n\n"..s..dat2
end end
elseif t == "t" or t == "table" then elseif t == "t" or t == "table" then
str = { str = {
@ -366,12 +373,18 @@ function multi:getTasksDetails(t)
SystemLoad = multi.Round(multi:getLoad(),2) SystemLoad = multi.Round(multi:getLoad(),2)
} }
str.threads = {} str.threads = {}
str.systemthreads = {}
for i,v in pairs(self.Mainloop) do for i,v in pairs(self.Mainloop) do
str[#str+1]={Type=v.Type,Name=v.Name,Uptime=os.clock()-v.creationTime,Priority=self.PriorityResolve[v.Priority],TID = i} str[#str+1]={Type=v.Type,Name=v.Name,Uptime=os.clock()-v.creationTime,Priority=self.PriorityResolve[v.Priority],TID = i}
end end
for i=1,#multi.scheduler.Threads do for i=1,#multi.scheduler.Threads do
str.threads[multi.scheduler.Threads[i].Name]={Uptime = os.clock()-multi.scheduler.Threads[i].creationTime} str.threads[multi.scheduler.Threads[i].Name]={Uptime = os.clock()-multi.scheduler.Threads[i].creationTime}
end end
if multi.canSystemThread then
for i=1,#multi.SystemThreads do
str.systemthreads[multi.SystemThreads[i].Name]={Uptime = os.clock()-multi.SystemThreads[i].creationTime}
end
end
return str return str
end end
end end
@ -1576,7 +1589,7 @@ function multi.initThreads()
event:OnEvent(function(evnt) event:OnEvent(function(evnt)
evnt.link.sleep=0 evnt.link.sleep=0
evnt:Destroy() evnt:Destroy()
end) end):setName("multi.thread.skip")
elseif ret[1]=="_hold_" then elseif ret[1]=="_hold_" then
self.Threads[i].timer:Reset() self.Threads[i].timer:Reset()
self.Threads[i].sleep=math.huge self.Threads[i].sleep=math.huge
@ -1589,7 +1602,7 @@ function multi.initThreads()
multi.nextStep(function() multi.nextStep(function()
evnt:Destroy() evnt:Destroy()
end) end)
end) end):setName("multi.thread.hold")
elseif ret.Name then elseif ret.Name then
self.Globals[ret.Name]=ret.Value self.Globals[ret.Name]=ret.Value
end end

View File

@ -32,6 +32,8 @@ end
-- Step 1 get lanes -- Step 1 get lanes
lanes=require("lanes").configure() lanes=require("lanes").configure()
local multi = require("multi") -- get it all and have it on all lanes local multi = require("multi") -- get it all and have it on all lanes
multi.SystemThreads = {}
local thread = thread
multi.isMainThread=true multi.isMainThread=true
function multi:canSystemThread() function multi:canSystemThread()
return true return true
@ -93,6 +95,9 @@ end
function THREAD.getName() function THREAD.getName()
return THREAD_NAME return THREAD_NAME
end end
function THREAD.getID()
return THREAD_ID
end
--[[ Step 4 We need to get sleeping working to handle timing... We want idle wait, not busy wait --[[ Step 4 We need to get sleeping working 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 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 we use that and wrap it in a nice package]] however, a linda recieve will in fact be a idle wait! So we use that and wrap it in a nice package]]
@ -109,17 +114,32 @@ function THREAD.hold(n)
end end
local rand = math.random(1,10000000) local rand = math.random(1,10000000)
-- Step 5 Basic Threads! -- Step 5 Basic Threads!
-- local threads = {}
local count = 0
local started
function multi:newSystemThread(name,func,...) function multi:newSystemThread(name,func,...)
multi.InitSystemThreadErrorHandler()
rand = math.random(1,10000000) rand = math.random(1,10000000)
local c={} local c={}
local __self=c local __self=c
c.name=name c.name=name
c.Name = name
c.Id = count
local THREAD_ID = count
count = count + 1
c.Type="sthread" c.Type="sthread"
c.creationTime = os.clock()
local THREAD_NAME=name local THREAD_NAME=name
local function func2(...) local function func2(...)
local multi = require("multi")
_G["THREAD_NAME"]=THREAD_NAME _G["THREAD_NAME"]=THREAD_NAME
_G["THREAD_ID"]=THREAD_ID
math.randomseed(rand) math.randomseed(rand)
func(...) func(...)
if _G.__Needs_Multi then
multi:mainloop()
end
THREAD.kill()
end end
c.thread=lanes.gen("*", func2)(...) c.thread=lanes.gen("*", func2)(...)
function c:kill() function c:kill()
@ -127,16 +147,32 @@ function multi:newSystemThread(name,func,...)
self.thread:cancel() self.thread:cancel()
print("Thread: '"..self.name.."' has been stopped!") print("Thread: '"..self.name.."' has been stopped!")
end end
c.status=multi:newUpdater(multi.Priority_IDLE) table.insert(multi.SystemThreads,c)
c.status.link=c c.OnError = multi:newConnection()
c.status:OnUpdate(function(self) return c
local v,err,t=self.link.thread:join(.001) end
if err then function multi.InitSystemThreadErrorHandler()
multi.OnError:Fire(self.link,err,"Error in systemThread: '"..self.link.name.."' <"..err..">") if started then return end
self:Destroy() multi:newThread("ThreadErrorHandler",function()
local deadThreads = {}
local threads = multi.SystemThreads
while true do
thread.sleep(.5) -- switching states often takes a huge hit on performance. half a second to tell me there is an error is good enough.
for i=#threads,1,-1 do
local v,err,t=threads[i].thread:join(.001)
if err then
if err:find("Thread was killed!") then
table.remove(threads,i)
else
threads[i].OnError:Fire(threads[i],err,"Error in systemThread: '"..threads[i].name.."' <"..err..">")
table.remove(threads,i)
table.insert(deadThreads,threads[i].Id)
GLOBAL["__DEAD_THREADS__"]=deadThreads
end
end
end
end end
end) end)
return c
end end
print("Integrated Lanes!") print("Integrated Lanes!")
multi.integration={} -- for module creators multi.integration={} -- for module creators

View File

@ -34,6 +34,7 @@ multi.integration.love2d.ThreadBase=[[
tab={...} tab={...}
__THREADID__=table.remove(tab,1) __THREADID__=table.remove(tab,1)
__THREADNAME__=table.remove(tab,1) __THREADNAME__=table.remove(tab,1)
THREAD_ID=table.remove(tab,1)
require("love.filesystem") require("love.filesystem")
require("love.system") require("love.system")
require("love.timer") require("love.timer")
@ -217,6 +218,9 @@ isMainThread=true
function THREAD.getName() function THREAD.getName()
return __THREADNAME__ return __THREADNAME__
end end
function THREAD.getID()
return THREAD_ID
end
function ToStr(val, name, skipnewlines, depth) function ToStr(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false skipnewlines = skipnewlines or false
depth = depth or 0 depth = depth or 0
@ -295,12 +299,16 @@ local function randomString(n)
end end
return str return str
end end
local count = 0
function multi:newSystemThread(name,func,...) -- the main method function multi:newSystemThread(name,func,...) -- the main method
local c={} local c={}
c.name=name c.name=name
c.Name = name
c.ID=c.name.."<ID|"..randomString(8)..">" c.ID=c.name.."<ID|"..randomString(8)..">"
c.Id=count
count = count + 1
c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func))) c.thread=love.thread.newThread(multi.integration.love2d.ThreadBase:gsub("INSERT_USER_CODE",dump(func)))
c.thread:start(c.ID,c.name,...) c.thread:start(c.ID,c.name,,...)
function c:kill() function c:kill()
multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__" multi.integration.GLOBAL["__DIEPLZ"..self.ID.."__"]="__DIEPLZ"..self.ID.."__"
end end

View File

@ -123,6 +123,7 @@ function multi:newSystemThreadedConnection(name,protect)
local qsm = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNCM"):init() local qsm = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNCM"):init()
local qs = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNC"):init() local qs = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNC"):init()
function c:init() function c:init()
_G.__Needs_Multi = true
local multi = require("multi") local multi = require("multi")
if multi:getPlatform()=="love2d" then if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL GLOBAL=_G.GLOBAL
@ -241,6 +242,7 @@ function multi:newSystemThreadedConsole(name)
local sThread=multi.integration.THREAD local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL local GLOBAL=multi.integration.GLOBAL
function c:init() function c:init()
_G.__Needs_Multi = true
local multi = require("multi") local multi = require("multi")
if multi:getPlatform()=="love2d" then if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL GLOBAL=_G.GLOBAL
@ -288,6 +290,7 @@ function multi:newSystemThreadedTable(name)
local sThread=multi.integration.THREAD local sThread=multi.integration.THREAD
local GLOBAL=multi.integration.GLOBAL local GLOBAL=multi.integration.GLOBAL
function c:init() -- create an init function so we can mimic on both love2d and lanes function c:init() -- create an init function so we can mimic on both love2d and lanes
_G.__Needs_Multi = true
local multi = require("multi") local multi = require("multi")
if multi:getPlatform()=="love2d" then if multi:getPlatform()=="love2d" then
GLOBAL=_G.GLOBAL GLOBAL=_G.GLOBAL

View File

@ -5,12 +5,12 @@ nGLOBAL = require("multi.integration.networkManager").init()
local a local a
local clock = os.clock local clock = os.clock
function sleep(n) -- seconds function sleep(n) -- seconds
local t0 = clock() local t0 = clock()
while clock() - t0 <= n do end while clock() - t0 <= n do end
end end
master = multi:newMaster{ master = multi:newMaster{
name = "Main", -- the name of the master name = "Main", -- the name of the master
--noBroadCast = true, -- if using the node manager, set this to true to avoid double connections --~ --noBroadCast = true, -- if using the node manager, set this to true to avoid double connections
managerDetails = {"192.168.1.4",12345}, -- the details to connect to the node manager (ip,port) managerDetails = {"192.168.1.4",12345}, -- the details to connect to the node manager (ip,port)
} }
master.OnError(function(name,err) master.OnError(function(name,err)
@ -28,9 +28,20 @@ end)
master.OnNodeConnected(function(name) master.OnNodeConnected(function(name)
table.insert(connlist,name) table.insert(connlist,name)
end) end)
multi.OnError(function(...) --~ multi:newThread("TaskView",function()
print(...) --~ while true do
--~ thread.sleep(1)
--~ print(multi:getTasksDetails())
--~ end
--~ end)
multi:newSystemThread("SystemThread",function()
local multi = require("multi")
print(THREAD.getName(),THREAD.getID())
THREAD.sleep(8)
end).OnError(function(a,b,c)
print("ERROR:",b)
end) end)
--~ print(multi:getTasksDetails())
multi:mainloop{ multi:mainloop{
protect = false protect = false
} }