V15.1.0 #26

Merged
rayaman merged 18 commits from V15.1.0 into master 2021-11-30 21:28:18 -05:00
7 changed files with 261 additions and 63 deletions
Showing only changes of commit efa30e30cc - Show all commits

View File

@ -11,6 +11,36 @@ Full Update Showcase
package.path = "./?/init.lua;"..package.path package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init() multi,thread = require("multi"):init()
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)
multi:newThread("Function Status Test",function()
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
ret.OnStatus(function(part,whole)
print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret2.OnStatus(function(part,whole)
print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret3.OnStatus(function(part,whole)
print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
end)
-- Connections can now be added together, if you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
print("Function Done!")
os.exit()
end)
test = thread:newFunction(function() test = thread:newFunction(function()
return 1,2,nil,3,4,5,6,7,8,9 return 1,2,nil,3,4,5,6,7,8,9
end,true) end,true)
@ -68,41 +98,101 @@ multi:mainloop()
Added: Added:
--- ---
## multi:newSystemThreadedJobQueue(n) isEmpty()
- returns true if the queue is empty, false if there are items in the queue.
**Note:** a queue might be empty, but the job may still be running and not finished yet!
Example:
```lua
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
package.cpath = [[C:\Program Files (x86)\Lua\5.1\systree\lib\lua\5.1\?.dll;C:\Program Files (x86)\Lua\5.1\systree\lib\lua\5.1\?\core.dll;]] ..package.cpath
multi,thread = require("multi"):init()
GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads
func = jq:newFunction("test",function(a,b)
THREAD.sleep(2)
return a+b
end)
for i = 1,10 do
func(i,i*3).connect(function(data)
print(data)
end)
end
local a = true
b = false
multi:newThread("Standard Thread 1",function()
while true do
thread.sleep(.1)
print("Empty:",jq:isEmpty())
end
end).OnError(function(self,msg)
print(msg)
end)
multi:mainloop()
```
## multi.TIMEOUT ## multi.TIMEOUT
`multi.TIMEOUT` is equal to "TIMEOUT", it is reccomended to use this incase things change later on. There are plans to change the timeout value to become a custom object instead of a string. `multi.TIMEOUT` is equal to "TIMEOUT", it is reccomended to use this incase things change later on. There are plans to change the timeout value to become a custom object instead of a string.
## new connections on threaded functions
- `func.OnStatus(...)`
Allows you to connect to the status of a function see [thread.pushStatus()](#status-added-to-threaded-functions)
- `func.OnReturn(...)`
Allows you to connect to the functions return event and capture its returns see [Example](#status-added-to-threaded-functions) for an example of it in use.
## multi:newProcessor(name) ## multi:newProcessor(name)
```lua ```lua
package.path = "./?/init.lua;"..package.path package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init() multi,thread = require("multi"):init()
-- Create a processor object, it works a lot like the multi object
sandbox = multi:newProcessor() sandbox = multi:newProcessor()
-- On our processor object create a TLoop that prints "testing..." every second
sandbox:newTLoop(function() sandbox:newTLoop(function()
print("testing...") print("testing...")
end,1) end,1)
-- Create a thread on the processor object
sandbox:newThread("Test Thread",function() sandbox:newThread("Test Thread",function()
-- Create a counter named 'a'
local a = 0 local a = 0
-- Start of the while loop that ends when a = 10
while true do while true do
-- pause execution of the thread for 1 second
thread.sleep(1) thread.sleep(1)
-- increment a by 1
a = a + 1 a = a + 1
-- display the name of the current process
print("Thread Test: ".. multi.getCurrentProcess().Name) print("Thread Test: ".. multi.getCurrentProcess().Name)
if a == 10 then if a == 10 then
-- Stopping the processor stops all objects created inside that process including threads. In the backend threads use a regular multiobject to handle the scheduler and all of the holding functions. These all stop when a processor is stopped. This can be really useful to sandbox processes that might need to turned on and off with ease and not having to think about it.
sandbox.Stop() sandbox.Stop()
end end
end end
-- Catch any errors that may come up
end).OnError(function(...) end).OnError(function(...)
print(...) print(...)
end) end)
sandbox.Start() -- Start the process sandbox.Start() -- Start the process
multi:mainloop() multi:mainloop() -- The main loop that allows all processes to continue
``` ```
**Note:** Processor objects have been added and removed many times in the past, but will remain with this update.
| Attribute | Type | Returns | Description | | Attribute | Type | Returns | Description |
---|---|---|--- ---|---|---|---
Start|Method()|self| Starts the process Start|Method()|self| Starts the process
@ -115,7 +205,70 @@ process|Thread|thread| A handle to a multi thread object
**Note:** All tasks/threads created on a process are linked to that process. If a process is stopped all tasks/threads will be halted until the process is started back up. **Note:** All tasks/threads created on a process are linked to that process. If a process is stopped all tasks/threads will be halted until the process is started back up.
## Connection can now be added together
Very useful when using thread.hold for multiple connections to trigger.
Iif you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
`print(conn + conn2 + conn3 + connN)`
Can be chained as long as you want! See example below
## Status added to threaded functions
- `thread.pushStatus(...)`
Allows a developer to push a status from a function.
- `tFunc.OnStatus(func(...))`
A connection that can be used on a function to view the status of the threaded function
Example:
```lua
package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init()
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)
multi:newThread("Function Status Test",function()
local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
ret.OnStatus(function(part,whole)
--[[ Print out the current status. In this case every second it will update with:
10%
20%
30%
...
100%
Function Done!
]]
print(math.ceil((part/whole)*1000)/10 .."%")
end)
ret2.OnStatus(function(part,whole)
print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret3.OnStatus(function(part,whole)
print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
end)
-- Connections can now be added together, if you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
print("Function Done!")
os.exit()
end)
```
Changed: Changed:
--- ---
@ -339,7 +492,7 @@ multi:lightloop()
``` ```
Going Forward: Going Forward:
--- ---
- There is no longer any plans for sterilization! Functions do not play nice on different platforms and there is no simple way to ensure that things work. - There is no longer any plans for sterilization! Functions do not play nice on different platforms and there is no simple way to ensure that things work.
Quality Of Life: Quality Of Life:
--- ---
@ -583,7 +736,7 @@ Changed:
- thread:newFunction(func,holup) — Added an argument holup to always force the threaded funcion to wait. Meaning you don't need to tell it to func().wait() or func().connect() - thread:newFunction(func,holup) — Added an argument holup to always force the threaded funcion to wait. Meaning you don't need to tell it to func().wait() or func().connect()
- multi:newConnection(protect,callback,kill) — Added the kill argument. Makes connections work sort of like a stack. Pop off the connections as they get called. So a one time connection handler. - multi:newConnection(protect,callback,kill) — Added the kill argument. Makes connections work sort of like a stack. Pop off the connections as they get called. So a one time connection handler.
- I'm not sure callback has been documented in any form. callback gets called each and everytime conn:Fire() gets called! As well as being triggered for each connfunc that is part of the connection. - I'm not sure callback has been documented in any form. callback gets called each and everytime conn:Fire() gets called! As well as being triggered for each connfunc that is part of the connection.
- modified the lanes manager to create globals GLOBAL and THREAD when a thread is started. This way you are now able to more closely mirror code between lanes and love. As of right now parity between both enviroments is now really good. Upvalues being copied by default in lanes is something that I will not try and mirror in love. It's better to pass what you need as arguments, this way you can keep things consistant. looping thorugh upvalues and sterlizing them and sending them are very complex and slow opperations. - modified the lanes manager to create globals GLOBAL and THREAD when a thread is started. This way you are now able to more closely mirror code between lanes and love. As of right now parity between both enviroments is now really good. Upvalues being copied by default in lanes is something that I will not try and mirror in love. It's better to pass what you need as arguments, this way you can keep things consistant. looping through upvalues and sterlizing them and sending them are very complex and slow.
Removed: Removed:
--- ---
@ -831,7 +984,7 @@ Tasks Details Table format
# Update 13.0.0 - Added some documentation, and some new features too check it out! # Update 13.0.0 - Added some documentation, and some new features too check it out!
------------- -------------
**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 performance 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!
Fixed: Fixed:
--- ---

View File

@ -167,15 +167,15 @@ end
-- Used with ISO Threads -- Used with ISO Threads
local function isolateFunction(func,env) local function isolateFunction(func,env)
local dmp = string.dump(func) local dmp = string.dump(func)
local env = env or {} local env = env or {}
if setfenv then if setfenv then
local f = loadstring(dmp,"IsolatedThread_PesudoThreading") local f = loadstring(dmp,"IsolatedThread_PesudoThreading")
setfenv(f,env) setfenv(f,env)
return f return f
else else
return load(dmp,"IsolatedThread_PesudoThreading","bt",env) return load(dmp,"IsolatedThread_PesudoThreading","bt",env)
end end
end end
function multi:Break() function multi:Break()
@ -326,7 +326,7 @@ local _tid = 0
function multi:newBase(ins) function multi:newBase(ins)
if not(self.Type=='rootprocess' or self.Type=='process' or self.Type=='queue' or self.Type == 'sandbox') then error('Can only create an object on multi or an interface obj') return false end if not(self.Type=='rootprocess' or self.Type=='process' or self.Type=='queue' or self.Type == 'sandbox') then error('Can only create an object on multi or an interface obj') return false end
local c = {} local c = {}
if self.Type=='process' or self.Type=='queue' or self.Type=='sandbox' then if self.Type=='process' or self.Type=='queue' or self.Type=='sandbox' then
setmetatable(c, {__index = multi}) setmetatable(c, {__index = multi})
else else
setmetatable(c, {__index = multi}) setmetatable(c, {__index = multi})
@ -373,6 +373,29 @@ function multi:newConnection(protect,func,kill)
else else
return self:connect(...) return self:connect(...)
end end
end,
__add = function(c1,c2)
cn = multi:newConnection()
if not c1.__hasInstances then
cn.__hasInstances = 2
cn.__count = 0
else
cn.__hasInstances = c1.__hasInstances + 1
cn.__count = c1.__count
end
c1(function(...)
cn.__count = cn.__count + 1
if cn.__count == cn.__hasInstances then
cn:Fire(...)
end
end)
c2(function(...)
cn.__count = cn.__count + 1
if cn.__count == cn.__hasInstances then
cn:Fire(...)
end
end)
return cn
end}) end})
c.Type='connector' c.Type='connector'
c.func={} c.func={}
@ -521,6 +544,7 @@ function multi:newConnection(protect,func,kill)
end end
return c return c
end end
multi.OnObjectCreated=multi:newConnection() multi.OnObjectCreated=multi:newConnection()
multi.OnObjectDestroyed=multi:newConnection() multi.OnObjectDestroyed=multi:newConnection()
multi.OnLoad = multi:newConnection(nil,nil,true) multi.OnLoad = multi:newConnection(nil,nil,true)
@ -912,37 +936,37 @@ local globalThreads = {}
local sandcount = 0 local sandcount = 0
function multi:newProcessor(name) function multi:newProcessor(name)
local c = {} local c = {}
setmetatable(c,{__index = self}) setmetatable(c,{__index = self})
local multi,thread = require("multi"):init() -- We need to capture the t in thread local multi,thread = require("multi"):init() -- We need to capture the t in thread
local name = name or "Processor_"..sandcount local name = name or "Processor_"..sandcount
sandcount = sandcount + 1 sandcount = sandcount + 1
c.Mainloop = {} c.Mainloop = {}
c.Type = "process" c.Type = "process"
c.Active = false c.Active = false
c.Name = "multi.process<".. (name or "") .. ">" c.Name = "multi.process<".. (name or "") .. ">"
c.process = self:newThread(c.Name,function() c.process = self:newThread(c.Name,function()
while true do while true do
thread.hold(function() thread.hold(function()
return c.Active return c.Active
end) end)
__CurrentProcess = c __CurrentProcess = c
c:uManager() c:uManager()
__CurrentProcess = self __CurrentProcess = self
end end
end) end)
c.OnError = c.process.OnError c.OnError = c.process.OnError
function c.Start() function c.Start()
c.Active = true c.Active = true
return self return self
end end
function c.Stop() function c.Stop()
c.Active = false c.Active = false
return self return self
end end
c:attachScheduler() c:attachScheduler()
c.initThreads() c.initThreads()
return c return c
end end
-- Threading stuff -- Threading stuff
@ -1205,16 +1229,16 @@ function thread:newFunction(func,holdme)
t.linkedFunction = temp t.linkedFunction = temp
t.statusconnector = temp.OnStatus t.statusconnector = temp.OnStatus
return temp return temp
end end
setmetatable(tfunc,tfunc) setmetatable(tfunc,tfunc)
return 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)
local f = string.dump(func) local f = string.dump(func)
local chunk = load(f,"env","bt",env) local chunk = load(f,"env","bt",env)
return chunk return chunk
end end
function multi:attachScheduler() function multi:attachScheduler()
@ -2055,18 +2079,18 @@ end
-- UTILS -- UTILS
-------- --------
function table.merge(t1, t2) function table.merge(t1, t2)
for k,v in pairs(t2) do for k,v in pairs(t2) do
if type(v) == 'table' then if type(v) == 'table' then
if type(t1[k] or false) == 'table' then if type(t1[k] or false) == 'table' then
table.merge(t1[k] or {}, t2[k] or {}) table.merge(t1[k] or {}, t2[k] or {})
else else
t1[k] = v t1[k] = v
end end
else else
t1[k] = v t1[k] = v
end end
end end
return t1 return t1
end end
if table.unpack and not unpack then if table.unpack and not unpack then
unpack=table.unpack unpack=table.unpack

