diff --git a/README.md b/README.md index a5a5248..9c1cbc1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ -# Multi Version: 16.0.0 Connecting the dots +# Multi Version: 16.0.0 - Getting the priorities straight **Key Changes** -- Concat connections +- Expanded connection logic +- New integration priorityManager +- Tests for threads +- Consistent behavior between the threading integrations +- Bug fixes Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it! @@ -16,9 +20,19 @@ Progress is being made in [v16.0.0](https://github.com/rayaman/multi/tree/v16.0. INSTALLING ---------- Link to optional dependencies: -- [lanes](https://github.com/LuaLanes/lanes) +- [lanes](https://github.com/LuaLanes/lanes) `luarocks install lanes` + +- [chronos](https://github.com/ldrumm/chronos) `luarocks install chronos` - [love2d](https://love2d.org/) + + When using love2d add multi:uManager() or any processor to love.update() + + ```lua + function love.update(dt) + multi:uManager() + end + ``` To install copy the multi folder into your environment and you are good to go
If you want to use the system threads, then you'll need to install lanes or love2d game engine! @@ -34,7 +48,7 @@ https://discord.gg/U8UspuA Planned features/TODO --------------------- -- [x] Create test suite (In progress, mostly done) +- [x] ~~Create test suite (In progress, mostly done)~~ - [ ] Network Parallelism rework Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md) diff --git a/docs/changes.md b/docs/changes.md index cde5e6e..9fabcec 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,7 +1,7 @@ # Changelog Table of contents --- -[Update 16.0.0 - Connecting the dots](#update-1600---connecting-the-dots)
+[Update 16.0.0 - Connecting the dots](#update-1600---getting-the-priorities-straight)
[Update 15.3.1 - Bug fix](#update-1531---bug-fix)
[Update 15.3.0 - A world of connections](#update-1530---a-world-of-connections)
[Update 15.2.1 - Bug fix](#update-1521---bug-fix)
@@ -58,15 +58,30 @@ Table of contents [Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)
[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different) -# Update 16.0.0 - Connecting the dots +# Update 16.0.0 - Getting the priorities straight Added --- +### New Integration - priorityManager + +Allows the user to have multi auto set priorities. Also adds the functionality to create your own runners (multi:mainloop(), multi:umanager()) that you can set using the priority manager. Even if you do not have `chronos` installed these features will still work! + +- multi:setCurrentTask() -- Used to set the current processor. Used in custom processors. - multi:setCurrentProcess() -- Used to set the current processor. It should only be called on a processor object -- multi.warn(...) -- Sends a warning. -- multi.error(err) -- When called this function will gracefully kill multi, cleaning things up. +- multi.warn(...) -- Sends a warning. Yellow `WARNING` +- multi.error(err) -- When called this function will gracefully kill multi, cleaning things up. Red `ERROR` + + **Note:** If you want to have multi.print, multi.warn and multi.error to work you need to enable them in settings + ```lua + multi, thread = require("multi"):init { + print=true, + warn=true, + error=true -- Errors will throw regardless. Setting to true will + -- cause the library to force hard crash itself! + } + ``` - THREAD.setENV(table) -- Set a simple table that will be merged into the global namespace - **Note:** To maintain compatibility between each integration use simple tables. No self references, and string indices only + **Note:** To maintain compatibility between each integration use simple tables. No self references, and string indices only. ```lua THREAD.setENV({ shared_function = function() @@ -204,8 +219,18 @@ Added Changed --- +- 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. +- Connection:getConnection(name) This will return the connection function which you can do what you will with it. +- Fast connections are the only connections. Legacy connections have been removed completely. Not much should change on the users end. Perhaps some minor changes. +- conn:Lock(conn) When supplied with a connection reference (What is returned by Connect(func)) it will only lock that connection Reference and not the entire connection. Calling without any arguments will lock the entire connection. +- connUnlock(conn) When supplied with a connection reference it restores that reference and it can be fired again. When no arguments are supplied it unlocks the entire connection. + + **Note:** Lock and Unlock when supplied with arguments and not supplied with arguments operate on different objects. If you unlock an entire connection. Individual connection refs will not unlock. The same applies with locking. The entire connection and references are treated differently. + - multi.OnObjectCreated is only called when an object is created in a particular process. Proc.OnObjectCreated is needed to detect when an object is created within a process. -- multi.print shows "INFO" before it's message. +- multi.print shows "INFO" before it's message. Blue `INFO` - Connections internals changed, not too much changed on the surface. - newConnection(protect, func, kill) - `protect` disables fastmode, but protects the connection @@ -221,8 +246,9 @@ Removed Fixed --- +- multi:reallocate(processor, index) has been fixed to work with the current changes of the library. - Issue with lanes not handling errors properly. This is now resolved -- Oversight with how pushStatus worked with nesting threaded functions, connections and forwarding events +- Oversight with how pushStatus worked with nesting threaded functions, connections and forwarding events. Changes made and this works now! ```lua func = thread:newFunction(function() for i=1,10 do diff --git a/init.lua b/init.lua index 70c23e6..ea4c5c0 100644 --- a/init.lua +++ b/init.lua @@ -142,25 +142,34 @@ function multi.ForEach(tab,func) for i=1,#tab do func(tab[i]) end end +function multi.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'} + for i=1,n do + str = str..''..strings[math.random(1,#strings)] + end + return str +end + local optimization_stats = {} local ignoreconn = true -function multi:newConnection(protect, func, kill) +local empty_func = function() end +function multi:newConnection(protect,func,kill) local c={} local call_funcs = {} local lock = false - c.__connectionAdded = function() end - c.rawadd = false - c.Parent = self + local locked = {} + local fast = {} + c.Parent=self - setmetatable(c,{ - __call=function(self, ...) -- () + setmetatable(c,{__call=function(self,...) local t = ... if type(t)=="table" then for i,v in pairs(t) do - if v == self then - local ref = self:Connect(select(2, ...)) + if v==self then + local ref = self:Connect(select(2,...)) if ref then - ref.root_link = select(1, ...) + ref.root_link = select(1,...) return ref end return self @@ -171,49 +180,6 @@ function multi:newConnection(protect, func, kill) return self:Connect(...) end end, - __mod = function(obj1, obj2) -- % - local cn = multi:newConnection() - if type(obj1) == "function" and type(obj2) == "table" then - obj2(function(...) - cn:Fire(obj1(...)) - end) - else - multi.error("Invalid mod!", type(obj1), type(obj2),"Expected function, connection(table)") - end - return cn - end, - __concat = function(obj1, obj2) -- .. - local cn = multi:newConnection() - local ref - if type(obj1) == "function" and type(obj2) == "table" then - cn(function(...) - if obj1(...) then - obj2:Fire(...) - end - end) - cn.__connectionAdded = function(conn, func) - cn:Unconnect(conn) - obj2:Connect(func) - end - elseif type(obj1) == "table" and type(obj2) == "function" then - ref = cn(function(...) - obj1:Fire(...) - obj2(...) - end) - cn.__connectionAdded = function() - cn.rawadd = true - cn:Unconnect(ref) - ref = cn(function(...) - if obj2(...) then - obj1:Fire(...) - end - end) - end - else - multi.error("Invalid concat!", type(obj1), type(obj2),"Expected function/connection(table), connection(table)/function") - end - return cn - end, __add = function(c1,c2) -- Or local cn = multi:newConnection() c1(function(...) @@ -270,63 +236,106 @@ function multi:newConnection(protect, func, kill) return #call_funcs~=0 end - function c:getConnection(name,ignore) - if ignore then - return connections[name] or CRef - else - return connections[name] or self + function c:Lock(conn) + if conn and not conn.lock then + conn.lock = function() end + for i = 1, #fast do + if conn.ref == fast[i] then + fast[i] = conn.lock + return self + end + end + return self end - end - - function c:Lock() - lock = self.Fire - self.Fire = function() end + lock = true return self end - function c:Unlock() - self.Fire = lock + function c:Unlock(conn) + if conn and conn.lock then + for i = 1, #fast do + if conn.lock == fast[i] then + fast[i] = conn.ref + return self + end + end + return self + end + lock = false return self end + if protect then + function c:Fire(...) + if lock then return end + local kills = {} + for i=1,#fast do + local suc, err = pcall(fast[i], ...) + if not suc then + print(err) + end + if kill then + table.remove(kills,i) + multi:newTask(function() + for _,k in pairs(kills) do + table.remove(fast, k) + end + end) + end + end + end + end + function c:getConnections() return call_funcs end + function c:getConnection(name, ignore) + return fast[name] or function() multi:warning("") end + end + function c:Unconnect(conn) - if conn.fast then - for i = 1, #call_funcs do - if conn.ref == call_funcs[i] then - table.remove(call_funcs, i) + for i = 1, #fast do + if conn.ref == fast[i] then + return table.remove(fast, i), i + end + end + end + + function c:fastMode() return self end + if kill then + local kills = {} + function c:Fire(...) + if lock then return end + for i=1,#fast do + fast[i](...) + if kill then + table.insert(kills,i) + multi:newTask(function() + for k = #kills, 1, -1 do + table.remove(fast, k) + table.remove(kills,i) + end + end) end end - elseif conn.Destroy then - conn:Destroy() end - end - - function c:Fire(...) - for i=1, #call_funcs do - call_funcs[i](...) - end - end - - -- Not needed anymore, since it's so light, I'll leave it in forever - function c:fastMode() return self end - - function c:Connect(func) - local th - if thread.getRunningThread then - th = thread.getRunningThread() - end - if th then - local fref = func - func = function(...) - __CurrentConnectionThread = th - fref(...) + else + function c:Fire(...) + if lock then return end + for i=1,#fast do + fast[i](...) end end - table.insert(call_funcs, func) + end + + function c:Connect(func, name) + table.insert(fast, func) + if name then + fast[name] = func + else + fast["Conn_"..multi.randomString(12)] = func + end local temp = {fast = true} setmetatable(temp,{ __call=function(s,...) @@ -346,11 +355,7 @@ function multi:newConnection(protect, func, kill) end, }) temp.ref = func - if self.rawadd then - self.rawadd = false - else - self.__connectionAdded(temp, func) - end + temp.name = name return temp end @@ -366,41 +371,11 @@ function multi:newConnection(protect, func, kill) return temp end - if find_optimization then - -- - end - c.connect=c.Connect c.GetConnection=c.getConnection c.HasConnections = c.hasConnections c.GetConnection = c.getConnection - if protect then -- Do some tests and override the fastmode if you want to do something differently - function c:Fire(...) - for i=#call_funcs,1,-1 do - if not call_funcs[i] then return end - local suc, err = pcall(call_funcs[i],...) - if not suc then - multi.print(err) - end - if kill then - table.remove(call_funcs,i) - end - end - end - elseif kill then - function c:Fire(...) - for i=#call_funcs,1,-1 do - call_funcs[i](...) - table.remove(call_funcs,i) - end - end - end - - if func then - c = c .. func - end - if not(ignoreconn) then self:create(c) end @@ -474,6 +449,7 @@ function multi:SetTime(n) self.link:Pause() self.OnTimedOut:Fire(self.link) self:Destroy() + return true end end return self @@ -564,7 +540,7 @@ function multi:isDone() end function multi:create(ref) - self.OnObjectCreated:Fire(ref,self) + self.OnObjectCreated:Fire(ref, self) return self end @@ -653,6 +629,7 @@ function multi:newEvent(task) self:Pause() self.returns = t c.OnEvent:Fire(self) + return true end end function c:SetTask(func) @@ -675,6 +652,7 @@ function multi:newUpdater(skip) if pos >= skip then pos = 0 self.OnUpdate:Fire(self) + return true end pos = pos+1 end @@ -701,6 +679,7 @@ function multi:newAlarm(set) self.Active=false self.OnRing:Fire(self) t = clock() + return true end end function c:Resume() @@ -732,10 +711,12 @@ function multi:newLoop(func,notime) if notime then function c:Act() self.OnLoop:Fire(self) + return true end else function c:Act() self.OnLoop:Fire(self,clock()-start) + return true end end c.OnLoop = self:newConnection():fastMode() @@ -783,6 +764,7 @@ function multi:newStep(start,reset,count,skip) if self.spos>=self.skip then self.spos=0 end + return true end c.Reset=c.Resume c.OnStart = self:newConnection():fastMode() @@ -820,6 +802,7 @@ function multi:newTLoop(func,set) self.life=self.life+1 self.timer:Reset() self.OnLoop:Fire(self,self.life) + return true end end function c:Set(set) @@ -878,6 +861,7 @@ function multi:newTStep(start,reset,count,set) self.OnEnd:Fire(self) self.pos=self.start end + return true end end function c:Set(set) @@ -957,6 +941,14 @@ function multi.getCurrentTask() return __CurrentTask end +function multi:setCurrentProcess() + __CurrentProcess = self +end + +function multi:setCurrentTask() + __CurrentTask = self +end + function multi:getName() return self.Name end @@ -998,7 +990,7 @@ function multi:newProcessor(name, nothread) c.OnError = multi:newConnection() end - c.OnError(multi.print) + c.OnError(multi.error) function c:getThreads() return c.threads @@ -1379,7 +1371,7 @@ function thread:newThread(name, func, ...) c.isError = false c.OnError = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true) - c.OnError(multi.print) + c.OnError(multi.error) function c:getName() return c.Name @@ -1450,7 +1442,7 @@ function thread:newThread(name, func, ...) globalThreads[c] = multi threadid = threadid + 1 - self:create(c) + multi:getCurrentProcess():create(c) c.creationTime = os.clock() return c end @@ -1865,10 +1857,6 @@ local function doOpt() end end -function multi:setCurrentProcess() - __CurrentProcess = self -end - local init = false function multi.init(settings, realsettings) if settings == multi then settings = realsettings end @@ -2076,15 +2064,6 @@ else end end -function multi.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'} - for i=1,n do - str = str..''..strings[math.random(1,#strings)] - end - return str -end - function multi:getChildren() return self.Mainloop end @@ -2173,12 +2152,16 @@ function multi:IsAnActor() return self.Act~=nil end -function multi:reallocate(o, n) - n=n or #o.Mainloop+1 +function multi:reallocate(processor, index) + index=index or #processor.Mainloop+1 local int=self.Parent - self:Destroy() - self.Parent=o - table.insert(o.Mainloop,n,self) + self.Parent=processor + print("Moving task to new processor!") + if index then + table.insert(processor.Mainloop, index, self) + else + table.insert(processor.Mainloop, self) + end self.Active=true return self end @@ -2200,18 +2183,28 @@ end function multi.print(...) if multi.defaultSettings.print then - print("INFO:", table.concat({...}, " ")) + local t = {} + for i,v in pairs({...}) do t[#t+1] = tostring(v) end + io.write("\x1b[94mINFO:\x1b[0m " .. table.concat(t," ") .. "\n") end end function multi.warn(...) if multi.defaultSettings.warn then - print("WARNING:", table.concat({...}, " ")) + local t = {} + for i,v in pairs({...}) do t[#t+1] = tostring(v) end + io.write("\x1b[93mWARNING:\x1b[0m " .. table.concat(t," ") .. "\n") end end -function multi.error(err) - error("ERROR: " .. err) +function multi.error(self, err) + if type(err) == "bool" then crash = err end + if type(self) == "string" then err = self end + io.write("\x1b[91mERROR:\x1b[0m " .. err .. "\n") + error("^^^") + if multi.defaultSettings.error then + os.exit(1) + end end multi.GetType = multi.getType diff --git a/integration/priorityManager/init.lua b/integration/priorityManager/init.lua index 9dd8e58..cf8cd99 100644 --- a/integration/priorityManager/init.lua +++ b/integration/priorityManager/init.lua @@ -25,57 +25,193 @@ local mainloop_p = multi.mainloop_p local uManagerRef = multi.uManagerRef local uManagerRefP = multi.uManagerRefP1 +local PROFILE_COUNT = 5 + -- self:setCurrentProcess() a bit slower than using the local var, but there isn't another option local priorityManager = multi:newProcessor("Priority Manager", true) +priorityManager.newThread = function() multi.warn("You cannot spawn threads on the priority manager!") end + +priorityManager.setPriorityScheme = function() multi.warn("You cannot set priority on the priorityManager!") end + +local function average(t) + local sum = 0 + for _,v in pairs(t) do + sum = sum + v + end + return sum / #t +end + +local function getPriority(obj) + local avg = average(obj.__profiling) + if avg < 0.0002 then + multi.print("Setting priority to: core") + return PList[1] + elseif avg < 0.0004 then + multi.print("Setting priority to: very high") + return PList[2] + elseif avg < 0.0008 then + multi.print("Setting priority to: high") + return PList[3] + elseif avg < 0.001 then + multi.print("Setting priority to: above normal") + return PList[4] + elseif avg < 0.0025 then + multi.print("Setting priority to: normal") + return PList[5] + elseif avg < 0.005 then + multi.print("Setting priority to: below normal") + return PList[6] + elseif avg < 0.008 then + multi.print("Setting priority to: low") + return PList[7] + elseif avg < 0.01 then + multi.print("Setting priority to: very low") + return PList[8] + else + multi.print("Setting priority to: idle") + return PList[9] + end +end + +local start, stop + +priorityManager.uManager = function(self) + -- proc.run already checks if the processor is active + self:setCurrentProcess() + local Loop=self.Mainloop + local ctask + for _D=#Loop,1,-1 do + ctask = Loop[_D] + ctask:setCurrentTask() + start = chronos.nanotime() + if ctask:Act() then + stop = chronos.nanotime() + if ctask.__profiling then + table.insert(ctask.__profiling, stop - start) + end + if ctask.__profiling and #ctask.__profiling == PROFILE_COUNT then + ctask:setPriority(getPriority(ctask)) + ctask:reallocate(ctask.__restoreProc) + ctask.__restoreProc = nil + ctask.__profiling = nil + end + end + self:setCurrentProcess() + end +end local function processHook(obj, proc) + if obj.Type == multi.PROCESS or not(obj.IsAnActor) then return end obj.__restoreProc = proc + obj.__profiling = {} obj:reallocate(priorityManager) end local function init() - local RR, PB, TB = 0, 1, 2 + local registry = {} multi.priorityScheme = { - RoundRobin = 0, - PriorityBased = 1, - TimedBased = 2 + RoundRobin = "RoundRobin", + PriorityBased = "PriorityBased", + TimeBased = "TimeBased" } + function multi:setProfilerCount(count) + PROFILE_COUNT = count + end + + function multi:recalibrate() + if self.__processConn then + local items = self.Mainloop + for i,v in pairs(items) do + processHook(v, self) + end + else + multi.error("Cannot recalibrate the priority if not using Time based mangement!") + end + end + + function multi:isRegistredScheme(scheme) + -- + end + + function multi:getRegisteredScheme(scheme) + -- + end + + local empty_func = function() return true end + function multi:registerScheme(name,options) + local mainloop = options.mainloop or multi.error("You must provide a mainloop option when registring a scheme!") + local umanager = options.umanager or multi.error("You must provide a umanager option when registring a scheme!") + + if not options.condition then + multi.warn("You might want to use condition when registring a scheme! A function that returns true has been auto generated for you!") + end + + local condition = options.condition or empty_func + + if registry[name] and not registry[name].static then + multi.warn("A scheme named: \"" .. name .. "\" has already been registred, overriting!") + else + multi.error("A scheme named: \"" .. name .. "\" has already been registred!") + end + + registry[name] = { + mainloop = mainloop, + umanager = umanger, + condition = condition, + static = options.static or false + } + return true + end + function multi:setPriorityScheme(scheme) + if not self.Type == multi.PROCESS or not self.Type == multi.ROOTPROCESS then multi.warn("You should only invoke setPriorityScheme on a processor object!") end - if scheme == RR then - multi.mainloop = mainloop - multi.uManager = uManagerRef - elseif scheme == PB then - multi.mainloop = mainloop_p - multi.uManager = uManagerRefP - elseif scheme == TB then - -- + + if scheme == multi.priorityScheme.RoundRobin then + if self.__processConn then self.OnObjectCreated:Unconnect(self.__processConn) self.__processConn = nil end + self.mainloop = mainloop + self.uManager = uManagerRef + elseif scheme == multi.priorityScheme.PriorityBased then + if self.__processConn then self.OnObjectCreated:Unconnect(self.__processConn) self.__processConn = nil end + self.mainloop = mainloop_p + self.uManager = uManagerRefP + elseif scheme == multi.priorityScheme.TimeBased then + if not chronos then return multi.warn("Unable to use TimeBased Priority without the chronos library!") end + if self.__processConn then multi.warn("Already enabled TimeBased Priority!") end + self.__processConn = self.OnObjectCreated(processHook) + self.mainloop = mainloop_p + self.uManager = uManagerRefP + elseif self:isRegistredScheme(scheme) then + local mainloop, umanager, condition = self:getRegisteredScheme(scheme) + if condition() then + self.mainloop = mainloop + self.uManager = umanager + end else - multi.error("Invalid priority scheme passed!") + self.error("Invalid priority scheme selected!") end + end end local function init_chronos() - multi:newThread("Priority Manager", function() + thread:newThread("System Priority Manager", function() while true do thread.yield() priorityManager.run() end - end) + end).OnError(multi.error) end if chronos then init_chronos() else - multi.print("In order to have time based priority management, you need to install the chronos library!") + multi.warn("In order to have time based priority management, you need to install the chronos library!") end -init() - ---chronos.nanotime() \ No newline at end of file +init() \ No newline at end of file diff --git a/tests/test.lua b/tests/test.lua index d844fee..299d78f 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -1,5 +1,151 @@ package.path = "../?/init.lua;../?.lua;"..package.path -multi, thread = require("multi"):init{print=true,findopt=true} +multi, thread = require("multi"):init{print=true,warn=true,error=true} require("multi.integration.priorityManager") -multi:mainloop() \ No newline at end of file +test = multi:newProcessor("Test") +test:setPriorityScheme(multi.priorityScheme.TimeBased) +multi.OnObjectCreated(function(proc, obj) + print("MULTI",proc.Type,obj.Type) +end) +local a = 0 +test:newUpdater(100000):OnUpdate(function() + print("Print is slowish") +end) + +print("Running...") + +multi:mainloop() + + +-- local conn1, conn2, conn3 = multi:newConnection(nil,nil,true), multi:newConnection(), multi:newConnection() + +-- local link = conn1(function() +-- print("Conn1, first") +-- end) + +-- local link2 = conn1(function() +-- print("Conn1, second") +-- end) + +-- local link3 = conn1(function() +-- print("Conn1, third") +-- end) + +-- local link4 = conn2(function() +-- print("Conn2, first") +-- end) + +-- local link5 = conn2(function() +-- print("Conn2, second") +-- end) + +-- local link6 = conn2(function() +-- print("Conn2, third") +-- end) + +-- print("Links 1-6",link,link2,link3,link4,link5,link6) +-- conn1:Lock(link) +-- print("All conns\n-------------") +-- conn1:Fire() +-- conn2:Fire() + +-- conn1:Unlock(link) + +-- conn1:Unconnect(link3) +-- conn2:Unconnect(link6) +-- print("All conns Edit\n---------------------") +-- conn1:Fire() +-- conn2:Fire() + +-- thread:newThread(function() +-- print("Awaiting status") +-- thread.hold(conn1 + (conn2 * conn3)) +-- print("Conn or Conn2 and Conn3") +-- end) + +-- multi:newAlarm(1):OnRing(function() +-- print("Conn") +-- conn1:Fire() +-- end) +-- multi:newAlarm(2):OnRing(function() +-- print("Conn2") +-- conn2:Fire() +-- end) +-- multi:newAlarm(3):OnRing(function() +-- print("Conn3") +-- conn3:Fire() +-- os.exit() +-- end) + + +-- local conn = multi:newSystemThreadedConnection("conn"):init() + +-- multi:newSystemThread("Thread_Test_1", function() +-- local multi, thread = require("multi"):init() +-- local conn = GLOBAL["conn"]:init() +-- local console = THREAD.getConsole() +-- conn(function(a,b,c) +-- console.print(THREAD:getName().." was triggered!",a,b,c) +-- end) +-- multi:mainloop() +-- end) + +-- multi:newSystemThread("Thread_Test_2", function() +-- local multi, thread = require("multi"):init() +-- local conn = GLOBAL["conn"]:init() +-- local console = THREAD.getConsole() +-- conn(function(a,b,c) +-- console.print(THREAD:getName().." was triggered!",a,b,c) +-- end) +-- multi:newAlarm(2):OnRing(function() +-- console.print("Fire 2!!!") +-- conn:Fire(4,5,6) +-- THREAD.kill() +-- end) + +-- multi:mainloop() +-- end) +-- local console = THREAD.getConsole() +-- conn(function(a,b,c) +-- console.print("Mainloop conn got triggered!",a,b,c) +-- end) + +-- alarm = multi:newAlarm(1) +-- alarm:OnRing(function() +-- console.print("Fire 1!!!") +-- conn:Fire(1,2,3) +-- end) + +-- alarm = multi:newAlarm(3):OnRing(function() +-- multi:newSystemThread("Thread_Test_3",function() +-- local multi, thread = require("multi"):init() +-- local conn = GLOBAL["conn"]:init() +-- local console = THREAD.getConsole() +-- conn(function(a,b,c) +-- console.print(THREAD:getName().." was triggered!",a,b,c) +-- end) +-- multi:newAlarm(4):OnRing(function() +-- console.print("Fire 3!!!") +-- conn:Fire(7,8,9) +-- end) +-- multi:mainloop() +-- end) +-- end) + +-- multi:newSystemThread("Thread_Test_4",function() +-- local multi, thread = require("multi"):init() +-- local conn = GLOBAL["conn"]:init() +-- local conn2 = multi:newConnection() +-- local console = THREAD.getConsole() +-- multi:newAlarm(2):OnRing(function() +-- conn2:Fire() +-- end) +-- multi:newThread(function() +-- console.print("Conn Test!") +-- thread.hold(conn + conn2) +-- console.print("It held!") +-- end) +-- multi:mainloop() +-- end) + +-- multi:mainloop() \ No newline at end of file