12.2.1 is out
Fixed some systemthreaded objects. More tests are needed though. About to start making real documentation soon!
This commit is contained in:
parent
eebd942555
commit
7eb9cd37e9
4
Documentation.md
Normal file
4
Documentation.md
Normal file
@ -0,0 +1,4 @@
|
||||
<details>
|
||||
<summary>Q1: What is the best Language in the World? </summary>
|
||||
A1: Lua
|
||||
</details>
|
||||
36
changes.md
36
changes.md
@ -1,5 +1,41 @@
|
||||
#Changes
|
||||
[TOC]
|
||||
Update 12.2.1 Time for some bug fixes!
|
||||
-------------
|
||||
Fixed: SystemThreadedJobQueues
|
||||
- You can now make as many job queues as you want! Just a warning when using a large amount of cores for the queue it takes a second or 2 to set up the jobqueues for data transfer. I am unsure if this is a lanes thing or not, but love2d has no such delay when setting up the jobqueue!
|
||||
- You now connect to the OnReady in the jobqueue object. No more holding everything else as you wait for a job queue to be ready
|
||||
- Jobqueues:doToAll now passes the queues multi interface as the first and currently only argument
|
||||
- No longer need to use jobqueue.OnReady() The code is smarter and will send the pushed jobs automatically when the threads are ready
|
||||
|
||||
Fixed: SystemThreadedConnection
|
||||
- They work the exact same way as before, but actually work as expected now. The issue before was how i implemented it. Now each connection knows the number of instances of that object that ecist. This way I no longer have to do fancy timings that may or may not work. I can send exactly enough info for each connection to consume from the queue.
|
||||
|
||||
Removed: multi:newQueuer
|
||||
- This feature has no real use after corutine based threads were introduced. You can use those to get the same effect as the queuer and do it better too.
|
||||
|
||||
Going forward:
|
||||
- Will I ever finish steralization? Who knows, but being able to save state would be nice. The main issue is there is no simple way to save state. While I can provide methods to allow one to turn the objects into strings and back, there is no way for me to make your code work with it in a simple way. For now only the basic functions will be here.
|
||||
- I need to make better documentation for this library as well. In its current state, all I have are examples and not a list of what is what.
|
||||
|
||||
# Example
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi = require("multi")
|
||||
GLOBAL, THREAD = require("multi.integration.lanesManager").init()
|
||||
jq = multi:newSystemThreadedJobQueue()
|
||||
jq:registerJob("test",function(a)
|
||||
return "Hello",a
|
||||
end)
|
||||
jq.OnJobCompleted(function(ID,...)
|
||||
print(ID,...)
|
||||
end)
|
||||
for i=1,16 do
|
||||
jq:pushJob("test",5)
|
||||
end
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
Update 12.2.0
|
||||
-------------
|
||||
**Added:**
|
||||
|
||||
@ -1,3 +1,26 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi = require("multi")
|
||||
os.sleep=love.timer.sleep
|
||||
multi.drawF={}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ryan Ward
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -24,8 +24,8 @@ SOFTWARE.
|
||||
local bin = pcall(require,"bin")
|
||||
local multi = {}
|
||||
local clock = os.clock
|
||||
multi.Version = "12.2.0"
|
||||
multi._VERSION = "12.2.0"
|
||||
multi.Version = "12.2.1"
|
||||
multi._VERSION = "12.2.1"
|
||||
multi.stage = "stable"
|
||||
multi.__index = multi
|
||||
multi.Mainloop = {}
|
||||
@ -522,40 +522,6 @@ function multi:newProcess(file)
|
||||
--~ c:IngoreObject()
|
||||
return c
|
||||
end
|
||||
function multi:newQueuer(file)
|
||||
local c=self:newProcess()
|
||||
c.Type='queue'
|
||||
c.last={}
|
||||
c.funcE={}
|
||||
function c:OnQueueCompleted(func)
|
||||
table.insert(self.funcE,func)
|
||||
return self
|
||||
end
|
||||
if file then
|
||||
self.Cself=c
|
||||
loadstring('local queue=multi.Cself '..io.open(file,'rb'):read('*all'))()
|
||||
end
|
||||
self:create(c)
|
||||
multi.OnObjectCreated(function(self)
|
||||
if self.Parent then
|
||||
if self.Parent.Type=="queue" then
|
||||
if self:isAnActor() then
|
||||
if self.Type=="alarm" then
|
||||
self.Active=false
|
||||
end
|
||||
self:Pause()
|
||||
self:connectFinal(multi.queuefinal)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
function c:Start()
|
||||
self.Mainloop[#self.Mainloop]:Resume()
|
||||
self.l:Resume()
|
||||
return self
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTimer()
|
||||
local c={}
|
||||
c.Type='timer'
|
||||
@ -1078,7 +1044,10 @@ function multi:uManager(settings)
|
||||
if settings.stopOnError then
|
||||
stopOnError = settings.stopOnError
|
||||
end
|
||||
multi.defaultSettings.p_i = settings.auto_stretch*self.Priority_Idle or self.Priority_Idle
|
||||
multi.defaultSettings.p_i = self.Priority_Idle
|
||||
if settings.auto_stretch then
|
||||
multi.defaultSettings.p_i = settings.auto_stretch*self.Priority_Idle
|
||||
end
|
||||
multi.defaultSettings.delay = settings.auto_delay or 3
|
||||
multi.defaultSettings.auto_lowerbound = settings.auto_lowerbound or self.Priority_Idle
|
||||
protect = settings.protect
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ryan Ward
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
@ -31,9 +31,8 @@ function os.getOS()
|
||||
end
|
||||
-- Step 1 get lanes
|
||||
lanes=require("lanes").configure()
|
||||
--~ package.path="lua/?/init.lua;lua/?.lua;"..package.path
|
||||
local multi = require("multi") -- get it all and have it on all lanes
|
||||
isMainThread=true
|
||||
multi.isMainThread=true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
@ -108,8 +107,10 @@ function THREAD.hold(n)
|
||||
end
|
||||
repeat wait() until n()
|
||||
end
|
||||
local rand = math.random(1,10000000)
|
||||
-- Step 5 Basic Threads!
|
||||
function multi:newSystemThread(name,func,...)
|
||||
rand = math.random(1,10000000)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
@ -117,6 +118,7 @@ function multi:newSystemThread(name,func,...)
|
||||
local THREAD_NAME=name
|
||||
local function func2(...)
|
||||
_G["THREAD_NAME"]=THREAD_NAME
|
||||
math.randomseed(rand)
|
||||
func(...)
|
||||
end
|
||||
c.thread=lanes.gen("*", func2)(...)
|
||||
@ -130,8 +132,7 @@ function multi:newSystemThread(name,func,...)
|
||||
c.status:OnUpdate(function(self)
|
||||
local v,err,t=self.link.thread:join(.001)
|
||||
if err then
|
||||
multi.OnError:Fire(self.link,err)
|
||||
print("Error in systemThread: '"..self.link.name.."' <"..err..">")
|
||||
multi.OnError:Fire(self.link,err,"Error in systemThread: '"..self.link.name.."' <"..err..">")
|
||||
self:Destroy()
|
||||
end
|
||||
end)
|
||||
|
||||
@ -1,3 +1,26 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi = require("multi.compat.love2d")
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ryan Ward
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@ -1,5 +1,26 @@
|
||||
-- CURRENT TASK:
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi = require("multi")
|
||||
local net = require("net")
|
||||
require("bin")
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ryan Ward
|
||||
Copyright (c) 2018 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
@ -114,141 +114,124 @@ function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a chann
|
||||
end
|
||||
function multi:newSystemThreadedConnection(name,protect)
|
||||
local c={}
|
||||
c.name = name
|
||||
c.protect=protect
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
c.name = name or error("You must supply a name for this object!")
|
||||
c.protect = protect or false
|
||||
c.count = 0
|
||||
multi:newSystemThreadedQueue(name.."THREADED_CALLFIRE"):init()
|
||||
local qsm = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNCM"):init()
|
||||
local qs = multi:newSystemThreadedQueue(name.."THREADED_CALLSYNC"):init()
|
||||
function c:init()
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local conns = 0
|
||||
local qF = sThread.waitFor(self.name.."THREADED_CALLFIRE"):init()
|
||||
local qSM = sThread.waitFor(self.name.."THREADED_CALLSYNCM"):init()
|
||||
local qS = sThread.waitFor(self.name.."THREADED_CALLSYNC"):init()
|
||||
qSM:push("OK")
|
||||
local conn = {}
|
||||
conn.name = self.name
|
||||
conn.count = 0
|
||||
if isMainThread then
|
||||
if GLOBAL[self.name.."THREADED_CONNQ"] then -- if this thing exists then lets grab it, we are doing something different here. instead of cleaning things up, we will gave a dedicated queue to manage things
|
||||
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
|
||||
else
|
||||
conn.queueCall = multi:newSystemThreadedQueue(self.name.."THREADED_CALLQ"):init()
|
||||
end
|
||||
else
|
||||
local multi = require("multi") -- so things don't break, but also allows bi-directional connections to work
|
||||
conn.queueCall = sThread.waitFor(self.name.."THREADED_CALLQ"):init()
|
||||
end
|
||||
conn.obj = multi:newConnection(self.protect)
|
||||
setmetatable(conn,{__call=function(self,...) return self:connect(...) end})
|
||||
conn.obj=multi:newConnection(self.protect)
|
||||
function conn:connect(func)
|
||||
return self.obj(func)
|
||||
end
|
||||
function conn:fConnect(func)
|
||||
return self.obj:fConnect(func)
|
||||
end
|
||||
function conn:holdUT(n)
|
||||
self.obj:holdUT(n)
|
||||
end
|
||||
function conn:Bind(t)
|
||||
self.obj:Bind(t)
|
||||
end
|
||||
function conn:Remove()
|
||||
self.obj:Remove()
|
||||
end
|
||||
function conn:getConnection(name,ingore)
|
||||
return self.obj:getConnection(name,ingore)
|
||||
end
|
||||
function conn:Fire(...)
|
||||
local args = {...}
|
||||
table.insert(args,1,multi.randomString(8))
|
||||
table.insert(args,1,self.name)
|
||||
table.insert(args,1,"F")
|
||||
self.queueCall:push(args)
|
||||
if self.trigger_self then
|
||||
self.obj:Fire(...)
|
||||
local args = {multi.randomString(8),...}
|
||||
for i = 1, conns do
|
||||
qF:push(args)
|
||||
end
|
||||
end
|
||||
self.cleanup = .01
|
||||
function conn:SetCleanUpRate(n)
|
||||
self.cleanup=n or .01
|
||||
local lastID = ""
|
||||
local lastCount = 0
|
||||
multi:newThread("syncer",function()
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local fire = qF:peek()
|
||||
local count = qS:peek()
|
||||
if fire and fire[1]~=lastID then
|
||||
lastID = fire[1]
|
||||
qF:pop()
|
||||
table.remove(fire,1)
|
||||
conn.obj:Fire(unpack(fire))
|
||||
end
|
||||
if count and count[1]~=lastCount then
|
||||
conns = count[2]
|
||||
lastCount = count[1]
|
||||
qs:pop()
|
||||
end
|
||||
conn.lastid=""
|
||||
conn.looper = multi:newLoop(function(self)
|
||||
local con = self.link
|
||||
local data = con.queueCall:peek()
|
||||
if not data then return end
|
||||
local id = data[3]
|
||||
if data[1]=="F" and data[2]==con.name and con.lastid~=id then
|
||||
con.lastid=id
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
con.obj:Fire(unpack(data))
|
||||
local alarm = multi:newAlarm(con.cleanup)
|
||||
alarm:OnRing(function()
|
||||
alarm:Destroy()
|
||||
local dat = con.queueCall:peek()
|
||||
if not dat then return end
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
table.remove(data,1)-- Remove the first 3 elements
|
||||
if dat[3]==id then
|
||||
con.queueCall:pop()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
conn.HoldUT=conn.holdUT
|
||||
conn.looper.link=conn
|
||||
conn.Connect=conn.connect
|
||||
conn.FConnect=conn.fConnect
|
||||
conn.GetConnection=conn.getConnection
|
||||
return conn
|
||||
end
|
||||
multi:newThread("connSync",function()
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local syncIN = qsm:pop()
|
||||
if syncIN then
|
||||
if syncIN=="OK" then
|
||||
c.count = c.count + 1
|
||||
else
|
||||
c.count = c.count - 1
|
||||
end
|
||||
local rand = math.random(1,1000000)
|
||||
for i = 1, c.count do
|
||||
qs:push({rand,c.count})
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
GLOBAL[name]=c
|
||||
return c
|
||||
end
|
||||
function multi:systemThreadedBenchmark(n,p)
|
||||
function multi:systemThreadedBenchmark(n)
|
||||
n=n or 1
|
||||
local cores=multi.integration.THREAD.getCores()
|
||||
local queue=multi:newSystemThreadedQueue("QUEUE")
|
||||
multi.integration.GLOBAL["__SYSTEMBENCHMARK__"]=n
|
||||
local queue=multi:newSystemThreadedQueue("THREAD_BENCH_QUEUE"):init()
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local c = {}
|
||||
for i=1,cores do
|
||||
multi:newSystemThread("STHREAD_BENCH",function()
|
||||
multi:newSystemThread("STHREAD_BENCH",function(n)
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end -- we cannot have upvalues... in love2d globals, not locals must be used
|
||||
queue=sThread.waitFor("QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
|
||||
multi:benchMark(sThread.waitFor("__SYSTEMBENCHMARK__")):OnBench(function(self,count)
|
||||
queue=sThread.waitFor("THREAD_BENCH_QUEUE"):init() -- always wait for when looking for a variable at the start of the thread!
|
||||
multi:benchMark(n):OnBench(function(self,count)
|
||||
queue:push(count)
|
||||
multi:Stop()
|
||||
sThread.kill()
|
||||
end)
|
||||
multi:mainloop()
|
||||
end)
|
||||
end
|
||||
local c={}
|
||||
c.tt=function() end
|
||||
c.p=p
|
||||
function c:OnBench(func)
|
||||
self.tt=func
|
||||
end,n)
|
||||
end
|
||||
multi:newThread("THREAD_BENCH",function()
|
||||
thread.sleep(n+.1)
|
||||
GLOBAL["QUEUE"]=nil -- time to clean up
|
||||
local num=0
|
||||
data=queue:pop()
|
||||
while data do
|
||||
num=num+data
|
||||
data=queue:pop()
|
||||
local count = 0
|
||||
local cc = 0
|
||||
while true do
|
||||
thread.skip(1)
|
||||
local dat = queue:pop()
|
||||
if dat then
|
||||
cc=cc+1
|
||||
count = count + dat
|
||||
if cc == cores then
|
||||
c.OnBench:Fire(count)
|
||||
thread.kill()
|
||||
end
|
||||
end
|
||||
if p then
|
||||
print(tostring(p)..num)
|
||||
end
|
||||
c.tt(c,num)
|
||||
end)
|
||||
c.OnBench = multi:newConnection()
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedConsole(name)
|
||||
@ -263,7 +246,7 @@ function multi:newSystemThreadedConsole(name)
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local cc={}
|
||||
if isMainThread then
|
||||
if multi.isMainThread then
|
||||
if GLOBAL["__SYSTEM_CONSLOE__"] then
|
||||
cc.stream = sThread.waitFor("__SYSTEM_CONSLOE__"):init()
|
||||
else
|
||||
@ -304,14 +287,17 @@ function multi:newSystemThreadedTable(name)
|
||||
local sThread=multi.integration.THREAD
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
local multi = require("multi")
|
||||
if multi:getPlatform()=="love2d" then
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
end
|
||||
local cc={}
|
||||
cc.tab={}
|
||||
if isMainThread then
|
||||
if multi.isMainThread then
|
||||
if not GLOBAL[self.name.."_Tabled_Connection"] then
|
||||
cc.conn = multi:newSystemThreadedConnection(self.name.."_Tabled_Connection"):init()
|
||||
end
|
||||
else
|
||||
cc.conn = sThread.waitFor(self.name.."_Tabled_Connection"):init()
|
||||
end
|
||||
@ -330,125 +316,112 @@ function multi:newSystemThreadedTable(name)
|
||||
__newindex=function(t,k,v)
|
||||
t.tab[k]=v
|
||||
t.conn:Fire(k,v)
|
||||
end,
|
||||
end
|
||||
})
|
||||
return cc
|
||||
end
|
||||
GLOBAL[c.name]=c
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
local c={}
|
||||
local jobqueuecount = 0
|
||||
function multi:newSystemThreadedJobQueue(a,b)
|
||||
jobqueuecount=jobqueuecount+1
|
||||
local GLOBAL=multi.integration.GLOBAL
|
||||
local sThread=multi.integration.THREAD
|
||||
local c = {}
|
||||
c.numberofcores = 4
|
||||
c.name = "SYSTEM_THREADED_JOBQUEUE_"..jobqueuecount
|
||||
-- This is done to keep backwards compatability for older code
|
||||
if type(a)=="string" and not(b) then
|
||||
c.name = a
|
||||
elseif type(a)=="number" and not (b) then
|
||||
c.numberofcores = a
|
||||
elseif type(a)=="string" and type(b)=="number" then
|
||||
c.name = a
|
||||
c.numberofcores = b
|
||||
elseif type(a)=="number" and type(b)=="string" then
|
||||
c.name = b
|
||||
c.numberofcores = a
|
||||
end
|
||||
c.isReady = false
|
||||
c.jobnum=1
|
||||
c.cores=numOfCores or multi.integration.THREAD.getCores()
|
||||
c.queueIN=multi:newSystemThreadedQueue("THREADED_JQ"):init()
|
||||
c.queueOUT=multi:newSystemThreadedQueue("THREADED_JQO"):init()
|
||||
c.queueALL=multi:newSystemThreadedQueue("THREADED_QALL"):init()
|
||||
c.REG=multi:newSystemThreadedQueue("THREADED_JQ_F_REG"):init()
|
||||
c.OnReady=multi:newConnection()
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
local queueIN = self:newSystemThreadedQueue("QUEUE_IN_"..c.name):init()
|
||||
local queueCC = self:newSystemThreadedQueue("QUEUE_CC_"..c.name):init()
|
||||
local queueREG = self:newSystemThreadedQueue("QUEUE_REG_"..c.name):init()
|
||||
local queueJD = self:newSystemThreadedQueue("QUEUE_JD_"..c.name):init()
|
||||
local queueDA = self:newSystemThreadedQueue("QUEUE_DA_"..c.name):init()
|
||||
c.OnReady = multi:newConnection()
|
||||
function c:registerJob(name,func)
|
||||
for i=1,self.cores do
|
||||
self.REG:push({name,func})
|
||||
for i = 1, self.numberofcores do
|
||||
queueREG:push({name,func})
|
||||
end
|
||||
end
|
||||
c.tempQueue = {}
|
||||
function c:pushJob(name,...)
|
||||
self.queueOUT:push({self.jobnum,name,...})
|
||||
if not self.isReady then
|
||||
table.insert(c.tempQueue,{self.jobnum,name,...})
|
||||
self.jobnum=self.jobnum+1
|
||||
return self.jobnum-1
|
||||
else
|
||||
queueIN:push{self.jobnum,name,...}
|
||||
self.jobnum=self.jobnum+1
|
||||
return self.jobnum-1
|
||||
end
|
||||
local GLOBAL=multi.integration.GLOBAL -- set up locals in case we are using lanes
|
||||
local sThread=multi.integration.THREAD -- set up locals in case we are using lanes
|
||||
end
|
||||
function c:doToAll(func)
|
||||
local TaskName=multi.randomString(16)
|
||||
for i=1,self.cores do
|
||||
self.queueALL:push({TaskName,func})
|
||||
for i = 1, self.numberofcores do
|
||||
queueDA:push{multi.randomString(12),func}
|
||||
end
|
||||
end
|
||||
function c:start()
|
||||
multi:newEvent(function()
|
||||
return self.ThreadsLoaded==true
|
||||
end):OnEvent(function(evnt)
|
||||
GLOBAL["THREADED_JQ"]=nil -- remove it
|
||||
GLOBAL["THREADED_JQO"]=nil -- remove it
|
||||
GLOBAL["THREADED_JQ_F_REG"]=nil -- remove it
|
||||
self:doToAll(function()
|
||||
_G["__started__"]=true
|
||||
SFunc()
|
||||
end)
|
||||
evnt:Destroy()
|
||||
end)
|
||||
end
|
||||
GLOBAL["__JQ_COUNT__"]=c.cores
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("System Threaded Job Queue Worker Thread #"..i,function(name,ind)
|
||||
for i=1,c.numberofcores do
|
||||
multi:newSystemThread(c.name.." Worker Thread #"..i,function(name)
|
||||
local multi = require("multi")
|
||||
ThreadName=name
|
||||
__sleep__=.001
|
||||
if love then -- lets make sure we don't reference up-values if using love2d
|
||||
GLOBAL=_G.GLOBAL
|
||||
sThread=_G.sThread
|
||||
__sleep__=.1
|
||||
end
|
||||
JQI=sThread.waitFor("THREADED_JQO"):init() -- Grab it
|
||||
JQO=sThread.waitFor("THREADED_JQ"):init() -- Grab it
|
||||
REG=sThread.waitFor("THREADED_JQ_F_REG"):init() -- Grab it
|
||||
QALL=sThread.waitFor("THREADED_QALL"):init() -- Grab it
|
||||
QALLT={}
|
||||
FUNCS={}
|
||||
SFunc=multi:newFunction(function(self)
|
||||
MainLoop:Pause()
|
||||
multi:newAlarm(.1):OnRing(function(alarm)
|
||||
alarm:Destroy()
|
||||
MainLoop:Resume()
|
||||
end)
|
||||
end)
|
||||
local CC = sThread.waitFor("QUEUE_CC_"..name):init()
|
||||
CC:push("ready")
|
||||
local FUNCS={}
|
||||
local ids = {}
|
||||
local JQI = sThread.waitFor("QUEUE_IN_"..name):init()
|
||||
local JD = sThread.waitFor("QUEUE_JD_"..name):init()
|
||||
local REG = sThread.waitFor("QUEUE_REG_"..name):init()
|
||||
local DA = sThread.waitFor("QUEUE_DA_"..name):init()
|
||||
local lastjob = os.clock()
|
||||
multi:newLoop(function()
|
||||
local job=JQI:pop()
|
||||
local rd=REG:peek()
|
||||
local da=DA:peek()
|
||||
if rd then
|
||||
if not FUNCS[rd[1]] then
|
||||
FUNCS[rd[1]]=rd[2]
|
||||
rd=nil -- lets clean up
|
||||
rd=nil
|
||||
REG:pop()
|
||||
end
|
||||
end
|
||||
local d=QALL:peek()
|
||||
if d then
|
||||
if not QALLT[d[1]] then
|
||||
QALLT[d[1]]=true
|
||||
d[2]()
|
||||
d=nil -- lets clean up
|
||||
QALL:pop()
|
||||
end
|
||||
end
|
||||
if da then
|
||||
if not ids[da[1]] then
|
||||
local meh = da[1]
|
||||
ids[da[1]]=true
|
||||
da[2](multi)
|
||||
da=nil
|
||||
DA:pop()
|
||||
multi:newAlarm(60):OnRing(function(a)
|
||||
ids[meh] = nil
|
||||
a:Destroy()
|
||||
end)
|
||||
setmetatable(_G,{
|
||||
__index=function(t,k)
|
||||
return FUNCS[k]
|
||||
end
|
||||
})
|
||||
lastjob=os.clock()
|
||||
MainLoop=multi:newLoop(function(self)
|
||||
if __started__ then
|
||||
local job=JQI:pop()
|
||||
end
|
||||
if job then
|
||||
lastjob=os.clock()
|
||||
local d=QALL:peek()
|
||||
if d then
|
||||
if not QALLT[d[1]] then
|
||||
QALLT[d[1]]=true
|
||||
d[2]()
|
||||
d=nil -- lets clean up
|
||||
QALL:pop()
|
||||
end
|
||||
end
|
||||
lastjob = os.clock()
|
||||
local ID=table.remove(job,1) -- return and remove
|
||||
local name=table.remove(job,1) -- return and remove
|
||||
if FUNCS[name] then
|
||||
JQO:push({ID,FUNCS[name](unpack(job))})
|
||||
else
|
||||
self:hold(function() return FUNCS[name] end)
|
||||
JQO:push({ID,FUNCS[name](unpack(job))})
|
||||
end
|
||||
local _name=table.remove(job,1) -- return and remove
|
||||
if FUNCS[_name] then
|
||||
JD:push({ID,FUNCS[_name](unpack(job))})
|
||||
else -- making use of that new holding feature
|
||||
JD:push({ID,FUNCS:waitFor(_name)(unpack(job))})
|
||||
end
|
||||
end
|
||||
end)
|
||||
@ -460,33 +433,40 @@ function multi:newSystemThreadedJobQueue(numOfCores)
|
||||
thread.sleep(.001)
|
||||
end
|
||||
end)
|
||||
JQO:push({"_THREADINIT_"})
|
||||
setmetatable(_G,{
|
||||
__index=function(t,k)
|
||||
return FUNCS[k]
|
||||
end
|
||||
})
|
||||
if not love then
|
||||
multi:mainloop()
|
||||
end
|
||||
end,"Thread<"..i..">",i)
|
||||
end,c.name)
|
||||
end
|
||||
c.OnJobCompleted=multi:newConnection()
|
||||
c.threadsResponded = 0
|
||||
c.updater=multi:newLoop(function(self)
|
||||
local data=self.link.queueIN:pop()
|
||||
while data do
|
||||
if data then
|
||||
local a=unpack(data)
|
||||
if a=="_THREADINIT_" then
|
||||
self.link.threadsResponded=self.link.threadsResponded+1
|
||||
if self.link.threadsResponded==self.link.cores then
|
||||
self.link.ThreadsLoaded=true
|
||||
self.link.OnReady:Fire()
|
||||
end
|
||||
else
|
||||
self.link.OnJobCompleted:Fire(unpack(data))
|
||||
multi:newThread("counter",function()
|
||||
print("thread started")
|
||||
local _count = 0
|
||||
while _count<c.numberofcores do
|
||||
thread.skip(1)
|
||||
if queueCC:pop() then
|
||||
_count = _count + 1
|
||||
end
|
||||
end
|
||||
data=self.link.queueIN:pop()
|
||||
c.isReady = true
|
||||
for i=1,#c.tempQueue do
|
||||
queueIN:push(c.tempQueue[i])
|
||||
end
|
||||
c.tempQueue = nil
|
||||
c.OnReady:Fire(c)
|
||||
local dat
|
||||
while true do
|
||||
thread.skip(1)
|
||||
dat = queueJD:pop()
|
||||
if dat then
|
||||
c.OnJobCompleted:Fire(unpack(dat))
|
||||
end
|
||||
end
|
||||
end)
|
||||
c.updater.link=c
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedExecute(cmd)
|
||||
|
||||
82
test.lua
82
test.lua
@ -1,54 +1,36 @@
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
--~ package.cpath="./?.dll;"..package.cpath
|
||||
--~ time = require("time")
|
||||
--~ local d1 = time.date(2012, 4, 30)
|
||||
--~ a=time.nowlocal()
|
||||
--~ while true do
|
||||
--~ print(time.nowlocal():ticks())
|
||||
--~ end
|
||||
multi = require("multi")
|
||||
--~ multi:newTLoop(function(self)
|
||||
--~ a = 0
|
||||
--~ end,.001)
|
||||
--~ multi:benchMark(1,nil,"Steps/s:"):OnBench(function()
|
||||
--~ os.exit()
|
||||
--~ end)
|
||||
function multi:ResetPriority()
|
||||
self.solid = false
|
||||
end
|
||||
local clock = os.clock
|
||||
function sleep(n) -- seconds
|
||||
local t0 = clock()
|
||||
while clock() - t0 <= n do end
|
||||
end
|
||||
local a=0
|
||||
local b=0
|
||||
local c=0
|
||||
multi:benchMark(1,multi.Priority_Core,"Regular Bench:"):OnBench(function() -- the onbench() allows us to do each bench after each other!
|
||||
print("AutoP\n---------------")
|
||||
multi:newLoop(function()
|
||||
a=a+1
|
||||
end)
|
||||
t=multi:newLoop(function()
|
||||
c=c+1
|
||||
sleep(.001)
|
||||
end)
|
||||
multi:newLoop(function()
|
||||
b=b+1
|
||||
end)
|
||||
multi:benchMark(1,multi.Priority_Core,"Hmm:"):OnBench(function()
|
||||
multi.nextStep(function()
|
||||
print(a,b,c)
|
||||
--~ os.exit()
|
||||
end)
|
||||
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
|
||||
conn = multi:newSystemThreadedConnection("test"):init()
|
||||
multi:newSystemThread("Work",function()
|
||||
local multi = require("multi")
|
||||
conn = THREAD.waitFor("test"):init()
|
||||
conn(function(...)
|
||||
print(...)
|
||||
end)
|
||||
multi:newTLoop(function()
|
||||
conn:Fire("meh2")
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
end)
|
||||
settings = {
|
||||
--~ priority = 3, -- this is overwritten while auto_priority is being used! You can also use -1 for this setting as well
|
||||
auto_priority = true,
|
||||
auto_stretch = 1000,
|
||||
auto_lowerbound = multi.Priority_Idle
|
||||
}
|
||||
while true do
|
||||
multi:uManager(settings)
|
||||
end
|
||||
multi.OnError(function(a,b,c)
|
||||
print(c)
|
||||
end)
|
||||
multi:newTLoop(function()
|
||||
conn:Fire("meh")
|
||||
end,1)
|
||||
conn(function(...)
|
||||
print(">",...)
|
||||
end)
|
||||
|
||||
--~ jq = multi:newSystemThreadedJobQueue()
|
||||
--~ jq:registerJob("test",function(a)
|
||||
--~ return "Hello",a
|
||||
--~ end)
|
||||
--~ jq.OnJobCompleted(function(ID,...)
|
||||
--~ print(ID,...)
|
||||
--~ end)
|
||||
--~ for i=1,16 do
|
||||
--~ jq:pushJob("test",5)
|
||||
--~ end
|
||||
multi:mainloop()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user