From 7b70838567b45ad4628fb8a8e6a08efb4ca85272 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 23 Apr 2022 21:07:56 -0400 Subject: [PATCH 01/12] Planning stuff --- test.lua | 106 ++----------------------------------------------------- 1 file changed, 2 insertions(+), 104 deletions(-) diff --git a/test.lua b/test.lua index af0c2cb..1616eb4 100644 --- a/test.lua +++ b/test.lua @@ -2,110 +2,8 @@ package.path = "./?/init.lua;"..package.path multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.threading"):init() --- Using a system thread, but both system and local threads support this! --- Don't worry if you don't have lanes or love2d. PesudoThreading will kick in to emulate the threading features if you do not have access to system threading. -func = THREAD:newFunction(function(count) - print("Starting Status test: ",count) - local a = 0 - while true do - a = a + 1 - THREAD.sleep(.1) - -- Push the status from the currently running threaded function to the main thread - THREAD.pushStatus(a,count) - if a == count then break end - end - return "Done" -end) - -thread:newThread("test",function() - local ret = func(10) - ret.OnStatus(function(part,whole) - print("Ret1: ",math.ceil((part/whole)*1000)/10 .."%") - end) - print("TEST",func(5).wait()) - -- The results from the OnReturn connection is passed by thread.hold - print("Status:",thread.hold(ret.OnReturn)) - print("Function Done!") -end).OnError(function(...) - print("Error:",...) -end) - -local ret = func(10) -local ret2 = func(15) -local ret3 = func(20) -local s1,s2,s3 = 0,0,0 -ret.OnError(function(...) - print("Error:",...) -end) -ret2.OnError(function(...) - print("Error:",...) -end) -ret3.OnError(function(...) - print("Error:",...) -end) -ret.OnStatus(function(part,whole) - s1 = math.ceil((part/whole)*1000)/10 - print(s1) -end) -ret2.OnStatus(function(part,whole) - s2 = math.ceil((part/whole)*1000)/10 - print(s2) -end) -ret3.OnStatus(function(part,whole) - s3 = math.ceil((part/whole)*1000)/10 - print(s3) -end) - -loop = multi:newTLoop() - -function loop:testing() - print("testing haha") +function multi:newSystemThreadedConnection() + -- end -loop:Set(1) -t = loop:OnLoop(function() - print("Looping...") -end):testing() - -local proc = multi:newProcessor("Test") -local proc2 = multi:newProcessor("Test2") -local proc3 = proc2:newProcessor("Test3") -proc.Start() -proc2.Start() -proc3.Start() -proc:newThread("TestThread_1",function() - while true do - thread.sleep(1) - end -end) -proc:newThread("TestThread_2",function() - while true do - thread.sleep(1) - end -end) -proc2:newThread("TestThread_3",function() - while true do - thread.sleep(1) - end -end) - -thread:newThread(function() - thread.sleep(1) - local tasks = multi:getStats() - - for i,v in pairs(tasks) do - print("Process: " ..i.. "\n\tTasks:") - for ii,vv in pairs(v.tasks) do - print("\t\t"..vv:getName()) - end - print("\tThreads:") - for ii,vv in pairs(v.threads) do - print("\t\t"..vv:getName()) - end - end - - thread.sleep(10) -- Wait 10 seconds then kill the process! - os.exit() -end) - multi:mainloop() \ No newline at end of file -- 2.43.0 From bac91574a21faac4b572c70b35922ae43dd573e1 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 6 Jun 2022 22:42:30 -0400 Subject: [PATCH 02/12] Changed readme, added rockspec for 15.2.1 and 15.3.0 --- README.md | 5 +++- multi.zip | Bin 0 -> 52689 bytes rockspecs/multi-15.2-1.rockspec | 39 ++++++++++++++++++++++++++++++++ rockspecs/multi-15.3-0.rockspec | 39 ++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 multi.zip create mode 100644 rockspecs/multi-15.2-1.rockspec create mode 100644 rockspecs/multi-15.3-0.rockspec diff --git a/README.md b/README.md index b1abc29..c1599be 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,10 @@ Link to optional dependencies: 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! -**or** use luarocks `luarocks install multi` + +``` +luarocks install multi +``` Discord ------- diff --git a/multi.zip b/multi.zip new file mode 100644 index 0000000000000000000000000000000000000000..45aa8564f52240ca64b3ee9aa7b4d42fc09cf5b2 GIT binary patch literal 52689 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 JeJwwq{y&vlYVQC5 literal 0 HcmV?d00001 diff --git a/rockspecs/multi-15.2-1.rockspec b/rockspecs/multi-15.2-1.rockspec new file mode 100644 index 0000000..12c0c0c --- /dev/null +++ b/rockspecs/multi-15.2-1.rockspec @@ -0,0 +1,39 @@ +package = "multi" +version = "15.2-1" +source = { + url = "git://github.com/rayaman/multi.git", + tag = "v15.2.1", +} +description = { + summary = "Lua Multi tasking library", + detailed = [[ + This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration). + Check github for documentation. + ]], + homepage = "https://github.com/rayaman/multi", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + ["multi"] = "multi/init.lua", + ["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua", + ["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua", + ["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua", + ["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua", + ["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua", + ["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua", + --["multi.integration.lovrManager"] = "multi/integration/lovrManager/init.lua", + --["multi.integration.lovrManager.extensions"] = "multi/integration/lovrManager/extensions.lua", + --["multi.integration.lovrManager.threads"] = "multi/integration/lovrManager/threads.lua", + ["multi.integration.pesudoManager"] = "multi/integration/pesudoManager/init.lua", + ["multi.integration.pesudoManager.extensions"] = "multi/integration/pesudoManager/extensions.lua", + ["multi.integration.pesudoManager.threads"] = "multi/integration/pesudoManager/threads.lua", + ["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua", + ["multi.integration.threading"] = "multi/integration/threading.lua", + --["multi.integration.networkManager"] = "multi/integration/networkManager.lua", + } +} \ No newline at end of file diff --git a/rockspecs/multi-15.3-0.rockspec b/rockspecs/multi-15.3-0.rockspec new file mode 100644 index 0000000..d96f6f4 --- /dev/null +++ b/rockspecs/multi-15.3-0.rockspec @@ -0,0 +1,39 @@ +package = "multi" +version = "15.3-0" +source = { + url = "git://github.com/rayaman/multi.git", + tag = "v15.3.0", +} +description = { + summary = "Lua Multi tasking library", + detailed = [[ + This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration). + Check github for documentation. + ]], + homepage = "https://github.com/rayaman/multi", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + ["multi"] = "multi/init.lua", + ["multi.integration.lanesManager"] = "multi/integration/lanesManager/init.lua", + ["multi.integration.lanesManager.extensions"] = "multi/integration/lanesManager/extensions.lua", + ["multi.integration.lanesManager.threads"] = "multi/integration/lanesManager/threads.lua", + ["multi.integration.loveManager"] = "multi/integration/loveManager/init.lua", + ["multi.integration.loveManager.extensions"] = "multi/integration/loveManager/extensions.lua", + ["multi.integration.loveManager.threads"] = "multi/integration/loveManager/threads.lua", + --["multi.integration.lovrManager"] = "multi/integration/lovrManager/init.lua", + --["multi.integration.lovrManager.extensions"] = "multi/integration/lovrManager/extensions.lua", + --["multi.integration.lovrManager.threads"] = "multi/integration/lovrManager/threads.lua", + ["multi.integration.pesudoManager"] = "multi/integration/pesudoManager/init.lua", + ["multi.integration.pesudoManager.extensions"] = "multi/integration/pesudoManager/extensions.lua", + ["multi.integration.pesudoManager.threads"] = "multi/integration/pesudoManager/threads.lua", + ["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua", + ["multi.integration.threading"] = "multi/integration/threading.lua", + --["multi.integration.networkManager"] = "multi/integration/networkManager.lua", + } +} \ No newline at end of file -- 2.43.0 From 7dfbf16bc17136c11a5b4093b2190d18ca1c3169 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 6 Jun 2022 22:42:53 -0400 Subject: [PATCH 03/12] Removed multi.zip --- multi.zip | Bin 52689 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 multi.zip diff --git a/multi.zip b/multi.zip deleted file mode 100644 index 45aa8564f52240ca64b3ee9aa7b4d42fc09cf5b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52689 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 JeJwwq{y&vlYVQC5 -- 2.43.0 From 0e2119a87d292fbbd991ce56160674333a5eec86 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 6 Jun 2022 22:43:36 -0400 Subject: [PATCH 04/12] Added .zip to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c9636a2..8538ab3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ *lua5.4 *luajit *.code-workspace -*.dat \ No newline at end of file +*.dat +*.zip \ No newline at end of file -- 2.43.0 From e2bb9644235d9a268157213b86edce57787012b3 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 11 Jun 2022 23:24:28 -0400 Subject: [PATCH 05/12] Removed print statement --- multi/init.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/multi/init.lua b/multi/init.lua index f74e3bc..9625e4c 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -1,4 +1,3 @@ -print("Dev") --[[ MIT License @@ -35,7 +34,7 @@ if not _G["$multi"] then _G["$multi"] = {multi=multi,thread=thread} end -multi.Version = "15.2.0" +multi.Version = "15.3.0" multi.Name = "root" multi.NIL = {Type="NIL"} local NIL = multi.NIL @@ -1472,7 +1471,6 @@ co_status = { switch[task](ref,thd) cmds[r1](ref,r2,r3,r4,r5) if ret ~= CMD and _ ~= nil then -- The rework makes this necessary - print("Hello") co_status["dead"](thd,ref,task,i,th) end r1=nil r2=nil r3=nil r4=nil r5=nil -- 2.43.0 From 9050e65d93fcee4691a5ae8fb6e9d1ed63d490da Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sun, 18 Sep 2022 09:33:13 -0400 Subject: [PATCH 06/12] Back and making progress --- README.md | 7 +- changes.md | 31 +++++++ multi/init.lua | 91 ++++++++++--------- multi/integration/lanesManager/extensions.lua | 2 + test.lua | 26 +++++- 5 files changed, 111 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index b1abc29..131db6a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Multi Version: 15.3.0 +# Multi Version: 15.3.0 Connecting the dots **Key Changes** - @@ -16,8 +16,9 @@ Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3. INSTALLING ---------- Link to optional dependencies: -[lanes](https://github.com/LuaLanes/lanes) -[love2d](https://love2d.org/) +- [lanes](https://github.com/LuaLanes/lanes) + +- [love2d](https://love2d.org/) 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! diff --git a/changes.md b/changes.md index 77368e5..7457dfe 100644 --- a/changes.md +++ b/changes.md @@ -3,6 +3,37 @@ Table of contents --- [Update 15.2.0 - Upgrade Complete](#update-1520---upgrade-complete)
[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)
[Update 15.0.0 - The art of faking it](#update-1500---the-art-of-faking-it)
[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)
[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)
[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)
[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)
[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300---added-some-documentation-and-some-new-features-too-check-it-out)
[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)
[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)
[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)
[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)
[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)
[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)
[Update: 1.11.0](#update-1110)
[Update: 1.10.0](#update-1100)
[Update: 1.9.2](#update-192)
[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)
[Update: 1.9.0](#update-190)
[Update: 1.8.7](#update-187)
[Update: 1.8.6](#update-186)
[Update: 1.8.5](#update-185)
[Update: 1.8.4](#update-184)
[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)
[Update: 1.8.2](#update-182)
[Update: 1.8.1](#update-181)
[Update: 1.7.6](#update-176)
[Update: 1.7.5](#update-175)
[Update: 1.7.4](#update-174)
[Update: 1.7.3](#update-173)
[Update: 1.7.2](#update-172)
[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)
[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)
[Update: 1.6.0](#update-160)
[Update: 1.5.0](#update-150)
[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)
[Update: 1.4.0 (3/20/2017)](#update-140-3202017)
[Update: 1.3.0 (1/29/2017)](#update-130-1292017)
[Update: 1.2.0 (12.31.2016)](#update-120-12312016)
[Update: 1.1.0](#update-110)
[Update: 1.0.0](#update-100)
[Update: 0.6.3](#update-063)
[Update: 0.6.2](#update-062)
[Update: 0.6.1-6](#update-061-6)
[Update: 0.5.1-6](#update-051-6)
[Update: 0.4.1](#update-041)
[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)
[Update: EventManager 2.0.0](#update-eventmanager-200)
[Update: EventManager 1.2.0](#update-eventmanager-120)
[Update: EventManager 1.1.0](#update-eventmanager-110)
[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 15.3.0 - A world of connection + +Full Update Showcase + +Added +--- +- `multi:newSystemThreadedConnection()` + + Allows one to trigger connection events across threads. +- `multi:newConnection():SetHelper(func)` + + Sets the helper function that the connection object uses when creating connection links. + +Changed +--- +- `Connection:[connect, hasConnections, getConnection]` changed to be `Connection:[Connect, HasConnections, getConnections]`. This was done in an attempt to follow a consistent naming scheme. The old methods still will work to prevent old code breaking. + +Removed +--- +- Connection objects methods removed: + - holdUT(), HoldUT() -- With the way `thread.hold(conn)` interacts with connections this method was no longer needed. To emulate this use `multi.hold(conn)`. `multi.hold()` is able to emulate what `thread.hold()` outside of a thread, albeit with some drawbacks. + +Fixed +--- +- + +ToDo +--- + +- Work on network parallelism (I am really excited to start working on this. Not because it will have much use, but because it seems like a cool addition/project to work on. I just need time to actually do work on stuff) + # Update 15.2.0 - Upgrade Complete Full Update Showcase diff --git a/multi/init.lua b/multi/init.lua index cd91633..080c5d9 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -58,15 +58,15 @@ multi.Priority_Very_Low = 16384 multi.Priority_Idle = 65536 multi.PriorityResolve = { - [1]="Core", - [4]="Very High", - [16]="High", - [64]="Above Normal", - [256]="Normal", - [1024]="Below Normal", - [4096]="Low", - [16384]="Very Low", - [65536]="Idle", + [1] = "Core", + [4] = "Very High", + [16] = "High", + [64] = "Above Normal", + [256] = "Normal", + [1024] = "Below Normal", + [4096] = "Low", + [16384] = "Very Low", + [65536] = "Idle", } local PList = {multi.Priority_Core,multi.Priority_Very_High,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Very_Low,multi.Priority_Idle} @@ -116,19 +116,20 @@ function multi:newConnection(protect,func,kill) local lock = false c.callback = func c.Parent=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,...)) + local ref = self:Connect(select(2,...)) ref.root_link = select(1,...) return ref end end - return self:connect(...) + return self:Connect(...) else - return self:connect(...) + return self:Connect(...) end end, __add = function(c1,c2) @@ -154,32 +155,18 @@ function multi:newConnection(protect,func,kill) end) return cn end}) + c.Type='connector' c.func={} c.ID=0 local protect=protect or false local connections={} c.FC=0 + function c:hasConnections() return #call_funcs~=0 end - function c:holdUT(n) - local n=n or 0 - self.waiting=true - local count=0 - local id=self:connect(function() - count = count + 1 - if n<=count then - self.waiting=false - end - end) - repeat - self.Parent:uManager() - until self.waiting==false - id:Destroy() - return self - end - c.HoldUT=c.holdUT + function c:getConnection(name,ignore) if ignore then return connections[name] or CRef @@ -187,14 +174,17 @@ function multi:newConnection(protect,func,kill) return connections[name] or self end end + function c:Lock() lock = true return self end + function c:Unlock() lock = false return self end + if protect then function c:Fire(...) if lock then return end @@ -216,48 +206,56 @@ function multi:newConnection(protect,func,kill) end end end + local fast = {} function c:getConnections() return call_funcs end + function c:fastMode() function self:Fire(...) for i=1,#fast do fast[i](...) end end - function self:connect(func) + function self:Connect(func) table.insert(fast,func) end end + function c:Bind(t) local temp = call_funcs call_funcs=t return temp end + function c:Remove() local temp = call_funcs call_funcs={} return temp end + local function conn_helper(self,func,name,num) self.ID=self.ID+1 + if num then table.insert(call_funcs,num,func) else table.insert(call_funcs,1,func) end + local temp = { func=func, Type="connector_link", Parent=self, connect = function(s,...) - return self:connect(...) + return self:Connect(...) end } + setmetatable(temp,{ __call=function(s,...) - return self:connect(...) + return self:Connect(...) end, __index = function(t,k) if rawget(t,"root_link") then @@ -272,6 +270,7 @@ function multi:newConnection(protect,func,kill) rawset(t,k,v) end, }) + function temp:Fire(...) if lock then return end if protect then @@ -283,6 +282,7 @@ function multi:newConnection(protect,func,kill) return call_funcs(...) end end + function temp:Destroy() for i=#call_funcs,1,-1 do if call_funcs[i]~=nil then @@ -294,6 +294,7 @@ function multi:newConnection(protect,func,kill) end end end + if name then connections[name]=temp end @@ -302,7 +303,8 @@ function multi:newConnection(protect,func,kill) end return temp end - function c:connect(...)--func,name,num + + function c:Connect(...)--func,name,num local tab = {...} local funcs={} for i=1,#tab do @@ -320,8 +322,15 @@ function multi:newConnection(protect,func,kill) return conn_helper(self,tab[1],tab[2],tab[3]) end end + + function c:SetHelper(func) + conn_helper = func + end + c.Connect=c.connect c.GetConnection=c.getConnection + c.HasConnections = c.hasConnections + c.GetConnection = c.getConnection if not(ignoreconn) then multi:create(c) end @@ -2043,14 +2052,14 @@ function multi.print(...) end end -multi.GetType=multi.getType -multi.IsPaused=multi.isPaused -multi.IsActive=multi.isActive -multi.Reallocate=multi.Reallocate -multi.ConnectFinal=multi.connectFinal -multi.ResetTime=multi.SetTime -multi.IsDone=multi.isDone -multi.SetName = multi.setName +multi.GetType = multi.getType +multi.IsPaused = multi.isPaused +multi.IsActive = multi.isActive +multi.Reallocate = multi.Reallocate +multi.ConnectFinal = multi.connectFinal +multi.ResetTime = multi.SetTime +multi.IsDone = multi.isDone +multi.SetName = multi.setName -- Special Events local _os = os.exit diff --git a/multi/integration/lanesManager/extensions.lua b/multi/integration/lanesManager/extensions.lua index 198e551..bc37455 100644 --- a/multi/integration/lanesManager/extensions.lua +++ b/multi/integration/lanesManager/extensions.lua @@ -41,6 +41,7 @@ function multi:newSystemThreadedQueue(name) GLOBAL[name or "_"] = c return c end + function multi:newSystemThreadedTable(name) local c = {} c.link = lanes.linda() @@ -58,6 +59,7 @@ function multi:newSystemThreadedTable(name) GLOBAL[name or "_"] = c return c end + function multi:newSystemThreadedJobQueue(n) local c = {} c.cores = n or THREAD.getCores()*2 diff --git a/test.lua b/test.lua index 1616eb4..dc3361e 100644 --- a/test.lua +++ b/test.lua @@ -2,8 +2,30 @@ package.path = "./?/init.lua;"..package.path multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.threading"):init() -function multi:newSystemThreadedConnection() - -- +function multi:newSystemThreadedConnection(name,...) + local master_conn = multi:newConnection(...) + local c = {} + local name = name or multi.randomString(16) + local connections = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. + setmetatable(c,master_conn) -- A different approach will be taken for the non main connection objects + c.subscribe = multi:newSystemThreadedQueue("Subscribe_"..name) + multi:newThread("STC_"..name,function() + while true do + thread.yield() + local item = c.subscribe:pop() + if item ~= nil then + connections[#connections+1] = item + thread.skip(multi.Priority_Normal) -- Usually a bunch of threads subscribe close to the same time. Process those by ensuring that they come alive around the same time + else -- I'm using these "Constant" values since they may change with other releases and this should allow these functions to adjust with them. + thread.skip(multi.Priority_Idle) + end + end + end) + function c:init() + return self + end + GLOBAL[name or "_"] = c + return c end multi:mainloop() \ No newline at end of file -- 2.43.0 From 8f3eb6d9a35a2266c3d3917394a613e6654c45d9 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sun, 18 Sep 2022 09:36:47 -0400 Subject: [PATCH 07/12] Fixed changelog order --- changes.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/changes.md b/changes.md index 3509c65..8b235e7 100644 --- a/changes.md +++ b/changes.md @@ -4,9 +4,6 @@ Table of contents [Update 15.2.1 - Bug fix](#update-1521---bug-fix)
[Update 15.2.0 - Upgrade Complete](#update-1520---upgrade-complete)
[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)
[Update 15.0.0 - The art of faking it](#update-1500---the-art-of-faking-it)
[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)
[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)
[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)
[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)
[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300---added-some-documentation-and-some-new-features-too-check-it-out)
[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)
[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)
[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)
[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)
[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)
[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)
[Update: 1.11.0](#update-1110)
[Update: 1.10.0](#update-1100)
[Update: 1.9.2](#update-192)
[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)
[Update: 1.9.0](#update-190)
[Update: 1.8.7](#update-187)
[Update: 1.8.6](#update-186)
[Update: 1.8.5](#update-185)
[Update: 1.8.4](#update-184)
[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)
[Update: 1.8.2](#update-182)
[Update: 1.8.1](#update-181)
[Update: 1.7.6](#update-176)
[Update: 1.7.5](#update-175)
[Update: 1.7.4](#update-174)
[Update: 1.7.3](#update-173)
[Update: 1.7.2](#update-172)
[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)
[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)
[Update: 1.6.0](#update-160)
[Update: 1.5.0](#update-150)
[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)
[Update: 1.4.0 (3/20/2017)](#update-140-3202017)
[Update: 1.3.0 (1/29/2017)](#update-130-1292017)
[Update: 1.2.0 (12.31.2016)](#update-120-12312016)
[Update: 1.1.0](#update-110)
[Update: 1.0.0](#update-100)
[Update: 0.6.3](#update-063)
[Update: 0.6.2](#update-062)
[Update: 0.6.1-6](#update-061-6)
[Update: 0.5.1-6](#update-051-6)
[Update: 0.4.1](#update-041)
[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)
[Update: EventManager 2.0.0](#update-eventmanager-200)
[Update: EventManager 1.2.0](#update-eventmanager-120)
[Update: EventManager 1.1.0](#update-eventmanager-110)
[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 15.2.1 - Bug fix -Fixed issue #41 - # Update 15.3.0 - A world of connection Full Update Showcase @@ -38,6 +35,10 @@ ToDo - Work on network parallelism (I am really excited to start working on this. Not because it will have much use, but because it seems like a cool addition/project to work on. I just need time to actually do work on stuff) +# Update 15.2.1 - Bug fix +Fixed issue #41 +--- + # Update 15.2.0 - Upgrade Complete Full Update Showcase -- 2.43.0 From 1546076c80298ee27964baf7bc1a836f6dd62e62 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sun, 18 Sep 2022 12:22:34 -0400 Subject: [PATCH 08/12] Protection is handled by Fire Funcction --- multi/init.lua | 11 +++-------- multi/integration/lanesManager/extensions.lua | 8 ++++---- test.lua | 19 +++++++++++++++---- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/multi/init.lua b/multi/init.lua index c850a53..2cd2953 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -273,14 +273,7 @@ function multi:newConnection(protect,func,kill) function temp:Fire(...) if lock then return end - if protect then - local t=pcall(call_funcs,...) - if t then - return t - end - else - return call_funcs(...) - end + return call_funcs(...) end function temp:Destroy() @@ -298,9 +291,11 @@ function multi:newConnection(protect,func,kill) if name then connections[name]=temp end + if self.callback then self.callback(temp) end + return temp end diff --git a/multi/integration/lanesManager/extensions.lua b/multi/integration/lanesManager/extensions.lua index bc37455..15f5bb1 100644 --- a/multi/integration/lanesManager/extensions.lua +++ b/multi/integration/lanesManager/extensions.lua @@ -64,10 +64,10 @@ function multi:newSystemThreadedJobQueue(n) local c = {} c.cores = n or THREAD.getCores()*2 c.OnJobCompleted = multi:newConnection() - local funcs = multi:newSystemThreadedTable() - local queueJob = multi:newSystemThreadedQueue() - local queueReturn = multi:newSystemThreadedQueue() - local doAll = multi:newSystemThreadedQueue() + local funcs = multi:newSystemThreadedTable():init() + local queueJob = multi:newSystemThreadedQueue():init() + local queueReturn = multi:newSystemThreadedQueue():init() + local doAll = multi:newSystemThreadedQueue():init() local ID=1 local jid = 1 function c:isEmpty() diff --git a/test.lua b/test.lua index b5483b9..f92ec6d 100644 --- a/test.lua +++ b/test.lua @@ -4,24 +4,35 @@ multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.lanesManager"):init() function multi:newSystemThreadedConnection(name,...) - local master_conn = multi:newConnection(...) local c = {} + local proxy_conn = multi:newConnection(...) local name = name or multi.randomString(16) local connections = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. setmetatable(c,master_conn) -- A different approach will be taken for the non main connection objects - c.subscribe = multi:newSystemThreadedQueue("Subscribe_"..name) + c.subscribe = multi:newSystemThreadedQueue("Subscribe_"..name):init() -- Incoming subscriptions multi:newThread("STC_"..name,function() while true do - thread.yield() local item = c.subscribe:pop() + -- We need to check on broken connections + -- c:Ping() + -- if item ~= nil then connections[#connections+1] = item thread.skip(multi.Priority_Normal) -- Usually a bunch of threads subscribe close to the same time. Process those by ensuring that they come alive around the same time - else -- I'm using these "Constant" values since they may change with other releases and this should allow these functions to adjust with them. + else -- I'm using these "Constants" since they may change with other releases and this should allow these functions to adjust with them. thread.skip(multi.Priority_Idle) end end end) + function c:Ping() -- Threaded Function call, can use thread.* + -- + end + function c:Fire(...) + -- + end + function c:Connect(...) + -- + end function c:init() return self end -- 2.43.0 From bb7fab0857ebe7a9a6bed60c1e2d9b9619c4e1a8 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sun, 18 Sep 2022 13:56:00 -0400 Subject: [PATCH 09/12] working on systemthreadedconnections --- changes.md | 4 ++++ multi/init.lua | 8 +++++++- test.lua | 25 ++++++++++++++++++++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/changes.md b/changes.md index 8b235e7..bd5bcae 100644 --- a/changes.md +++ b/changes.md @@ -17,6 +17,10 @@ Added Sets the helper function that the connection object uses when creating connection links. +- `multi.ForEach(table, callback_function)` + + Loops through the table and calls callback_function with each element of the array. + Changed --- - `Connection:[connect, hasConnections, getConnection]` changed to be `Connection:[Connect, HasConnections, getConnections]`. This was done in an attempt to follow a consistent naming scheme. The old methods still will work to prevent old code breaking. diff --git a/multi/init.lua b/multi/init.lua index 2cd2953..fe2f078 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -109,6 +109,12 @@ function multi:getStats() end --Helpers +function multi.ForEach(tab,func) + for i=1,#tab do + func(tab[i]) + end +end + local ignoreconn = true function multi:newConnection(protect,func,kill) local c={} @@ -198,6 +204,7 @@ function multi:newConnection(protect,func,kill) end else function c:Fire(...) + if lock then return end for i=#call_funcs,1,-1 do call_funcs[i](...) if kill then @@ -272,7 +279,6 @@ function multi:newConnection(protect,func,kill) }) function temp:Fire(...) - if lock then return end return call_funcs(...) end diff --git a/test.lua b/test.lua index f92ec6d..bba1341 100644 --- a/test.lua +++ b/test.lua @@ -3,24 +3,34 @@ package.cpath = "lua5.4/lib/lua/?/core.dll;"..package.cpath multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.lanesManager"):init() + + function multi:newSystemThreadedConnection(name,...) local c = {} + c.CONN = 0x00 + c.TRIG = 0x01 + c.PING = 0x02 + c.PONG = 0x03 local proxy_conn = multi:newConnection(...) local name = name or multi.randomString(16) local connections = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. + local funcs = {} setmetatable(c,master_conn) -- A different approach will be taken for the non main connection objects c.subscribe = multi:newSystemThreadedQueue("Subscribe_"..name):init() -- Incoming subscriptions multi:newThread("STC_"..name,function() while true do + thread.yield() local item = c.subscribe:pop() -- We need to check on broken connections -- c:Ping() -- if item ~= nil then connections[#connections+1] = item + multi.ForEach(funcs, function(link) -- Sync new connections + item:push{c.CONN, link} + end) thread.skip(multi.Priority_Normal) -- Usually a bunch of threads subscribe close to the same time. Process those by ensuring that they come alive around the same time - else -- I'm using these "Constants" since they may change with other releases and this should allow these functions to adjust with them. - thread.skip(multi.Priority_Idle) + -- I'm using these "Constants" since they may change with other releases and this should allow these functions to adjust with them. end end end) @@ -30,9 +40,18 @@ function multi:newSystemThreadedConnection(name,...) function c:Fire(...) -- end - function c:Connect(...) + function c:Sync(link) -- end + function c:Connect(func) + local conn_func = func + proxy_conn(function() + funcs[#funcs+1] = func -- Used for syncing new connections to this connection later on + multi.ForEach(c.connections, function(link) + link:push{CONN, func} + end) + end) + end function c:init() return self end -- 2.43.0 From 772095431f1fc3e8709557a06c80b462f6cb2e08 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Tue, 20 Sep 2022 16:52:51 -0400 Subject: [PATCH 10/12] Some changes to extensions, working on STC --- changes.md | 4 +- multi/init.lua | 5 +- multi/integration/lanesManager/extensions.lua | 4 + multi/integration/lanesManager/init.lua | 1 + multi/integration/loveManager/extensions.lua | 6 +- multi/integration/lovrManager/extensions.lua | 6 +- test.lua | 74 +++++++++++++------ test2.lua | 33 +++++++++ 8 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 test2.lua diff --git a/changes.md b/changes.md index bd5bcae..5c5621b 100644 --- a/changes.md +++ b/changes.md @@ -21,6 +21,8 @@ Added Loops through the table and calls callback_function with each element of the array. +- If a name is not supplied when creating threads; a name is randomly generated. Unless sending through an established channel/queue you might not be able to easily init the object. + Changed --- - `Connection:[connect, hasConnections, getConnection]` changed to be `Connection:[Connect, HasConnections, getConnections]`. This was done in an attempt to follow a consistent naming scheme. The old methods still will work to prevent old code breaking. @@ -32,7 +34,7 @@ Removed Fixed --- -- +- SystemThreaded Objects variables weren't consistent. ToDo --- diff --git a/multi/init.lua b/multi/init.lua index fe2f078..e6caf78 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -1254,9 +1254,8 @@ local startme_len = 0 function thread:newThread(name,func,...) multi.OnLoad:Fire() -- This was done incase a threaded function was called before mainloop/uManager was called local func = func or name - - if type(name) == "function" then - name = "Thread#"..threadCount + if func == name then + name = name or multi.randomString(16) end local c={nil,nil,nil,nil,nil,nil,nil} local env = {self=c} diff --git a/multi/integration/lanesManager/extensions.lua b/multi/integration/lanesManager/extensions.lua index 15f5bb1..54b121a 100644 --- a/multi/integration/lanesManager/extensions.lua +++ b/multi/integration/lanesManager/extensions.lua @@ -24,7 +24,9 @@ SOFTWARE. local multi, thread = require("multi"):init() local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD function multi:newSystemThreadedQueue(name) + local name = name or multi.randomString(16) local c = {} + c.Name = name c.linda = lanes.linda() function c:push(v) self.linda:send("Q", v) @@ -43,8 +45,10 @@ function multi:newSystemThreadedQueue(name) end function multi:newSystemThreadedTable(name) + local name = name or multi.randomString(16) local c = {} c.link = lanes.linda() + c.Name = name setmetatable(c,{ __index = function(t,k) return c.link:get(k) diff --git a/multi/integration/lanesManager/init.lua b/multi/integration/lanesManager/init.lua index 4bada5a..b016699 100644 --- a/multi/integration/lanesManager/init.lua +++ b/multi/integration/lanesManager/init.lua @@ -62,6 +62,7 @@ function THREAD:newFunction(func,holdme) end function multi:newSystemThread(name, func, ...) + local name = name or multi.randomString(16) multi.InitSystemThreadErrorHandler() local rand = math.random(1, 10000000) local return_linda = lanes.linda() diff --git a/multi/integration/loveManager/extensions.lua b/multi/integration/loveManager/extensions.lua index cf6beb2..9d6fa7f 100644 --- a/multi/integration/loveManager/extensions.lua +++ b/multi/integration/loveManager/extensions.lua @@ -27,6 +27,7 @@ local multi, thread = require("multi").init() GLOBAL = multi.integration.GLOBAL THREAD = multi.integration.THREAD function multi:newSystemThreadedQueue(name) + local name = name or multi.randomString(16) local c = {} c.Name = name local fRef = {"func",nil} @@ -64,10 +65,11 @@ function multi:newSystemThreadedQueue(name) return c end function multi:newSystemThreadedTable(name) + local name = name or multi.randomString(16) local c = {} - c.name = name + c.Name = name function c:init() - return THREAD.createTable(self.name) + return THREAD.createTable(self.Name) end THREAD.package(name,c) return c diff --git a/multi/integration/lovrManager/extensions.lua b/multi/integration/lovrManager/extensions.lua index 7032b1d..b2082da 100644 --- a/multi/integration/lovrManager/extensions.lua +++ b/multi/integration/lovrManager/extensions.lua @@ -25,6 +25,7 @@ local multi, thread = require("multi").init() GLOBAL = multi.integration.GLOBAL THREAD = multi.integration.THREAD function multi:newSystemThreadedQueue(name) + local name = name or multi.randomString(16) local c = {} c.Name = name local fRef = {"func",nil} @@ -62,10 +63,11 @@ function multi:newSystemThreadedQueue(name) return c end function multi:newSystemThreadedTable(name) + local name = name or multi.randomString(16) local c = {} - c.name = name + c.Name = name function c:init() - return THREAD.createTable(self.name) + return THREAD.createTable(self.Name) end THREAD.package(name,c) return c diff --git a/test.lua b/test.lua index bba1341..87c2e00 100644 --- a/test.lua +++ b/test.lua @@ -3,59 +3,89 @@ package.cpath = "lua5.4/lib/lua/?/core.dll;"..package.cpath multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.lanesManager"):init() - - function multi:newSystemThreadedConnection(name,...) local c = {} c.CONN = 0x00 c.TRIG = 0x01 c.PING = 0x02 c.PONG = 0x03 - local proxy_conn = multi:newConnection(...) + c.proxy_conn = multi:newConnection(...) + local function remove(a, b) + local ai = {} + local r = {} + for k,v in pairs(a) do ai[v]=true end + for k,v in pairs(b) do + if ai[v]==nil then table.insert(r,a[k]) end + end + return r + end local name = name or multi.randomString(16) + c.Name = name local connections = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. local funcs = {} setmetatable(c,master_conn) -- A different approach will be taken for the non main connection objects - c.subscribe = multi:newSystemThreadedQueue("Subscribe_"..name):init() -- Incoming subscriptions - multi:newThread("STC_"..name,function() + c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..name):init() -- Incoming subscriptions + multi:newThread("STC_SUB_MAN"..name,function() + local item while true do thread.yield() - local item = c.subscribe:pop() -- We need to check on broken connections - -- c:Ping() - -- - if item ~= nil then - connections[#connections+1] = item - multi.ForEach(funcs, function(link) -- Sync new connections - item:push{c.CONN, link} - end) - thread.skip(multi.Priority_Normal) -- Usually a bunch of threads subscribe close to the same time. Process those by ensuring that they come alive around the same time - -- I'm using these "Constants" since they may change with other releases and this should allow these functions to adjust with them. - end + c:Ping() -- Should return instantlly and process this in another thread + thread.hold(function() + item = c.subscribe:peek() + if item ~= nil and item[1] == c.CONN then + c.subscribe:pop() + connections[#connections+1] = item + multi.ForEach(funcs, function(link) -- Sync new connections + item:push{c.CONN, link} + end) + end + -- Nil return keeps this hold going until timeout + end,{cycles=multi.Priority_Normal}) + -- Usually a bunch of threads subscribe close to the same time. + -- Give those threads some time to ready up. end end) - function c:Ping() -- Threaded Function call, can use thread.* - -- - end + c.Ping = thread:newFunction(function(self) + c.Ping:Pause() -- Don't allow this function to be called more than once + local pings = {} + multi.ForEach(funcs, function(link) -- Sync new connections + link:push{self.PING} + end) + thread.hold(function() + item = self.subscribe:peek() + if item ~= nil and item[1] == self.PONG then + table.insert(pings,item[2]) + end + end,{sleep=3}) -- Give all threads time to respond to the ping + -- We waited long enough for a response, anything that did not respond gets removed + remove(funcs, pings) + c.Ping:Resume() + end,false) function c:Fire(...) - -- + master_conn:Fire(...) + multi.ForEach(funcs, function(link) -- Sync new connections + link:push{self.TRIG,{...}} + end) end function c:Sync(link) -- end function c:Connect(func) local conn_func = func + self.proxy_conn(func) proxy_conn(function() funcs[#funcs+1] = func -- Used for syncing new connections to this connection later on - multi.ForEach(c.connections, function(link) + multi.ForEach(self.connections, function(link) link:push{CONN, func} end) end) end function c:init() + self.proxy_conn return self end - GLOBAL[name or "_"] = c + GLOBAL[name] = c return c end diff --git a/test2.lua b/test2.lua new file mode 100644 index 0000000..5603708 --- /dev/null +++ b/test2.lua @@ -0,0 +1,33 @@ +function difference(a, b) + local ai = {} + local r = {} + local rr = {} + for k,v in pairs(a) do r[k] = v; ai[v]=true end + for k,v in pairs(b) do + if ai[v]==nil then table.insert(rr,r[k]) end + end + return rr +end +function remove(a, b) + local ai = {} + local r = {} + for k,v in pairs(a) do ai[v]=true end + for k,v in pairs(b) do + if ai[v]==nil then table.insert(r,a[k]) end + end + return r +end + +function printtab(tab,msg) + print(msg or "TABLE") + for i,v in pairs(tab) do + print(i, v) + end + print("") +end + +local tab1 = {1,2,3,4,5} +local tab2 = {3,4,5,6,7} +tab1 = remove(tab1,tab2) +printtab(tab1, "Table 1") +printtab(tab2, "Table 2") -- 2.43.0 From 62e05788d2a3d3bba600b973b659b706d7affbdb Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Wed, 21 Sep 2022 00:06:48 -0400 Subject: [PATCH 11/12] Working on STC --- makeENV.lua | 10 ++-- multi/init.lua | 2 +- test.lua | 142 +++++++++++++++++++++++++++++++------------------ 3 files changed, 96 insertions(+), 58 deletions(-) diff --git a/makeENV.lua b/makeENV.lua index acb6fc0..d312349 100644 --- a/makeENV.lua +++ b/makeENV.lua @@ -1,9 +1,9 @@ commands = [[ -mkdir luajit && python -m hererocks -j 2.1.0-beta3 -r latest --patch --compat all ./luajit && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.1 && python -m hererocks -l 5.1 -r latest --patch --compat all ./lua5.1 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.2 && python -m hererocks -l 5.2 -r latest --patch --compat all ./lua5.2 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.3 && python -m hererocks -l 5.3 -r latest --patch --compat all ./lua5.3 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.4 && python -m hererocks -l 5.4 -r latest --patch --compat all ./lua5.4 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi +mkdir luajit && python -m hererocks -j 2.1.0-beta3 -r latest --patch --compat all ./luajit && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi && luarocks install lanes +mkdir lua5.1 && python -m hererocks -l 5.1 -r latest --patch --compat all ./lua5.1 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi && luarocks install lanes +mkdir lua5.2 && python -m hererocks -l 5.2 -r latest --patch --compat all ./lua5.2 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi && luarocks install lanes +mkdir lua5.3 && python -m hererocks -l 5.3 -r latest --patch --compat all ./lua5.3 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi && luarocks install lanes +mkdir lua5.4 && python -m hererocks -l 5.4 -r latest --patch --compat all ./lua5.4 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi && luarocks install lanes ]] function string.split (inputstr, sep) local sep = sep or "\n" diff --git a/multi/init.lua b/multi/init.lua index e6caf78..63e3bbc 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -328,7 +328,7 @@ function multi:newConnection(protect,func,kill) conn_helper = func end - c.Connect=c.connect + c.connect=c.Connect c.GetConnection=c.getConnection c.HasConnections = c.hasConnections c.GetConnection = c.getConnection diff --git a/test.lua b/test.lua index 87c2e00..d7bcd66 100644 --- a/test.lua +++ b/test.lua @@ -1,15 +1,15 @@ -package.path = "./?/init.lua;?.lua;lua5.4/share/lua/?/init.lua;lua5.4/share/lua/?.lua;"..package.path -package.cpath = "lua5.4/lib/lua/?/core.dll;"..package.cpath +package.path = "./?/init.lua;?.lua;lua5.2/share/lua/5.2/?/init.lua;lua5.2/share/lua/5.2/?.lua;" +package.cpath = "lua5.2/lib/lua/5.2/?/core.dll;" multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.lanesManager"):init() -function multi:newSystemThreadedConnection(name,...) +function multi:newSystemThreadedConnection(name) + local name = name or multi.randomString(16) local c = {} c.CONN = 0x00 c.TRIG = 0x01 c.PING = 0x02 c.PONG = 0x03 - c.proxy_conn = multi:newConnection(...) local function remove(a, b) local ai = {} local r = {} @@ -19,37 +19,14 @@ function multi:newSystemThreadedConnection(name,...) end return r end - local name = name or multi.randomString(16) c.Name = name - local connections = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. - local funcs = {} - setmetatable(c,master_conn) -- A different approach will be taken for the non main connection objects - c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..name):init() -- Incoming subscriptions - multi:newThread("STC_SUB_MAN"..name,function() - local item - while true do - thread.yield() - -- We need to check on broken connections - c:Ping() -- Should return instantlly and process this in another thread - thread.hold(function() - item = c.subscribe:peek() - if item ~= nil and item[1] == c.CONN then - c.subscribe:pop() - connections[#connections+1] = item - multi.ForEach(funcs, function(link) -- Sync new connections - item:push{c.CONN, link} - end) - end - -- Nil return keeps this hold going until timeout - end,{cycles=multi.Priority_Normal}) - -- Usually a bunch of threads subscribe close to the same time. - -- Give those threads some time to ready up. - end - end) - c.Ping = thread:newFunction(function(self) - c.Ping:Pause() -- Don't allow this function to be called more than once + c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. + local ping + ping = thread:newFunction(function(self) + ping:Pause() -- Don't allow this function to be called until the first instance is done local pings = {} - multi.ForEach(funcs, function(link) -- Sync new connections + local count = #self.links + multi.ForEach(self.links, function(link) -- Sync new connections link:push{self.PING} end) thread.hold(function() @@ -57,36 +34,97 @@ function multi:newSystemThreadedConnection(name,...) if item ~= nil and item[1] == self.PONG then table.insert(pings,item[2]) end + if #pings==count then + return true -- If we get all pings give control back + end end,{sleep=3}) -- Give all threads time to respond to the ping -- We waited long enough for a response, anything that did not respond gets removed - remove(funcs, pings) - c.Ping:Resume() + self.links = remove(self.links, pings) + ping:Resume() end,false) + + thread:newThread("STC_SUB_MAN"..name,function() + local item + while true do + thread.yield() + -- We need to check on broken connections + ping() -- Should return instantlly and process this in another thread + thread.hold(function() + item = c.subscribe:peek() + if item ~= nil and item[1] == c.CONN then + c.subscribe:pop() + multi.ForEach(c.links, function(link) -- Sync new connections + item[2]:push{c.CONN, link} + end) + c.links[#c.links+1] = item + end + -- Nil return keeps this hold going until timeout + end,{cycles=multi.Priority_Normal}) + -- Usually a bunch of threads subscribe close to the same time. + end + end) + function c:Fire(...) - master_conn:Fire(...) - multi.ForEach(funcs, function(link) -- Sync new connections - link:push{self.TRIG,{...}} - end) - end - function c:Sync(link) - -- - end - function c:Connect(func) - local conn_func = func - self.proxy_conn(func) - proxy_conn(function() - funcs[#funcs+1] = func -- Used for syncing new connections to this connection later on - multi.ForEach(self.connections, function(link) - link:push{CONN, func} - end) + self.proxy_conn:Fire(...) + local args = {...} + multi.ForEach(self.links, function(link) -- Sync new connections + print(link[2]) -- Bugs everywhere + for i,v in pairs(link[2]) do print(i,v) end + link[2]:push{self.TRIG, args} end) end + function c:init() - self.proxy_conn + self.links = {} + self.proxy_conn = multi:newConnection() + setmetatable(self, {__index = self.proxy_conn}) + self.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init() -- Incoming subscriptions + thread:newThread("STC_CONN_MAN"..name,function() + local item + local link_self_ref = multi:newSystemThreadedQueue() + self.subscribe:push{self.CONN,link_self_ref} + while true do + item = thread.hold(function() + return self.subscribe:peek() + end) + if item[1] == self.PING then + self.subscribe:push{self.PONG, link_self_ref} + elseif item[1] == self.CONN then + if item[2] ~= link_self_ref then + table.insert(self.links, item[2]) + end + elseif item[1] == self.TRIG then + self.proxy_conn:Fire(unpack(item[2])) + else + -- This shouldn't be the case + end + end + end) return self end + GLOBAL[name] = c + return c end +local conn = multi:newSystemThreadedConnection("conn"):init() +multi:newSystemThread("Test",function() + local multi, thread = require("multi"):init() + local conn = THREAD.waitFor("conn"):init() + conn(function() + print("Thread was triggered!") + end) + multi:mainloop() +end) +-- conn(function() +-- print("Mainloop conn got triggered!") +-- end) + +alarm = multi:newAlarm(1) +alarm:OnRing(function() + print("Ring") + conn:Fire() +end) + multi:mainloop() \ No newline at end of file -- 2.43.0 From 7bbb217018d6f2fd2ef5d93116df4b0dee41191c Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Wed, 21 Sep 2022 23:16:38 -0400 Subject: [PATCH 12/12] Working with lanes, todo implement for love2d and test. 'Should work' the way it is with love2d --- changes.md | 74 +++++++- multi/integration/lanesManager/extensions.lua | 125 ++++++++++++- multi/integration/lanesManager/init.lua | 1 + test.lua | 170 ++++++------------ 4 files changed, 253 insertions(+), 117 deletions(-) diff --git a/changes.md b/changes.md index 5c5621b..d6cef4f 100644 --- a/changes.md +++ b/changes.md @@ -7,12 +7,84 @@ Table of contents # Update 15.3.0 - A world of connection Full Update Showcase +```lua +multi, thread = require("multi"):init{print=true} +GLOBAL, THREAD = require("multi.integration.lanesManager"):init() + +local conn = multi:newSystemThreadedConnection("conn"):init() + +multi:newSystemThread("Thread_Test_1",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + conn(function() + print(THREAD:getName().." was triggered!") + end) + multi:mainloop() +end) + +multi:newSystemThread("Thread_Test_2",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + conn(function(a,b,c) + print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + print("Fire 2!!!") + conn:Fire(4,5,6) + THREAD.kill() + end) + + multi:mainloop() +end) + +conn(function(a,b,c) + print("Mainloop conn got triggered!",a,b,c) +end) + +alarm = multi:newAlarm(1) +alarm:OnRing(function() + 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() + conn(function(a,b,c) + print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + 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() + multi:newAlarm(2):OnRing(function() + conn2:Fire() + end) + multi:newThread(function() + print("Conn Test!") + thread.hold(conn + conn2) + print("It held!") + end) + multi:mainloop() +end) + +multi:mainloop() +``` Added --- - `multi:newSystemThreadedConnection()` - Allows one to trigger connection events across threads. + Allows one to trigger connection events across threads. Works like how any connection would work. Supports all of the features, can even be `added` with non SystemThreadedConnections as demonstrated in the full showcase. - `multi:newConnection():SetHelper(func)` Sets the helper function that the connection object uses when creating connection links. diff --git a/multi/integration/lanesManager/extensions.lua b/multi/integration/lanesManager/extensions.lua index 54b121a..e0add79 100644 --- a/multi/integration/lanesManager/extensions.lua +++ b/multi/integration/lanesManager/extensions.lua @@ -22,7 +22,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] local multi, thread = require("multi"):init() -local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD +if not (GLOBAL and THREAD) then + local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD +else + lanes = require("lanes") +end function multi:newSystemThreadedQueue(name) local name = name or multi.randomString(16) local c = {} @@ -174,4 +178,121 @@ function multi:newSystemThreadedJobQueue(n) end,i).priority = thread.Priority_Core end return c -end \ No newline at end of file +end + +function multi:newSystemThreadedConnection(name) + local name = name or multi.randomString(16) + local c = {} + c.CONN = 0x00 + c.TRIG = 0x01 + c.PING = 0x02 + c.PONG = 0x03 + local function remove(a, b) + local ai = {} + local r = {} + for k,v in pairs(a) do ai[v]=true end + for k,v in pairs(b) do + if ai[v]==nil then table.insert(r,a[k]) end + end + return r + end + c.CID = THREAD.getID() + c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init() + c.Name = name + c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. + -- Locals will only live in the thread that creates the original object + local ping + local pong = function(link, links) + local res = thread.hold(function() + return link:peek()[1] == c.PONG + end,{sleep=3}) + + if not res then + self.links = remove(self.links, pings) + else + link:pop() + end + end + + ping = thread:newFunction(function(self) + ping:Pause() + multi.ForEach(self.links, function(link) -- Sync new connections + link:push{self.PING} + multi:newThread("pong Thread", pong, link, links) + end) + + thread.sleep(3) + + ping:Resume() + end,false) + + thread:newThread("STC_SUB_MAN"..name,function() + local item + while true do + thread.yield() + -- We need to check on broken connections + ping(c) -- Should return instantlly and process this in another thread + item = thread.hold(function() -- This will keep things held up until there is something to process + return c.subscribe:pop() + end) + if item[1] == c.CONN then + multi.ForEach(c.links, function(link) -- Sync new connections + item[2]:push{c.CONN, link} + end) + c.links[#c.links+1] = item[2] + elseif item[1] == c.TRIG then + c:Fire(unpack(item[2])) + c.proxy_conn:Fire(unpack(item[2])) + end + end + end) + --- ^^^ This will only exist in the init thread + + function c:Fire(...) + local args = {...} + if self.CID == THREAD.getID() then -- Host Call + for _, link in pairs(self.links) do + link:push {self.TRIG, args} + end + else + self.subscribe:push {self.TRIG, args} + end + end + + function c:init() + local multi, thread = require("multi"):init() + self.links = {} + self.proxy_conn = multi:newConnection() + local mt = getmetatable(self.proxy_conn) + setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add}) + thread:newThread("STC_CONN_MAN"..name,function() + local item + local link_self_ref = multi:newSystemThreadedQueue() + self.subscribe:push{self.CONN, link_self_ref} + while true do + item = thread.hold(function() + return link_self_ref:peek() + end) + if item[1] == self.PING then + link_self_ref:push{self.PONG} + link_self_ref:pop() + elseif item[1] == self.CONN then + if item[2].Name ~= link_self_ref.Name then + table.insert(self.links, item[2]) + end + link_self_ref:pop() + elseif item[1] == self.TRIG then + self.proxy_conn:Fire(unpack(item[2])) + link_self_ref:pop() + else + -- This shouldn't be the case + end + end + end) + return self + end + + GLOBAL[name] = c + + return c +end \ No newline at end of file diff --git a/multi/integration/lanesManager/init.lua b/multi/integration/lanesManager/init.lua index b016699..3653b55 100644 --- a/multi/integration/lanesManager/init.lua +++ b/multi/integration/lanesManager/init.lua @@ -88,6 +88,7 @@ function multi:newSystemThread(name, func, ...) }, priority=c.priority },function(...) + require("multi.integration.lanesManager.extensions") local has_error = true return_linda:set("returns",{func(...)}) has_error = false diff --git a/test.lua b/test.lua index d7bcd66..5fdd687 100644 --- a/test.lua +++ b/test.lua @@ -3,128 +3,70 @@ package.cpath = "lua5.2/lib/lua/5.2/?/core.dll;" multi, thread = require("multi"):init{print=true} GLOBAL, THREAD = require("multi.integration.lanesManager"):init() -function multi:newSystemThreadedConnection(name) - local name = name or multi.randomString(16) - local c = {} - c.CONN = 0x00 - c.TRIG = 0x01 - c.PING = 0x02 - c.PONG = 0x03 - local function remove(a, b) - local ai = {} - local r = {} - for k,v in pairs(a) do ai[v]=true end - for k,v in pairs(b) do - if ai[v]==nil then table.insert(r,a[k]) end - end - return r - end - c.Name = name - c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. - local ping - ping = thread:newFunction(function(self) - ping:Pause() -- Don't allow this function to be called until the first instance is done - local pings = {} - local count = #self.links - multi.ForEach(self.links, function(link) -- Sync new connections - link:push{self.PING} - end) - thread.hold(function() - item = self.subscribe:peek() - if item ~= nil and item[1] == self.PONG then - table.insert(pings,item[2]) - end - if #pings==count then - return true -- If we get all pings give control back - end - end,{sleep=3}) -- Give all threads time to respond to the ping - -- We waited long enough for a response, anything that did not respond gets removed - self.links = remove(self.links, pings) - ping:Resume() - end,false) - - thread:newThread("STC_SUB_MAN"..name,function() - local item - while true do - thread.yield() - -- We need to check on broken connections - ping() -- Should return instantlly and process this in another thread - thread.hold(function() - item = c.subscribe:peek() - if item ~= nil and item[1] == c.CONN then - c.subscribe:pop() - multi.ForEach(c.links, function(link) -- Sync new connections - item[2]:push{c.CONN, link} - end) - c.links[#c.links+1] = item - end - -- Nil return keeps this hold going until timeout - end,{cycles=multi.Priority_Normal}) - -- Usually a bunch of threads subscribe close to the same time. - end - end) - - function c:Fire(...) - self.proxy_conn:Fire(...) - local args = {...} - multi.ForEach(self.links, function(link) -- Sync new connections - print(link[2]) -- Bugs everywhere - for i,v in pairs(link[2]) do print(i,v) end - link[2]:push{self.TRIG, args} - end) - end - - function c:init() - self.links = {} - self.proxy_conn = multi:newConnection() - setmetatable(self, {__index = self.proxy_conn}) - self.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init() -- Incoming subscriptions - thread:newThread("STC_CONN_MAN"..name,function() - local item - local link_self_ref = multi:newSystemThreadedQueue() - self.subscribe:push{self.CONN,link_self_ref} - while true do - item = thread.hold(function() - return self.subscribe:peek() - end) - if item[1] == self.PING then - self.subscribe:push{self.PONG, link_self_ref} - elseif item[1] == self.CONN then - if item[2] ~= link_self_ref then - table.insert(self.links, item[2]) - end - elseif item[1] == self.TRIG then - self.proxy_conn:Fire(unpack(item[2])) - else - -- This shouldn't be the case - end - end - end) - return self - end - - GLOBAL[name] = c - - return c -end - local conn = multi:newSystemThreadedConnection("conn"):init() -multi:newSystemThread("Test",function() + +multi:newSystemThread("Thread_Test_1",function() local multi, thread = require("multi"):init() - local conn = THREAD.waitFor("conn"):init() + local conn = GLOBAL["conn"]:init() conn(function() - print("Thread was triggered!") + print(THREAD:getName().." was triggered!") end) multi:mainloop() end) --- conn(function() --- print("Mainloop conn got triggered!") --- end) + +multi:newSystemThread("Thread_Test_2",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + conn(function(a,b,c) + print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + print("Fire 2!!!") + conn:Fire(4,5,6) + THREAD.kill() + end) + + multi:mainloop() +end) + +conn(function(a,b,c) + print("Mainloop conn got triggered!",a,b,c) +end) alarm = multi:newAlarm(1) -alarm:OnRing(function() - print("Ring") - conn:Fire() +alarm:OnRing(function() + 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() + conn(function(a,b,c) + print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + 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() + multi:newAlarm(2):OnRing(function() + conn2:Fire() + end) + multi:newThread(function() + print("Conn Test!") + thread.hold(conn + conn2) + print("It held!") + end) + multi:mainloop() end) multi:mainloop() \ No newline at end of file -- 2.43.0