View File

@ -68,6 +68,9 @@ function multi:newSystemThreadedJobQueue(n)
local doAll = multi:newSystemThreadedQueue() local doAll = multi:newSystemThreadedQueue()
local ID=1 local ID=1
local jid = 1 local jid = 1
function c:isEmpty()
return queueJob:peek()==nil
end
function c:doToAll(func) function c:doToAll(func)
for i=1,c.cores do for i=1,c.cores do
doAll:push{ID,func} doAll:push{ID,func}

View File

@ -102,6 +102,9 @@ function multi:newSystemThreadedJobQueue(n)
self.queue:push{name,self.id,...} self.queue:push{name,self.id,...}
return self.id return self.id
end end
function c:isEmpty()
return queueJob:peek()==nil
end
local nFunc = 0 local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue function c:newFunction(name,func,holup) -- This registers with the queue
if type(name)=="function" then if type(name)=="function" then

View File

@ -100,6 +100,9 @@ function multi:newSystemThreadedJobQueue(n)
self.queue:push{name,self.id,...} self.queue:push{name,self.id,...}
return self.id return self.id
end end
function c:isEmpty()
return queueJob:peek()==nil
end
local nFunc = 0 local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue function c:newFunction(name,func,holup) -- This registers with the queue
if type(name)=="function" then if type(name)=="function" then

