From 4877f64ca127c8ff286b9a2f0a075bb82c6c1f55 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sun, 16 Jan 2022 16:18:17 -0500 Subject: [PATCH] Fix some bugs, added connection/thread tests --- changes.md | 8 +- multi/init.lua | 178 ++++++++++++++++++++------------------- tests/connectionTest.lua | 71 ++++++++++++++++ tests/objectTests.lua | 2 +- tests/runtests.lua | 20 ++--- 5 files changed, 176 insertions(+), 103 deletions(-) create mode 100644 tests/connectionTest.lua diff --git a/changes.md b/changes.md index fd7c302..c8d9d83 100644 --- a/changes.md +++ b/changes.md @@ -66,7 +66,7 @@ Changed: - Connection Objects no longer Fire with syntax sugar when attached to an object: - `multiobj:OnSomeEvent(arg1,arg2.arg3)` No longer triggers the Fire event. As part of the update to make all objects use connections internally this little used feature had to be scrapped! + `multiobj:OnSomeEvent(...)` No longer triggers the Fire event. As part of the update to make all objects use connections internally this little used feature had to be scrapped! - multi:newTStep now derives it's functionality from multi:newStep (Cut's down on code length a bit) @@ -76,14 +76,16 @@ Removed: --- - `multi:newFunction(func)` - `thread:newFunction(func)` Has many more features and replaces what multi:newFunction did +- `multi.holdFor()` Now that multi.hold takes the option table that thread.hold has this feature can be emulated using that. - Calling Fire on a connection no longer returns anything! Now that internal features use connections, I noticed how slow connections are and have increased their speed quite a bit. From 50,000 Steps per seconds to almost 7 Million. All other features should work just fine. Only returning values has been removed Fixed: --- - [Issue](https://github.com/rayaman/multi/issues/30) with Lanes crashing the lua state. Issue seems to be related to my filesystem -- [Issue](https://github.com/rayaman/multi/issues/29) where System threaded functions not up to date with threaded functions -- Issue where gettasksdetails would try to process a destroyed object causing it to crash +- [Issue](https://github.com/rayaman/multi/issues/29) where System threaded functions not being up to date with threaded functions +- Issue where gettasksdetails() would try to process a destroyed object causing it to crash +- Issue with multi.hold() not pumping the mainloop and only the scheduler ToDo: diff --git a/multi/init.lua b/multi/init.lua index 5a099bc..0348e22 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -137,7 +137,7 @@ function multi:getTasksDetails(t) {"Thread Name","Uptime","TID","Attached To"} } local proc_tab = { - {"Process Name", "Uptime", "PID", "Load", "Cycles per Second per task"} + {"Process Name", "Uptime", "PID", "Load", "CpS"} } for th,process in pairs(globalThreads) do if tostring(th.isProcessThread) == "destroyed" then @@ -372,7 +372,7 @@ function multi:newConnection(protect,func,kill) end end function temp:Destroy() - for i=1,#call_funcs do + for i=#call_funcs,1,-1 do if call_funcs[i]~=nil then if call_funcs[i]==self.func then table.remove(call_funcs,i) @@ -742,8 +742,6 @@ function multi:newStep(start,reset,count,skip) c.skip=skip or 0 c.spos=0 c.count=count or 1*think - c.funcE={} - c.funcS={} c.start=start or 1 if start~=nil and reset~=nil then if start>reset then @@ -905,9 +903,13 @@ function multi:scheduleJob(time,func) end local __CurrentProcess = multi +local __CurrentTask function multi.getCurrentProcess() return __CurrentProcess end +function multi.getCurrentTask() + return __CurrentTask +end local sandcount = 1 function multi:newProcessor(name) @@ -1087,14 +1089,17 @@ function multi.hold(func,opt) return thread.sleep(func) end local death = false + local proc = multi.getCurrentTask() + proc:Pause() if type(func)=="number" then self:newThread("Hold_func",function() - thread.sleep(func) + thread.hold(func) death = true end) while not death do - multi.scheduler:Act() + multi:uManager() end + proc:Resume() else local rets self:newThread("Hold_func",function() @@ -1102,25 +1107,13 @@ function multi.hold(func,opt) death = true end) while not death do - multi.scheduler:Act() + multi:uManager() end + proc:Resume() return unpack(rets) end end -function multi.holdFor(n,func) - local temp - multi.getCurrentProcess():newThread(function() - thread.sleep(n) - temp = true - end) - return multi.getCurrentProcess().hold(function() - if func() then - return func() - elseif temp then - return multi.NIL, multi.TIMEOUT - end - end) -end + local function cleanReturns(...) local returns = {...} local rets = {} @@ -1133,10 +1126,12 @@ local function cleanReturns(...) end return unpack(returns,1,ind) end + function thread.pushStatus(...) local t = thread.getRunningThread() t.statusconnector:Fire(...) end + function thread:newFunctionBase(generator,holdme) return function() local tfunc = {} @@ -1635,12 +1630,14 @@ function multi:lightloop(settings) multi.OnPreLoad:Fire() if not isRunning then local Loop=self.Mainloop + local ctask while true do for _D=#Loop,1,-1 do - if Loop[_D].Active then - self.CID=_D + __CurrentTask = Loop[_D] + ctask = __CurrentTask + if ctask.Active then if not protect then - Loop[_D]:Act() + ctask:Act() end end end @@ -1692,26 +1689,29 @@ function multi:mainloop(settings) local autoP = 0 local solid,sRef local cc=0 + local ctask multi.OnLoad:Fire() while mainloopActive do if priority == 1 then for _D=#Loop,1,-1 do + __CurrentTask = Loop[_D] + ctask = __CurrentTask for P=1,7 do - if Loop[_D] then - if (PS.PList[P])%Loop[_D].Priority==0 then - if Loop[_D].Active then - self.CID=_D + if ctask then + if (PS.PList[P])%ctask.Priority == 0 then + if ctask.Active then + self.CID = _D if not protect then - Loop[_D]:Act() + ctask:Act() __CurrentProcess = self else - local status, err=pcall(Loop[_D].Act,Loop[_D]) + local status, err = pcall(ctask.Act,ctask) __CurrentProcess = self if err then - Loop[_D].error=err - self.OnError:Fire(Loop[_D],err) + ctask.error=err + self.OnError:Fire(ctask,err) if stopOnError then - Loop[_D]:Destroy() + ctask:Destroy() end end end @@ -1722,21 +1722,22 @@ function multi:mainloop(settings) end elseif priority == 2 then for _D=#Loop,1,-1 do - if Loop[_D] then - if (PStep)%Loop[_D].Priority==0 then - if Loop[_D].Active then - self.CID=_D + __CurrentTask = Loop[_D] + ctask = __CurrentTask + if ctask then + if (PStep)%ctask.Priority==0 then + if ctask.Active then if not protect then - Loop[_D]:Act() + ctask:Act() __CurrentProcess = self else - local status, err=pcall(Loop[_D].Act,Loop[_D]) + local status, err=pcall(ctask.Act,ctask) __CurrentProcess = self if err then - Loop[_D].error=err - self.OnError:Fire(Loop[_D],err) + ctask.error=err + self.OnError:Fire(ctask,err) if stopOnError then - Loop[_D]:Destroy() + ctask:Destroy() end end end @@ -1756,21 +1757,22 @@ function multi:mainloop(settings) cc=0 end for _D=#Loop,1,-1 do - if Loop[_D] then - if Loop[_D].Priority == p_c or (Loop[_D].Priority == p_h and tt<.5) or (Loop[_D].Priority == p_an and tt<.125) or (Loop[_D].Priority == p_n and tt<.063) or (Loop[_D].Priority == p_bn and tt<.016) or (Loop[_D].Priority == p_l and tt<.003) or (Loop[_D].Priority == p_i and tt<.001) then - if Loop[_D].Active then - self.CID=_D + __CurrentTask = Loop[_D] + ctask = __CurrentTask + if ctask then + if ctask.Priority == p_c or (ctask.Priority == p_h and tt<.5) or (ctask.Priority == p_an and tt<.125) or (ctask.Priority == p_n and tt<.063) or (ctask.Priority == p_bn and tt<.016) or (ctask.Priority == p_l and tt<.003) or (ctask.Priority == p_i and tt<.001) then + if ctask.Active then if not protect then - Loop[_D]:Act() + ctask:Act() __CurrentProcess = self else - local status, err=pcall(Loop[_D].Act,Loop[_D]) + local status, err=pcall(ctask.Act,ctask) __CurrentProcess = self if err then - Loop[_D].error=err - self.OnError:Fire(Loop[_D],err) + ctask.error=err + self.OnError:Fire(ctask,err) if stopOnError then - Loop[_D]:Destroy() + ctask:Destroy() end end end @@ -1780,51 +1782,51 @@ function multi:mainloop(settings) end elseif priority == -1 then for _D=#Loop,1,-1 do - sRef = Loop[_D] - if Loop[_D] then - if (sRef.Priority == p_c) or PStep==0 then - if sRef.Active then - self.CID=_D + __CurrentTask = Loop[_D] + ctask = __CurrentTask + if ctask then + if (ctask.Priority == p_c) or PStep==0 then + if ctask.Active then if not protect then - if sRef.solid then - sRef:Act() + if ctask.solid then + ctask:Act() __CurrentProcess = self solid = true else - time = multi.timer(sRef.Act,sRef) - sRef.solid = true + time = multi.timer(ctask.Act,ctask) + ctask.solid = true solid = false end - if Loop[_D] and not solid then + if ctask and not solid then if time == 0 then - Loop[_D].Priority = p_c + ctask.Priority = p_c else - Loop[_D].Priority = P_LB + ctask.Priority = P_LB end end else - if Loop[_D].solid then - Loop[_D]:Act() + if ctask.solid then + ctask:Act() __CurrentProcess = self solid = true else - time, status, err=multi.timer(pcall,Loop[_D].Act,Loop[_D]) + time, status, err=multi.timer(pcall,ctask.Act,ctask) __CurrentProcess = self - Loop[_D].solid = true + ctask.solid = true solid = false end - if Loop[_D] and not solid then + if ctask and not solid then if time == 0 then - Loop[_D].Priority = p_c + ctask.Priority = p_c else - Loop[_D].Priority = P_LB + ctask.Priority = P_LB end end if err then - Loop[_D].error=err - self.OnError:Fire(Loop[_D],err) + ctask.error=err + self.OnError:Fire(ctask,err) if stopOnError then - Loop[_D]:Destroy() + ctask:Destroy() end end end @@ -1844,20 +1846,21 @@ function multi:mainloop(settings) end else for _D=#Loop,1,-1 do - if Loop[_D] then - if Loop[_D].Active then - self.CID=_D + __CurrentTask = Loop[_D] + ctask = __CurrentTask + if ctask then + if ctask.Active then if not protect then - Loop[_D]:Act() + ctask:Act() __CurrentProcess = self else - local status, err=pcall(Loop[_D].Act,Loop[_D]) + local status, err=pcall(ctask.Act,ctask) __CurrentProcess = self if err then - Loop[_D].error=err - self.OnError:Fire(Loop[_D],err) + ctask.error=err + self.OnError:Fire(ctask,err) if stopOnError then - Loop[_D]:Destroy() + ctask:Destroy() end end end @@ -2136,25 +2139,24 @@ function multi:enableLoadDetection() multi.maxSpd = stop end -local busy = false local lastVal = 0 local last_step = 0 -local bb = 0 function multi:getLoad() - if not multi.maxSpd then self:enableLoadDetection() end - if busy then return lastVal,last_step end + if not multi.maxSpd then multi:enableLoadDetection() end local val = nil local bench - self:benchMark(.01):OnBench(function(time,steps) + local bb + self:benchMark(.01).OnBench(function(time,steps) bench = steps bb = steps end) _,timeout = multi.hold(function() return bench - end,{sleep=.011}) + end,{sleep=.012}) if timeout then - bench = 150000 + bench = 0 + bb = 0 end bench = bench^1.5 val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100) diff --git a/tests/connectionTest.lua b/tests/connectionTest.lua new file mode 100644 index 0000000..98221b4 --- /dev/null +++ b/tests/connectionTest.lua @@ -0,0 +1,71 @@ +function connectionThreadTests(multi,thread) + print("Starting Connection and Thread tests!") + func = thread:newFunction(function(count) + local a = 0 + while true do + a = a + 1 + thread.sleep(.1) + thread.pushStatus(a,count) + if a == count then break end + end + return "Done" + end) + local ret = func(10) + local ret2 = func(15) + local ret3 = func(20) + local s1,s2,s3 = 0,0,0 + ret.OnStatus(function(part,whole) + s1 = math.ceil((part/whole)*1000)/10 + end) + ret2.OnStatus(function(part,whole) + s2 = math.ceil((part/whole)*1000)/10 + end) + ret3.OnStatus(function(part,whole) + s3 = math.ceil((part/whole)*1000)/10 + end) + local err, timeout = thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn,{sleep=3}) + if s1 == 100 and s2 == 100 and s3 == 100 then + print("Threads: Ok") + else + print("Threads on status error") + end + if timeout then + print("Threads or Connection error!") + else + print("Connection Test 1: Ok") + end + conn1 = multi:newConnection() + conn2 = multi:newConnection() + conn3 = multi:newConnection() + local c1,c2,c3,c4 = false,false,false,false + local a = conn1(function() + c1 = true + end) + local b = conn2(function() + c2 = true + end) + local c = conn3(function() + c3 = true + end) + local d = conn3(function() + c4 = true + end) + conn1:Fire() + conn2:Fire() + conn3:Fire() + if c1 and c2 and c3 and c4 then + print("Connection Test 2: Ok") + else + print("Connection Test 2: Error") + end + c3 = false + c4 = false + d:Destroy() + conn3:Fire() + if c3 and not(c4) then + print("Connection Test 3: Ok") + else + print("Connection Test 3: Error removing connection") + end +end +return connectionThreadTests \ No newline at end of file diff --git a/tests/objectTests.lua b/tests/objectTests.lua index afb090b..c72624d 100644 --- a/tests/objectTests.lua +++ b/tests/objectTests.lua @@ -37,6 +37,6 @@ function objectTests(multi,thread) if tloops > 10 then print("TLoops: Ok") else print("TLoops: Bad!") end if updaters > 100 then print("Updaters: Ok") else print("Updaters: Bad!") end end) - return event + thread.hold(event.OnEvent) end return objectTests \ No newline at end of file diff --git a/tests/runtests.lua b/tests/runtests.lua index 4249110..b338b81 100644 --- a/tests/runtests.lua +++ b/tests/runtests.lua @@ -16,20 +16,18 @@ package.path="./?.lua;../?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package This will be pushed directly to the master as tests start existing. ]] local multi, thread = require("multi"):init() - local good = false runTest = thread:newFunction(function() local objects = multi:newProcessor("Basic Object Tests") objects.Start() - otest = require("tests/objectTests")(objects,thread) - thread.hold(otest.OnEvent) - print("Timers: Ok") - print("Connections: Ok") - print("Threads: Ok") - print(objects:getTasksDetails()) - good = true - print("\nTests done") + require("tests/objectTests")(objects,thread) + objects.Stop() + local conn_thread = multi:newProcessor("Connection/Thread Tests") + conn_thread.Start() + require("tests/connectionTest")(conn_thread,thread) + conn_thread.Stop() + print(multi:getTasksDetails()) os.exit() -end,true) -print(runTest()) +end) +runTest() multi:mainloop() \ No newline at end of file