diff --git a/changes.md b/changes.md index 9ec603a..32762ab 100644 --- a/changes.md +++ b/changes.md @@ -120,15 +120,84 @@ process|Thread|thread| A handle to a multi thread object Changed: --- +- `f = thread:newFunction(func,holdme)` + - Nothing changed that will affect how the object functions defaulty. The returned function is now a table that is callable and 2 new methods have been added: + + Method | Description + ---|--- + Pause() | Pauses the function, Will cause the function to return `nil, Function is paused` + Resume() | Resumes the function + + ```lua + package.path = "./?/init.lua;"..package.path + multi, thread = require("multi"):init() + + test = thread:newFunction(function(a,b) + thread.sleep(1) + return a,b + end, true) + + print(test(1,2)) + + test:Pause() + + print(test(1,2)) + + test:Resume() + + print(test(1,2)) + + --[[ -- If you left holdme nil/false + + print(test(1,2).connect(function(...) + print(...) + end)) + + test:Pause() + + print(test(1,2).connect(function(...) + print(...) + end)) + + test:Resume() + + print(test(1,2).connect(function(...) + print(...) + end)) + + ]] + + multi:mainloop() + ``` + + **Output:** + + ``` + 1 2 + nil Function is paused + 1 2 + ``` + + **If holdme is nil/false:** + + ``` + nil Function is paused + + + 1 2 nil... + 1 2 nil... + ``` + - thread.hold(n,opt) [Ref. Issue](https://github.com/rayaman/multi/issues/24) - Added option table to thread.hold | Option | Description | ---|--- + | interval | Time between each poll | | cycles | Number of cycles before timing out | | sleep | Number of seconds before timing out | - | interval | Time between each poll | + | skip | Number of cycles before testing again, does not cause a timeout! | - **Note:** cycles and sleep options cannot both be used at the same time. Cycles take priority if both are present! HoldFor and HoldWithin can be emulated using the new features. Old functions will remain for backward compatibility. + **Note:** cycles and sleep options cannot both be used at the same time. Interval and skip cannot be used at the same time either. Cycles take priority than sleep if both are present! HoldFor and HoldWithin can be emulated using the new features. Old functions will remain for backward compatibility. Using cycles, sleep or interval will cause a timeout; returning nil, multi.TIMEOUT - `n` can be a number and thread.hold will act like thread.sleep. When `n` is a number the option table will be ignored! diff --git a/multi/init.lua b/multi/init.lua index ad909b9..7547e7a 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -102,7 +102,7 @@ function multi:getTasksDetails(t) 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(multi.PausedObjects) do + for v,i in pairs(self.PausedObjects) do local name = v.Name or "" if name~="" then name = " <"..name..">" @@ -1007,6 +1007,10 @@ function thread.hold(n,opt) dRef[2] = opt.sleep dRef[3] = n or dFunc return coroutine.yield(dRef) + elseif opt.skip then + dRef[1] = "_skip_" + dRef[2] = opt.skip or 1 + return coroutine.yield(dRef) end end if type(n) == "number" then @@ -1126,27 +1130,50 @@ local function cleanReturns(...) return unpack(returns,1,ind) end function thread:newFunction(func,holdme) - return function(...) - local rets, err + local tfunc = {} + tfunc.Active = true + function tfunc:Pause() + self.Active = false + end + function tfunc:Resume() + self.Active = true + end + local function noWait() + return nil, "Function is paused" + end + local rets, err local function wait(no) - if thread.isThread() and not (no) then - return multi.hold(function() - if err then - 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]) - end - end) - else - while not rets and not err do - multi.scheduler:Act() - end + if thread.isThread() and not (no) then + return multi.hold(function() if err then - return nil,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]) end - return cleanReturns(rets[1],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]) + end) + else + while not rets and not err do + multi.scheduler:Act() end + if err then + return nil,err + end + return cleanReturns(rets[1],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]) end + end + tfunc.__call = function(t,...) + if not t.Active then + if holdme then + return nil, "Function is paused" + end + return { + isTFunc = true, + wait = noWait, + connect = function(f) + f(nil,"Function is paused") + end + } + end local t = multi.getCurrentProcess():newThread("TempThread",func,...) t.OnDeath(function(self,status,...) rets = {...} end) t.OnError(function(self,e) err = e end) @@ -1158,11 +1185,13 @@ function thread:newFunction(func,holdme) wait = wait, connect = function(f) t.OnDeath(function(self,status,...) f(...) end) - t.OnError(function(self,err) f(err) end) + t.OnError(function(self,err) f(nil,err) end) end } return temp end + setmetatable(tfunc,tfunc) + return tfunc end -- A cross version way to set enviroments, not the same as fenv though function multi.setEnv(func,env) diff --git a/test.lua b/test.lua index 2cb56d3..22a513f 100644 --- a/test.lua +++ b/test.lua @@ -2,60 +2,75 @@ package.path = "./?/init.lua;"..package.path multi,thread = require("multi"):init() --GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available - -test = thread:newFunction(function() - return 1,2,nil,3,4,5,6,7,8,9 -end,true) -print(test()) -multi:newThread("testing",function() - print("#Test = ",test()) - print(thread.hold(function() - print("Hello!") - return false - end,{ - interval = 2, - cycles = 3 - })) -- End result, 3 attempts within 6 seconds. If still false then timeout - print("held") -end).OnError(function(...) - print(...) +test = thread:newFunction(function(a,b) + thread.sleep(1) + return a,b end) - -sandbox = multi:newProcessor() -for i,v in pairs(sandbox.process) do - print(i,v) -end -io.read() -sandbox:newTLoop(function() - print("testing...") -end,1) - -test2 = multi:newTLoop(function() - print("testing2...") -end,1) - -sandbox:newThread("Test Thread",function() - local a = 0 - while true do - thread.sleep(1) - a = a + 1 - print("Thread Test: ".. multi.getCurrentProcess().Name) - if a == 10 then - sandbox.Stop() - end - end -end).OnError(function(...) +print(test(1,2).connect(function(...) print(...) -end) -multi:newThread("Test Thread",function() - while true do - thread.sleep(1) - print("Thread Test: ".. multi.getCurrentProcess().Name) - end -end).OnError(function(...) +end)) +test:Pause() +print(test(1,2).connect(function(...) print(...) -end) +end)) +test:Resume() +print(test(1,2).connect(function(...) + print(...) +end)) -sandbox.Start() +-- test = thread:newFunction(function() +-- return 1,2,nil,3,4,5,6,7,8,9 +-- end,true) +-- print(test()) +-- multi:newThread("testing",function() +-- print("#Test = ",test()) +-- print(thread.hold(function() +-- print("Hello!") +-- return false +-- end,{ +-- interval = 2, +-- cycles = 3 +-- })) -- End result, 3 attempts within 6 seconds. If still false then timeout +-- print("held") +-- end).OnError(function(...) +-- print(...) +-- end) + +-- sandbox = multi:newProcessor() +-- for i,v in pairs(sandbox.process) do +-- print(i,v) +-- end +-- io.read() +-- sandbox:newTLoop(function() +-- print("testing...") +-- end,1) + +-- test2 = multi:newTLoop(function() +-- print("testing2...") +-- end,1) + +-- sandbox:newThread("Test Thread",function() +-- local a = 0 +-- while true do +-- thread.sleep(1) +-- a = a + 1 +-- print("Thread Test: ".. multi.getCurrentProcess().Name) +-- if a == 10 then +-- sandbox.Stop() +-- end +-- end +-- end).OnError(function(...) +-- print(...) +-- end) +-- multi:newThread("Test Thread",function() +-- while true do +-- thread.sleep(1) +-- print("Thread Test: ".. multi.getCurrentProcess().Name) +-- end +-- end).OnError(function(...) +-- print(...) +-- end) + +-- sandbox.Start() multi:mainloop() \ No newline at end of file