View File

@ -93,6 +93,10 @@ function multi:newSystemThreadedJobQueue(n)
jid = jid + 1 jid = jid + 1
return jid-1 return jid-1
end end
function c:isEmpty()
print(#jobs)
return #jobs == 0
end
local nFunc = 0 local nFunc = 0
function c:newFunction(name,func,holup) -- This registers with the queue function c:newFunction(name,func,holup) -- This registers with the queue
local func = stripUpValues(func) local func = stripUpValues(func)

View File

@ -1,23 +1,31 @@
package.path = "./?/init.lua;"..package.path package.path = "./?/init.lua;"..package.path
multi,thread = require("multi"):init() multi,thread = require("multi"):init()
func = thread:newFunction(function() func = thread:newFunction(function(count)
local a = 0 local a = 0
while true do while true do
a = a + 1 a = a + 1
thread.sleep(1) thread.sleep(.1)
thread.pushStatus(a) thread.pushStatus(a,count)
if a == 10 then break end if a == count then break end
end end
return "Done" return "Done"
end) end)
multi:newThread("test",function() multi:newThread("test",function()
local ret = func() local ret = func(10)
local ret2 = func(15)
local ret3 = func(20)
ret.OnStatus(function(part,whole) ret.OnStatus(function(part,whole)
print(math.ceil((part/whole)*1000)/10) print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%")
end) end)
thread.hold(ret.OnReturn) ret2.OnStatus(function(part,whole)
print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
end)
ret3.OnStatus(function(part,whole)
print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
end)
thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
print("Function Done!") print("Function Done!")
os.exit() os.exit()
end) end)