mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
Some major changes to loveManager
Still working on version 14.0,.0
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
--local test = love.newByteData(string.rep("\0",16)
|
||||
local ffi = require("ffi")
|
||||
local scratchpad = {}
|
||||
local mt = {
|
||||
__index = function(t, k)
|
||||
if type(k)=="string" then
|
||||
local a, b = k:match("(%d+):(%d+)")
|
||||
return t:read(tonumber(a),tonumber(b))
|
||||
elseif type(k)=="number" then
|
||||
return t:read(k,1)
|
||||
end
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
t:write(v,k)
|
||||
end
|
||||
}
|
||||
function scratchpad:new(data, size, rep)
|
||||
local c = {}
|
||||
local pad
|
||||
if type(data)=="string" then
|
||||
pad = love.data.newByteData(data or string.rep(rep or "\0",size or 16))
|
||||
elseif data:type()=="ByteData" then
|
||||
pad = bytedata
|
||||
end
|
||||
local ptr = ffi.cast("unsigned char*",pad:getPointer())
|
||||
local size = pad:getSize()
|
||||
function c:write(data, loc, len)
|
||||
if loc+(len or #data)>size then
|
||||
error("Attpemting to write data outside the bounds of data byte array!")
|
||||
end
|
||||
ffi.copy(ptr+(loc or 0), data, len or #data)
|
||||
end
|
||||
function c:read(loc, len)
|
||||
return ffi.string(ptr+(loc or 0), len or size)
|
||||
end
|
||||
setmetatable(c,mt)
|
||||
return c
|
||||
end
|
||||
return scratchpad
|
||||
+30
-77
@@ -952,27 +952,6 @@ function multi.nextStep(func)
|
||||
end
|
||||
multi.OnPreLoad=multi:newConnection()
|
||||
--Core Actors
|
||||
function multi:newCustomObject(objRef,isActor)
|
||||
local c={}
|
||||
if isActor then
|
||||
c=self:newBase()
|
||||
if type(objRef)=='table' then
|
||||
table.merge(c,objRef)
|
||||
end
|
||||
if not c.Act then
|
||||
function c:Act()
|
||||
-- Empty function
|
||||
end
|
||||
end
|
||||
else
|
||||
c=objRef or {}
|
||||
end
|
||||
if not c.Type then
|
||||
c.Type='coustomObject'
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
function multi:newEvent(task)
|
||||
local c=self:newBase()
|
||||
c.Type='event'
|
||||
@@ -1434,40 +1413,6 @@ function multi:newTimeStamper()
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
function multi:newWatcher(namespace,name)
|
||||
local function WatcherObj(ns,n)
|
||||
if self.Type=='queue' then
|
||||
multi.print("Cannot create a watcher on a queue! Creating on 'multi' instead!")
|
||||
self=multi
|
||||
end
|
||||
local c=self:newBase()
|
||||
c.Type='watcher'
|
||||
c.ns=ns
|
||||
c.n=n
|
||||
c.cv=ns[n]
|
||||
function c:OnValueChanged(func)
|
||||
table.insert(self.func,func)
|
||||
return self
|
||||
end
|
||||
function c:Act()
|
||||
if self.cv~=self.ns[self.n] then
|
||||
for i=1,#self.func do
|
||||
self.func[i](self,self.cv,self.ns[self.n])
|
||||
end
|
||||
self.cv=self.ns[self.n]
|
||||
end
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
if type(namespace)~='table' and type(namespace)=='string' then
|
||||
return WatcherObj(_G,namespace)
|
||||
elseif type(namespace)=='table' and (type(name)=='string' or 'number') then
|
||||
return WatcherObj(namespace,name)
|
||||
else
|
||||
multi.print('Warning, invalid arguments! Nothing returned!')
|
||||
end
|
||||
end
|
||||
-- Threading stuff
|
||||
multi.GlobalVariables={}
|
||||
if os.getOS()=="windows" then
|
||||
@@ -1536,27 +1481,33 @@ function thread.waitFor(name)
|
||||
return thread.get(name)
|
||||
end
|
||||
function thread:newFunction(func)
|
||||
local c = {}
|
||||
local c = {Type = "tfunc"}
|
||||
c.__call = function(self,...)
|
||||
local rets, err
|
||||
local t = multi:newThread("TempThread",func)
|
||||
local rets, err
|
||||
local function wait()
|
||||
return thread.hold(function()
|
||||
if err then
|
||||
return multi.NIL, err
|
||||
elseif rets then
|
||||
return unpack(rets)
|
||||
end
|
||||
end)
|
||||
end
|
||||
local t = multi:newThread("TempThread",func,...)
|
||||
t.OnDeath(function(self,status,...) rets = {...} end)
|
||||
t.OnError(function(self,e) err = e end)
|
||||
return {
|
||||
wait = function()
|
||||
return thread.hold(function()
|
||||
if err then
|
||||
return multi.NIL, err
|
||||
elseif rets then
|
||||
return unpack(rets)
|
||||
end
|
||||
end)
|
||||
end,
|
||||
connect = function(f)
|
||||
t.OnDeath(function(self,status,...) f(...) end)
|
||||
t.OnError(function(self,err) f(self, err) end)
|
||||
end
|
||||
}
|
||||
--if thread.isThread() then
|
||||
-- return wait()
|
||||
--else
|
||||
return {
|
||||
isTFunc = true,
|
||||
wait = wait,
|
||||
connect = function(f)
|
||||
t.OnDeath(function(self,status,...) f(...) end)
|
||||
t.OnError(function(self,err) f(self, err) end)
|
||||
end
|
||||
}
|
||||
--end
|
||||
end
|
||||
setmetatable(c,c)
|
||||
return c
|
||||
@@ -1600,24 +1551,26 @@ local threadCount = 0
|
||||
local threadid = 0
|
||||
thread.__threads = {}
|
||||
local threads = thread.__threads
|
||||
function multi:newThread(name,func)
|
||||
local Gref = _G
|
||||
function multi:newThread(name,func,...)
|
||||
local func = func or name
|
||||
if type(name) == "function" then
|
||||
name = "Thread#"..threadCount
|
||||
end
|
||||
local env = {}
|
||||
setmetatable(env,{
|
||||
__index = _G,
|
||||
__index = Gref,
|
||||
__newindex = function(t,k,v)
|
||||
if type(v)=="function" then
|
||||
rawset(t,k,thread:newFunction(v))
|
||||
else
|
||||
rawset(t,k,v)
|
||||
Gref[k]=v
|
||||
end
|
||||
end
|
||||
})
|
||||
setfenv(func,env)
|
||||
local c={}
|
||||
c.startArgs = {...}
|
||||
c.ref={}
|
||||
c.Name=name
|
||||
c.thread=coroutine.create(func)
|
||||
@@ -1741,7 +1694,7 @@ function multi.initThreads()
|
||||
multi.scheduler:OnLoop(function(self)
|
||||
for i=#threads,1,-1 do
|
||||
if not threads[i].__started then
|
||||
_,ret,r1,r2,r3,r4,r5,r6=coroutine.resume(threads[i].thread)
|
||||
_,ret,r1,r2,r3,r4,r5,r6=coroutine.resume(threads[i].thread,unpack(threads[i].startArgs))
|
||||
threads[i].__started = true
|
||||
helper(i)
|
||||
end
|
||||
|
||||
@@ -73,7 +73,70 @@ function THREAD.get(name)
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ""
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
local strings = {
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"0",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z"
|
||||
}
|
||||
for i = 1, n do
|
||||
str = str .. "" .. strings[math.random(1, #strings)]
|
||||
end
|
||||
|
||||
@@ -309,7 +309,70 @@ function dump(func)
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ""
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
local strings = {
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
"d",
|
||||
"e",
|
||||
"f",
|
||||
"g",
|
||||
"h",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"l",
|
||||
"m",
|
||||
"n",
|
||||
"o",
|
||||
"p",
|
||||
"q",
|
||||
"r",
|
||||
"s",
|
||||
"t",
|
||||
"u",
|
||||
"v",
|
||||
"w",
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"1",
|
||||
"2",
|
||||
"3",
|
||||
"4",
|
||||
"5",
|
||||
"6",
|
||||
"7",
|
||||
"8",
|
||||
"9",
|
||||
"0",
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"H",
|
||||
"I",
|
||||
"J",
|
||||
"K",
|
||||
"L",
|
||||
"M",
|
||||
"N",
|
||||
"O",
|
||||
"P",
|
||||
"Q",
|
||||
"R",
|
||||
"S",
|
||||
"T",
|
||||
"U",
|
||||
"V",
|
||||
"W",
|
||||
"X",
|
||||
"Y",
|
||||
"Z"
|
||||
}
|
||||
for i = 1, n do
|
||||
str = str .. "" .. strings[math.random(1, #strings)]
|
||||
end
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
local multi, thread = require("multi").init()
|
||||
local status = require("multi.integration.loveManager.status")
|
||||
local pad = require("multi.integration.loveManager.scratchpad")
|
||||
GLOBAL = multi.integration.GLOBAL
|
||||
THREAD = multi.integration.THREAD
|
||||
function multi:newSystemThreadedQueue(name)
|
||||
local c = {}
|
||||
c.Name = name
|
||||
local fRef = {"func",nil}
|
||||
function c:init()
|
||||
local q = {}
|
||||
q.chan = love.thread.getChannel(self.Name)
|
||||
function q:push(dat)
|
||||
if type(dat) == "function" then
|
||||
fRef[2] = THREAD.dump(dat)
|
||||
self.chan:push(fRef)
|
||||
return
|
||||
else
|
||||
self.chan:push(dat)
|
||||
end
|
||||
end
|
||||
function q:pop()
|
||||
local dat = self.chan:pop()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
function q:peek()
|
||||
local dat = self.chan:peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
return q
|
||||
end
|
||||
THREAD.package(name,c)
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c = {}
|
||||
c.name = name
|
||||
function c:init()
|
||||
return THREAD.createTable(self.name)
|
||||
end
|
||||
THREAD.package(name,c)
|
||||
return c
|
||||
end
|
||||
local jqc = 1
|
||||
function multi:newSystemThreadedJobQueue(n)
|
||||
local c = {}
|
||||
c.cores = n or THREAD.getCores()
|
||||
c.registerQueue = {}
|
||||
c.funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||
c.queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||
c.queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||
c.queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||
c.id = 0
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
c._bytedata = love.data.newByteData(string.rep(status.BUSY,c.cores))
|
||||
c.bytedata = pad:new(c._bytedata)
|
||||
local allfunc = 0
|
||||
function c:doToAll(func)
|
||||
self.bytedata:write(string.rep(status.BUSY,c.cores)) -- set all variables to busy
|
||||
local f = THREAD.dump(func)
|
||||
for i = 1, self.cores do
|
||||
self.queueAll:push({allfunc,f})
|
||||
end
|
||||
allfunc = allfunc + 1
|
||||
end
|
||||
function c:registerFunction(name,func)
|
||||
if self.funcs[name] then
|
||||
error("A function by the name "..name.." has already been registered!")
|
||||
end
|
||||
self.bytedata:write(string.rep(status.BUSY,c.cores)) -- set all variables to busy
|
||||
self.funcs[name] = func
|
||||
end
|
||||
function c:pushJob(name,...)
|
||||
self.id = self.id + 1
|
||||
self.queue:push{name,self.id,...}
|
||||
return self.id
|
||||
end
|
||||
multi:newThread("jobManager",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local dat = c.queueReturn:pop()
|
||||
if dat then
|
||||
print(dat)
|
||||
c.OnJobCompleted:Fire(unpack(dat))
|
||||
end
|
||||
end
|
||||
end)
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
||||
local multi, thread = require("multi"):init()
|
||||
local function atomic(channel)
|
||||
return channel:pop()
|
||||
end
|
||||
require("love.timer")
|
||||
local clock = os.clock
|
||||
local pad = require("multi.integration.loveManager.scratchpad")
|
||||
local status = require("multi.integration.loveManager.status")
|
||||
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||
local queue = love.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||
local queueReturn = love.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||
local lastProc = clock()
|
||||
local queueAll = love.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||
local registry = {}
|
||||
setmetatable(_G,{__index = funcs})
|
||||
multi:newThread("startUp",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
lastProc = os.clock()
|
||||
THREAD.loadDump(queueAll:pop()[2])()
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:newThread("runner",function()
|
||||
thread.sleep(.1)
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
lastProc = os.clock()
|
||||
THREAD.loadDump(queueAll:pop()[2])()
|
||||
end
|
||||
local dat = queue:performAtomic(atomic)
|
||||
if dat then
|
||||
lastProc = os.clock()
|
||||
local name = table.remove(dat,1)
|
||||
local id = table.remove(dat,1)
|
||||
local tab = {funcs[name](unpack(dat))}
|
||||
table.insert(tab,1,id)
|
||||
queueReturn:push(tab)
|
||||
end
|
||||
end
|
||||
end):OnError(function(...)
|
||||
error(...)
|
||||
end)
|
||||
multi:newThread("Idler",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
if clock()-lastProc> 2 then
|
||||
THREAD.sleep(.05)
|
||||
else
|
||||
THREAD.sleep(.001)
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
end,jqc)
|
||||
end
|
||||
jqc = jqc + 1
|
||||
return c
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
if ISTHREAD then
|
||||
error("You cannot require the loveManager from within a thread!")
|
||||
end
|
||||
local multi, thread = require("multi.compat.love2d"):init()
|
||||
local THREAD = {}
|
||||
__THREADID__ = 0
|
||||
__THREADNAME__ = "MainThread"
|
||||
multi.integration={}
|
||||
multi.integration.love2d={}
|
||||
multi.integration.love2d.defaultScratchpadSize = 1024
|
||||
multi.integration.love2d.GlobalScratchpad = love.data.newByteData(string.rep("\0",16*multi.integration.love2d.defaultScratchpadSize))
|
||||
local THREAD = require("multi.integration.loveManager.threads")
|
||||
local scratchpad = require("multi.integration.loveManager.scratchpad")
|
||||
local STATUS = require("multi.integration.loveManager.status")
|
||||
local GLOBAL = THREAD.getGlobal()
|
||||
local THREAD_ID = 1 -- The Main thread has ID of 0
|
||||
local OBJECT_ID = 0
|
||||
function multi:newSystemThread(name,func,...)
|
||||
local c = {}
|
||||
c.scratchpad = love.data.newByteData(string.rep("\0",multi.integration.love2d.defaultScratchpadSize))
|
||||
c.name = name
|
||||
c.ID=THREAD_ID
|
||||
c.thread=love.thread.newThread("multi/integration/loveManager/threadREF.lua")
|
||||
c.thread:start(THREAD.dump(func),c.ID,c.name,c.scratchpad,multi.integration.love2d.GlobalScratchpad,...)
|
||||
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
|
||||
GLOBAL["__THREAD_COUNT"] = THREAD_ID
|
||||
THREAD_ID=THREAD_ID+1
|
||||
end
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
return {init=function()
|
||||
return GLOBAL,THREAD
|
||||
end}
|
||||
@@ -0,0 +1,41 @@
|
||||
local ffi = require("ffi")
|
||||
local scratchpad = {}
|
||||
local mt = {
|
||||
__index = function(t, k)
|
||||
if type(k)=="string" then
|
||||
local a, b = k:match("(%d+):(%d+)")
|
||||
return t:read(tonumber(a),tonumber(b))
|
||||
elseif type(k)=="number" then
|
||||
return t:read(k,1)
|
||||
end
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
t:write(v,k)
|
||||
end
|
||||
}
|
||||
function scratchpad:new(data, size, rep)
|
||||
local c = {}
|
||||
local pad
|
||||
if type(data)=="string" then
|
||||
pad = love.data.newByteData(data or string.rep(rep or "\0",size or 16))
|
||||
elseif data:type()=="ByteData" then
|
||||
pad = data
|
||||
end
|
||||
local ptr = ffi.cast("unsigned char*",pad:getPointer())
|
||||
local size = pad:getSize()
|
||||
function c:write(data, loc, len)
|
||||
if (loc or 0)+(len or #data)>size then
|
||||
error("Attpemting to write data outside the bounds of data byte array!")
|
||||
end
|
||||
ffi.copy(ptr+(loc or 0), data, len or #data)
|
||||
end
|
||||
function c:read(loc, len)
|
||||
if (((loc or 0)+(len or size)) > size) or ((loc or 0)<0) then
|
||||
error("Attempt to read outside the bounds of the scratchpad!")
|
||||
end
|
||||
return ffi.string(ptr+(loc or 0), len or size)
|
||||
end
|
||||
setmetatable(c,mt)
|
||||
return c
|
||||
end
|
||||
return scratchpad
|
||||
@@ -0,0 +1,13 @@
|
||||
--[[
|
||||
global pads allow us to directly write data to threads. Each thread has a unique ID which means we can allocate space in memory for each thread to relay stats
|
||||
Below are codes, If there is more data that needs to be sent we can use byte 0 for that and byte 1,2 and 3 to define a channel
|
||||
]]
|
||||
local char = string.char
|
||||
local cmds = {
|
||||
OK = char(0x00), -- All is good thread is running can recieve and send data
|
||||
ERR = char(0x01), -- This tells the system that an error has occured
|
||||
STOP = char(0x02), -- Thread has finished
|
||||
BUSY = char(0x03), -- Thread is busy and isn't responding to messages right now
|
||||
POST = char(0x04), -- Important message for other threads to see, ChannelData with message MSG_TID
|
||||
}
|
||||
return cmds
|
||||
@@ -0,0 +1,13 @@
|
||||
ISTHREAD = true
|
||||
THREAD = require("multi.integration.loveManager.threads") -- order is important!
|
||||
scratchpad = require("multi.integration.loveManager.scratchpad")
|
||||
STATUS = require("multi.integration.loveManager.status")
|
||||
__IMPORTS = {...}
|
||||
__FUNC__=table.remove(__IMPORTS,1)
|
||||
__THREADID__=table.remove(__IMPORTS,1)
|
||||
__THREADNAME__=table.remove(__IMPORTS,1)
|
||||
pad=table.remove(__IMPORTS,1)
|
||||
globalhpad=table.remove(__IMPORTS,1)
|
||||
GLOBAL = THREAD.getGlobal()
|
||||
multi, thread = require("multi").init()
|
||||
THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))
|
||||
@@ -0,0 +1,208 @@
|
||||
--[[
|
||||
Shared methods for both the main thread and
|
||||
]]
|
||||
require("love.timer")
|
||||
require("love.system")
|
||||
require("love.data")
|
||||
local socket = require("socket")
|
||||
local multi, thread = require("multi").init()
|
||||
local threads = {}
|
||||
function threads.loadDump(d)
|
||||
return loadstring(d:getString())
|
||||
end
|
||||
function threads.dump(func)
|
||||
return love.data.newByteData(string.dump(func))
|
||||
end
|
||||
local fRef = {"func",nil}
|
||||
local function manage(channel, value)
|
||||
channel:clear()
|
||||
if type(value) == "function" then
|
||||
fRef[2] = THREAD.dump(value)
|
||||
channel:push(fRef)
|
||||
return
|
||||
else
|
||||
channel:push(value)
|
||||
end
|
||||
end
|
||||
local function RandomVariable(length)
|
||||
local res = {}
|
||||
math.randomseed(socket.gettime()*10000)
|
||||
for i = 1, length do
|
||||
res[#res+1] = string.char(math.random(97, 122))
|
||||
end
|
||||
return table.concat(res)
|
||||
end
|
||||
local GNAME = "__GLOBAL_"
|
||||
local proxy = {}
|
||||
function threads.set(name,val)
|
||||
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
|
||||
proxy[name]:performAtomic(manage, val)
|
||||
end
|
||||
function threads.get(name)
|
||||
if not proxy[name] then proxy[name] = love.thread.getChannel(GNAME..name) end
|
||||
local dat = proxy[name]:peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
|
||||
function threads.waitFor(name)
|
||||
if thread.isThread() then
|
||||
return thread.hold(function()
|
||||
return threads.get(name)
|
||||
end)
|
||||
end
|
||||
while threads.get(name)==nil do
|
||||
love.timer.sleep(.001)
|
||||
end
|
||||
local dat = threads.get(name)
|
||||
if type(dat) == "table" and dat.init then
|
||||
dat.init = threads.loadDump(dat.init)
|
||||
end
|
||||
return dat
|
||||
end
|
||||
|
||||
function threads.package(name,val)
|
||||
local init = val.init
|
||||
val.init=threads.dump(val.init)
|
||||
GLOBAL[name]=val
|
||||
val.init=init
|
||||
end
|
||||
|
||||
function threads.testFor(name,val,sym)
|
||||
-- Not yet implemented
|
||||
end
|
||||
function threads.getCores()
|
||||
return love.system.getProcessorCount()
|
||||
end
|
||||
function threads.kill()
|
||||
|
||||
end
|
||||
function threads.getThreads()
|
||||
local t = {}
|
||||
for i=1,GLOBAL["__THREAD_COUNT"] do
|
||||
t[#t+1]=GLOBAL["__THREAD_"..i]
|
||||
end
|
||||
return t
|
||||
end
|
||||
function threads.getThread(n)
|
||||
return GLOBAL["__THREAD_"..n]
|
||||
end
|
||||
function threads.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
function threads.getID()
|
||||
return __THREADID__
|
||||
end
|
||||
function threads.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
function threads.getGlobal()
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return THREAD.get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
THREAD.set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
function threads.createTable(n)
|
||||
local _proxy = {}
|
||||
local function set(name,val)
|
||||
if not _proxy[name] then _proxy[name] = love.thread.getChannel(n..name) end
|
||||
_proxy[name]:performAtomic(manage, val)
|
||||
end
|
||||
local function get(name)
|
||||
if not _proxy[name] then _proxy[name] = love.thread.getChannel(n..name) end
|
||||
local dat = _proxy[name]:peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
function threads.getConsole()
|
||||
local c = {}
|
||||
c.queue = love.thread.getChannel("__CONSOLE__")
|
||||
function c.print(...)
|
||||
c.queue:push{...}
|
||||
end
|
||||
function c.error(err)
|
||||
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
|
||||
error(err)
|
||||
end
|
||||
return c
|
||||
end
|
||||
if not ISTHREAD then
|
||||
print("mainthread")
|
||||
local clock = os.clock
|
||||
local lastproc = clock()
|
||||
local queue = love.thread.getChannel("__CONSOLE__")
|
||||
multi:newThread("consoleManager",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
dat = queue:pop()
|
||||
if dat then
|
||||
lastproc = clock()
|
||||
print(unpack(dat))
|
||||
end
|
||||
if clock()-lastproc>2 then
|
||||
thread.sleep(.1) -- Printing is for humans, sleep can be big to lower processing
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
function threads.createStaticTable(n)
|
||||
local __proxy = {}
|
||||
local function set(name,val)
|
||||
if __proxy[name] then return end
|
||||
local chan = love.thread.getChannel(n..name)
|
||||
if chan:getCount()>0 then return end
|
||||
chan:performAtomic(manage, val)
|
||||
__proxy[name] = val
|
||||
end
|
||||
local function get(name)
|
||||
if __proxy[name] then return __proxy[name] end
|
||||
local dat = love.thread.getChannel(n..name):peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
__proxy[name] = THREAD.loadDump(dat[2])
|
||||
return __proxy[name]
|
||||
else
|
||||
__proxy[name] = dat
|
||||
return __proxy[name]
|
||||
end
|
||||
end
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
function threads.hold(n)
|
||||
local dat
|
||||
while not(dat) do
|
||||
dat = n()
|
||||
end
|
||||
end
|
||||
return threads
|
||||
@@ -25,95 +25,20 @@ local multi, thread = require("multi").init()
|
||||
function multi:newSystemThreadedQueue(name) -- in love2d this will spawn a channel on both ends
|
||||
local c = {} -- where we will store our object
|
||||
c.name = name -- set the name this is important for the love2d side
|
||||
if love then -- check love
|
||||
if love.thread then -- make sure we can use the threading module
|
||||
function c:init() -- create an init function so we can mimic on both love2d and lanes
|
||||
self.chan = love.thread.getChannel(self.name) -- create channel by the name self.name
|
||||
function self:push(v) -- push to the channel
|
||||
local tab
|
||||
if type(v) == "table" then
|
||||
tab = {}
|
||||
for i, c in pairs(v) do
|
||||
if type(c) == "function" then
|
||||
tab[i] = "\1" .. string.dump(c)
|
||||
else
|
||||
tab[i] = c
|
||||
end
|
||||
end
|
||||
self.chan:push(tab)
|
||||
else
|
||||
self.chan:push(c)
|
||||
end
|
||||
end
|
||||
function self:pop() -- pop from the channel
|
||||
local v = self.chan:pop()
|
||||
if not v then
|
||||
return
|
||||
end
|
||||
if type(v) == "table" then
|
||||
tab = {}
|
||||
for i, c in pairs(v) do
|
||||
if type(c) == "string" then
|
||||
if c:sub(1, 1) == "\1" then
|
||||
tab[i] = loadstring(c:sub(2, -1))
|
||||
else
|
||||
tab[i] = c
|
||||
end
|
||||
else
|
||||
tab[i] = c
|
||||
end
|
||||
end
|
||||
return tab
|
||||
else
|
||||
return self.chan:pop()
|
||||
end
|
||||
end
|
||||
function self:peek()
|
||||
local v = self.chan:peek()
|
||||
if not v then
|
||||
return
|
||||
end
|
||||
if type(v) == "table" then
|
||||
tab = {}
|
||||
for i, c in pairs(v) do
|
||||
if type(c) == "string" then
|
||||
if c:sub(1, 1) == "\1" then
|
||||
tab[i] = loadstring(c:sub(2, -1))
|
||||
else
|
||||
tab[i] = c
|
||||
end
|
||||
else
|
||||
tab[i] = c
|
||||
end
|
||||
end
|
||||
return tab
|
||||
else
|
||||
return self.chan:pop()
|
||||
end
|
||||
end
|
||||
GLOBAL[self.name] = self -- send the object to the thread through the global interface
|
||||
return self -- return the object
|
||||
end
|
||||
return c
|
||||
else
|
||||
error("Make sure you required the love.thread module!") -- tell the user if he/she didn't require said module
|
||||
end
|
||||
else
|
||||
c.linda = lanes.linda() -- lanes is a bit easier, create the linda on the main thread
|
||||
function c:push(v) -- push to the queue
|
||||
self.linda:send("Q", v)
|
||||
end
|
||||
function c:pop() -- pop the queue
|
||||
return ({self.linda:receive(0, "Q")})[2]
|
||||
end
|
||||
function c:peek()
|
||||
return self.linda:get("Q")
|
||||
end
|
||||
function c:init() -- mimic the feature that love2d requires, so code can be consistent
|
||||
return self
|
||||
end
|
||||
multi.integration.GLOBAL[name] = c -- send the object to the thread through the global interface
|
||||
c.linda = lanes.linda() -- lanes is a bit easier, create the linda on the main thread
|
||||
function c:push(v) -- push to the queue
|
||||
self.linda:send("Q", v)
|
||||
end
|
||||
function c:pop() -- pop the queue
|
||||
return ({self.linda:receive(0, "Q")})[2]
|
||||
end
|
||||
function c:peek()
|
||||
return self.linda:get("Q")
|
||||
end
|
||||
function c:init() -- mimic the feature that love2d requires, so code can be consistent
|
||||
return self
|
||||
end
|
||||
multi.integration.GLOBAL[name] = c -- send the object to the thread through the global interface
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user