threaded functions can now be paused

This commit is contained in:
Ryan Ward 2021-07-05 22:23:45 -04:00
parent ea77b934b6
commit 9cff2735ba
3 changed files with 184 additions and 71 deletions

View File

@ -120,15 +120,84 @@ process|Thread|thread| A handle to a multi thread object
Changed: 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) - thread.hold(n,opt) [Ref. Issue](https://github.com/rayaman/multi/issues/24)
- Added option table to thread.hold - Added option table to thread.hold
| Option | Description | | Option | Description |
---|--- ---|---
| interval | Time between each poll |
| cycles | Number of cycles before timing out | | cycles | Number of cycles before timing out |
| sleep | Number of seconds 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 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! - `n` can be a number and thread.hold will act like thread.sleep. When `n` is a number the option table will be ignored!

View File

@ -102,7 +102,7 @@ function multi:getTasksDetails(t)
count = count + 1 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}) 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
for v,i in pairs(multi.PausedObjects) do for v,i in pairs(self.PausedObjects) do
local name = v.Name or "" local name = v.Name or ""
if name~="" then if name~="" then
name = " <"..name..">" name = " <"..name..">"
@ -1007,6 +1007,10 @@ function thread.hold(n,opt)
dRef[2] = opt.sleep dRef[2] = opt.sleep
dRef[3] = n or dFunc dRef[3] = n or dFunc
return coroutine.yield(dRef) return coroutine.yield(dRef)
elseif opt.skip then
dRef[1] = "_skip_"
dRef[2] = opt.skip or 1
return coroutine.yield(dRef)
end end
end end
if type(n) == "number" then if type(n) == "number" then
@ -1126,26 +1130,49 @@ local function cleanReturns(...)
return unpack(returns,1,ind) return unpack(returns,1,ind)
end end
function thread:newFunction(func,holdme) function thread:newFunction(func,holdme)
return function(...) local tfunc = {}
local rets, err 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) local function wait(no)
if thread.isThread() and not (no) then if thread.isThread() and not (no) then
return multi.hold(function() 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 err then 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 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 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 end
local t = multi.getCurrentProcess():newThread("TempThread",func,...) local t = multi.getCurrentProcess():newThread("TempThread",func,...)
t.OnDeath(function(self,status,...) rets = {...} end) t.OnDeath(function(self,status,...) rets = {...} end)
@ -1158,11 +1185,13 @@ function thread:newFunction(func,holdme)
wait = wait, wait = wait,
connect = function(f) connect = function(f)
t.OnDeath(function(self,status,...) f(...) end) 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 end
} }
return temp return temp
end end
setmetatable(tfunc,tfunc)
return tfunc
end end
-- A cross version way to set enviroments, not the same as fenv though -- A cross version way to set enviroments, not the same as fenv though
function multi.setEnv(func,env) function multi.setEnv(func,env)

117
test.lua
View File

@ -2,60 +2,75 @@ package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init() multi,thread = require("multi"):init()
--GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available --GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
test = thread:newFunction(function(a,b)
test = thread:newFunction(function() thread.sleep(1)
return 1,2,nil,3,4,5,6,7,8,9 return a,b
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) end)
print(test(1,2).connect(function(...)
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(...) print(...)
end) end))
multi:newThread("Test Thread",function() test:Pause()
while true do print(test(1,2).connect(function(...)
thread.sleep(1)
print("Thread Test: ".. multi.getCurrentProcess().Name)
end
end).OnError(function(...)
print(...) 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() multi:mainloop()