diff --git a/docs/changes.md b/docs/changes.md index a4f65ae..82e882a 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -237,6 +237,7 @@ Added Changed --- +- multi:newUpdater(skip, func) -- Now accepts func as the second argument. So you don't need to call OnUpdate(func) after creation. - multi errors now internally call `multi.error` instead of `multi.print` - Actors Act() method now returns true when the main event is fired. Steps/Loops always return true. Nil is returned otherwise. - Connection:Connect(func, name) Now you can supply a name and name the connection. @@ -296,7 +297,7 @@ Fixed ToDo --- -- N/A +- Network Manager, I know I said it will be in this release, but I'm still planning it out. # Update 15.3.1 - Bug fix Fixed diff --git a/init.lua b/init.lua index 16ec046..10202d9 100644 --- a/init.lua +++ b/init.lua @@ -32,6 +32,51 @@ local find_optimization = false local threadManager local __CurrentConnectionThread +-- Types + +multi.DestroyedObj = { + Type = "DESTROYED", +} + +local function uni() + return multi.DestroyedObj +end + +local function uniN() end +function multi.setType(obj,t) + if t == multi.DestroyedObj then + for i,v in pairs(obj) do + obj[i] = nil + end + setmetatable(obj, { + __index = function(t,k) + return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni}) + end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni + }) + end +end +setmetatable(multi.DestroyedObj, { + __index = function(t,k) + return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni}) + end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni +}) + +multi.DESTROYED = multi.DestroyedObj +multi.ROOTPROCESS = "rootprocess" +multi.CONNECTOR = "connector" +multi.TIMEMASTER = "timemaster" +multi.PROCESS = "process" +multi.TIMER = "timer" +multi.EVENT = "event" +multi.UPDATER = "updater" +multi.ALARM = "alarm" +multi.LOOP = "loop" +multi.TLOOP = "tloop" +multi.STEP = "step" +multi.TSTEP = "tstep" +multi.THREAD = "thread" +multi.SERVICE = "service" + if not _G["$multi"] then _G["$multi"] = {multi = multi, thread = thread} end @@ -43,7 +88,7 @@ local NIL = multi.NIL multi.Mainloop = {} multi.Children = {} multi.Active = true -multi.Type = "rootprocess" +multi.Type = multi.ROOTPROCESS multi.LinkedPath = multi multi.TIMEOUT = "TIMEOUT" multi.TID = 0 @@ -87,24 +132,6 @@ local function pack(...) return {...} end --- Types -multi.DESTROYED = multi.DestroyedObj -multi.ROOTPROCESS = "rootprocess" -multi.CONNECTOR = "connector" -multi.CONNECTOR_LINK = "connector_link" -multi.TIMEMASTER = "timemaster" -multi.PROCESS = "process" -multi.TIMER = "timer" -multi.EVENT = "event" -multi.UPDATER = "updater" -multi.ALARM = "alarm" -multi.LOOP = "loop" -multi.TLOOP = "tloop" -multi.STEP = "step" -multi.TSTEP = "tstep" -multi.THREAD = "thread" -multi.SERVICE = "service" - --Processor local priorityTable = {[false]="Disabled",[true]="Enabled"} local ProcessName = {"SubProcessor","MainProcessor"} @@ -273,7 +300,7 @@ function multi:newConnection(protect,func,kill) return cn end}) - c.Type='connector' + c.Type=multi.CONNECTOR c.func={} c.ID=0 local protect=protect or false @@ -504,7 +531,7 @@ end function multi:SetTime(n) if not n then n=3 end local c=self:newBase() - c.Type='timemaster' + c.Type=multi.TIMEMASTER c.timer=self:newTimer() c.timer:Start() c.set=n @@ -533,7 +560,7 @@ end -- Timer stuff done multi.PausedObjects = {} function multi:Pause() - if self.Type=='rootprocess' then + if self.Type==multi.ROOTPROCESS then multi.print("You cannot pause the main process. Doing so will stop all methods and freeze your program! However if you still want to use multi:_Pause()") else self.Active=false @@ -550,7 +577,7 @@ function multi:Pause() end function multi:Resume() - if self.Type=='process' or self.Type=='rootprocess' then + if self.Type==multi.PROCESS or self.Type==multi.ROOTPROCESS then self.Active=true local c=self:getChildren() for i=1,#c do @@ -567,7 +594,7 @@ function multi:Resume() end function multi:Destroy() - if self.Type=='process' or self.Type=='rootprocess' then + if self.Type==multi.PROCESS or self.Type==multi.ROOTPROCESS then local c=self:getChildren() for i=1,#c do self.OnObjectDestroyed:Fire(c[i]) @@ -620,9 +647,9 @@ end --Constructors [CORE] local _tid = 0 function multi:newBase(ins) - if not(self.Type=='rootprocess' or self.Type=='process') then multi.error('Can only create an object on multi or an interface obj') return false end + if not(self.Type==multi.ROOTPROCESS or self.Type==multi.PROCESS) then multi.error('Can only create an object on multi or an interface obj') return false end local c = {} - if self.Type=='process' then + if self.Type==multi.PROCESS then setmetatable(c, {__index = multi}) else setmetatable(c, {__index = multi}) @@ -646,18 +673,13 @@ function multi:newBase(ins) return c end -function multi:newConnector() - local c = {Type = "connector"} - return c -end - multi.OnObjectCreated=multi:newConnection() multi.OnObjectDestroyed=multi:newConnection() multi.OnLoad = multi:newConnection(nil,nil,true) ignoreconn = false function multi:newTimer() local c={} - c.Type='timer' + c.Type=multi.TIMER local time=0 local count=0 local paused=false @@ -690,7 +712,7 @@ end --Core Actors function multi:newEvent(task) local c=self:newBase() - c.Type='event' + c.Type=multi.EVENT local task = task or function() end function c:Act() local t = task(self) @@ -712,9 +734,9 @@ function multi:newEvent(task) return c end -function multi:newUpdater(skip) +function multi:newUpdater(skip, func) local c=self:newBase() - c.Type='updater' + c.Type=multi.UPDATER local pos = 1 local skip = skip or 1 function c:Act() @@ -731,13 +753,16 @@ function multi:newUpdater(skip) end c.OnUpdate = self:newConnection():fastMode() c:setName(c.Type) + if func then + c.OnUpdate(func) + end self:create(c) return c end function multi:newAlarm(set) local c=self:newBase() - c.Type='alarm' + c.Type=multi.ALARM c:setPriority("Low") c.set=set or 0 local count = 0 @@ -773,9 +798,9 @@ function multi:newAlarm(set) return c end -function multi:newLoop(func,notime) +function multi:newLoop(func, notime) local c=self:newBase() - c.Type='loop' + c.Type=multi.LOOP local start=clock() if notime then function c:Act() @@ -802,7 +827,7 @@ end function multi:newStep(start,reset,count,skip) local c=self:newBase() think=1 - c.Type='step' + c.Type=multi.STEP c.pos=start or 1 c.endAt=reset or math.huge c.skip=skip or 0 @@ -861,7 +886,7 @@ end function multi:newTLoop(func,set) local c=self:newBase() - c.Type='tloop' + c.Type=multi.TLOOP c.set=set or 0 c.timer=self:newTimer() c.life=0 @@ -902,7 +927,7 @@ end function multi:newTStep(start,reset,count,set) local c=self:newStep(start,reset,count) - c.Type='tstep' + c.Type=multi.TSTEP c:setPriority("Low") local reset = reset or math.huge c.timer=clock() @@ -1034,7 +1059,7 @@ function multi:newProcessor(name, nothread) local name = name or "Processor_" .. sandcount sandcount = sandcount + 1 c.Mainloop = {} - c.Type = "process" + c.Type = multi.PROCESS local Active = nothread or false c.Name = name or "" c.threads = {} @@ -1248,7 +1273,7 @@ function thread.hold(n,opt) if type(n) == "number" then thread.getRunningThread().lastSleep = clock() return yield(CMD, t_sleep, n or 0, nil, interval) - elseif type(n) == "table" and n.Type == "connector" then + elseif type(n) == "table" and n.Type == multi.CONNECTOR then return yield(CMD, t_hold, conn_test(n), nil, interval) elseif type(n) == "function" then return yield(CMD, t_hold, n or dFunc, nil, interval) @@ -1499,7 +1524,7 @@ function thread:newThread(name, func, ...) c.Name=name c.thread=create(func) c.sleep=1 - c.Type = "thread" + c.Type = multi.THREAD c.TID = threadid c.firstRunDone=false c._isPaused = false @@ -1561,7 +1586,7 @@ function thread:newThread(name, func, ...) c.Destroy = c.Kill if thread.isThread() then multi:newLoop(function(loop) - if self.Type == "process" then + if self.Type == multi.PROCESS then table.insert(self.startme, c) else table.insert(threadManager.startme, c) @@ -1569,7 +1594,7 @@ function thread:newThread(name, func, ...) loop:Break() end) else - if self.Type == "process" then + if self.Type == multi.PROCESS then table.insert(self.startme, c) else table.insert(threadManager.startme, c) @@ -1782,7 +1807,7 @@ end function multi:newService(func) -- Priority managed threads local c = {} - c.Type = "service" + c.Type = multi.SERVICE c.OnStopped = self:newConnection() c.OnStarted = self:newConnection() local Service_Data = {} @@ -1957,7 +1982,7 @@ local function doOpt() if type(n) == "number" then thread.getRunningThread().lastSleep = clock() return yield(CMD, t_sleep, n or 0, nil, interval) - elseif type(n) == "table" and n.Type == "connector" then + elseif type(n) == "table" and n.Type == multi.CONNECTOR then local rdy = function() return false end @@ -2074,32 +2099,6 @@ if table.unpack and not unpack then unpack=table.unpack end -multi.DestroyedObj = { - Type = "destroyed", -} - -local function uni() - return multi.DestroyedObj -end - -local function uniN() end -function multi.setType(obj,t) - if t == multi.DestroyedObj then - for i,v in pairs(obj) do - obj[i] = nil - end - setmetatable(obj, { - __index = function(t,k) - return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni}) - end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni - }) - end -end -setmetatable(multi.DestroyedObj, { - __index = function(t,k) - return setmetatable({},{__index = uni,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni}) - end,__newindex = uni,__call = uni,__metatable = multi.DestroyedObj,__tostring = function() return "destroyed" end,__unm = uni,__add = uni,__sub = uni,__mul = uni,__div = uni,__mod = uni,__pow = uni,__concat = uni -}) math.randomseed(os.time()) function multi:enableLoadDetection() diff --git a/integration/lanesManager/extensions.lua b/integration/lanesManager/extensions.lua index e2db201..fd687d2 100644 --- a/integration/lanesManager/extensions.lua +++ b/integration/lanesManager/extensions.lua @@ -22,8 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] local multi, thread = require("multi"):init() + if not (GLOBAL and THREAD) then - local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD + GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD else lanes = require("lanes") end @@ -54,17 +55,24 @@ function multi:newSystemThreadedTable(name) local c = {} c.link = lanes.linda() c.Name = name - setmetatable(c,{ + + -- function c:getIndex() + -- return c.link:dump() + -- end + + function c:init() + return self + end + + setmetatable(c,{ __index = function(t,k) return c.link:get(k) end, __newindex = function(t,k,v) - c.link:set(k,v) + c.link:set(k, v) end }) - function c:init() - return self - end + GLOBAL[name or "_"] = c return c end @@ -134,7 +142,7 @@ function multi:newSystemThreadedJobQueue(n) end) for i=1,c.cores do multi:newSystemThread("SystemThreadedJobQueue",function(queue) - local multi,thread = require("multi"):init() + local multi, thread = require("multi"):init() local idle = os.clock() local clock = os.clock local ref = 0 @@ -145,12 +153,14 @@ function multi:newSystemThreadedJobQueue(n) return queueJob:pop() end) idle = clock() - local name = table.remove(dat,1) - local jid = table.remove(dat,1) - local args = table.remove(dat,1) - queueReturn:push{jid, funcs[name](unpack(args)),queue} + thread:newThread("test",function() + local name = table.remove(dat, 1) + local jid = table.remove(dat, 1) + local args = table.remove(dat, 1) + queueReturn:push{jid, funcs[name](unpack(args)), queue} + end) end - end) + end).OnError(multi.error) thread:newThread("DoAllHandler",function() while true do local dat = thread.hold(function() diff --git a/integration/lanesManager/init.lua b/integration/lanesManager/init.lua index 398dc0c..3cb85a4 100644 --- a/integration/lanesManager/init.lua +++ b/integration/lanesManager/init.lua @@ -97,6 +97,7 @@ function multi:newSystemThread(name, func, ...) },function(...) multi, thread = require("multi"):init(multi_settings) require("multi.integration.lanesManager.extensions") + require("multi.integration.sharedExtensions") local has_error = true returns = {pcall(func, ...)} return_linda:set("returns", returns) @@ -179,6 +180,7 @@ multi.integration = {} -- for module creators multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD require("multi.integration.lanesManager.extensions") +require("multi.integration.sharedExtensions") return { init = function() return GLOBAL, THREAD diff --git a/integration/lanesManager/threads.lua b/integration/lanesManager/threads.lua index a1477ef..d8a3838 100644 --- a/integration/lanesManager/threads.lua +++ b/integration/lanesManager/threads.lua @@ -122,13 +122,11 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console) }) function THREAD.setENV(env, name) - name = name or "__env" - GLOBAL[name] = env + GLOBAL[name or "__env"] = env end function THREAD.getENV(name) - name = name or "__env" - return GLOBAL[name] + return GLOBAL[name or "__env"] end function THREAD.exposeENV(name) diff --git a/integration/loveManager/extensions.lua b/integration/loveManager/extensions.lua index c5b1cc1..dfa9688 100644 --- a/integration/loveManager/extensions.lua +++ b/integration/loveManager/extensions.lua @@ -184,12 +184,14 @@ function multi:newSystemThreadedJobQueue(n) 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) + multi:newThread("Test",function() + 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 end):OnError(function(...) diff --git a/integration/loveManager/init.lua b/integration/loveManager/init.lua index 8f26e30..2b5245c 100644 --- a/integration/loveManager/init.lua +++ b/integration/loveManager/init.lua @@ -53,6 +53,7 @@ multi.integration={} multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD pcall(require,"multi.integration.loveManager.extensions") +pcall(require,"multi.integration.sharedExtensions") stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))} ]] @@ -121,5 +122,6 @@ end multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD require("multi.integration.loveManager.extensions") +require("multi.integration.sharedExtensions") multi.print("Integrated Love Threading!") return {init = function() return GLOBAL, THREAD end} diff --git a/integration/pseudoManager/init.lua b/integration/pseudoManager/init.lua index e495223..6c8d187 100644 --- a/integration/pseudoManager/init.lua +++ b/integration/pseudoManager/init.lua @@ -110,6 +110,7 @@ multi.integration = {} -- for module creators multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD require("multi.integration.pseudoManager.extensions") +require("multi.integration.sharedExtensions") return { init = function() return GLOBAL, THREAD diff --git a/integration/sharedExtensions/init.lua b/integration/sharedExtensions/init.lua new file mode 100644 index 0000000..cbb81a5 --- /dev/null +++ b/integration/sharedExtensions/init.lua @@ -0,0 +1,127 @@ +local multi, thread = require("multi"):init() + + +-- Returns a handler that allows a user to interact with an object on another thread! +-- Create on the thread that you want to interact with, send over the handle +function multi:newProxy(obj) + + local c = { + __index = function() + -- + end, + __newindex = function() + -- + end, + __call = function() + -- + end + } + + c.name = multi.randomString(12) + + function c:init() + if not multi.isMainThread then + c.send = multi:newSystemThreadedQueue(self.name.."_S"):init() + c.recv = multi:newSystemThreadedQueue(self.name.."_R"):init() + c.ref = obj + else + GLOBAL = multi.integration.GLOBAL + THREAD = multi.integration.THREAD + c.send = THREAD.waitFor(self.name.."_S") + c.recv = THREAD.waitFor(self.name.."_R") + end + end + + return c +end + +function multi:newSystemThreadedProcessor(name, cores) + + local name = name or "STP_"multi.randomString(4) -- set a random name if none was given. + + local autoscale = autoscale or false -- Will scale up the number of cores that the process uses. + local c = {} + + setmetatable(c,{__index = multi}) + + c.cores = cores or 8 + c.Name = name + c.Mainloop = {} + c.__count = 0 + c.processors = {} + c.proc_list = {} + c.OnObjectCreated = multi:newConnection() + c.parent = self + c.jobqueue = multi:newSystemThreadedJobQueue(c.cores) + + c.jobqueue:registerFunction("__spawnThread__", function(name, func, ...) + local multi, thread = require("multi"):init() + thread:newThread(name, func, ...) + return true + end) + + c.jobqueue:registerFunction("__spawnTask__", function(obj, ...) + local multi, thread = require("multi"):init() + multi[obj](multi, func) + return true + end) + + c.OnObjectCreated(function(proc, obj) + if obj.Type == multi.UPDATER then + local func = obj.OnUpdate:Remove()[1] + c.jobqueue:pushJob("__spawnTask__", "newUpdater", func) + elseif obj.Type == multi.LOOP then + local func = obj.OnLoop:Remove()[1] + c.jobqueue:pushJob("__spawnTask__", "newLoop", func) + else + return multi.error("Invalid type!") + end + end) + + function c:getHandler() + -- Not needed + end + + function c:getThreads() + -- We might want to keep track of the number of threads we have + end + + function c:getFullName() + return self.parent:getFullName() .. "." .. c.Name + end + + function c:getName() + return self.Name + end + + function c:newThread(name, func, ...) + c.jobqueue:pushJob("__spawnThread__", name, func, ...) + end + + function c:newFunction(func, holdme) + return c.jobqueue:newFunction(func, holdme) + end + + function c.run() + -- Not needed + end + + function c.isActive() + -- + end + + function c.Start() + -- + end + + function c.Stop() + -- + end + + function c:Destroy() + -- + end + + return c +end +