From 257ed03728d1f33ebf1c0469d8d963534702f66f Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 3 Jun 2023 12:07:33 -0400 Subject: [PATCH] THREAD_NAME set for main thread, connections break the rules for proxies --- docs/changes.md | 67 +++++++++++++++++++++++++-- init.lua | 1 + integration/lanesManager/init.lua | 3 ++ integration/loveManager/init.lua | 18 ++++--- integration/lovrManager/init.lua | 2 + integration/pseudoManager/init.lua | 3 ++ integration/sharedExtensions/init.lua | 17 +++++-- 7 files changed, 93 insertions(+), 18 deletions(-) diff --git a/docs/changes.md b/docs/changes.md index 24bd809..44a30ca 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -138,7 +138,7 @@ Added alarm = stp:newAlarm(3) - alarm.OnRing:Connect(function(alarm) + alarm._OnRing:Connect(function(alarm) print("Hmm...", THREAD_NAME) end) ``` @@ -148,9 +148,70 @@ Added ``` Internally the SystemThreadedProcessor uses a JobQueue to handle things. The proxy function allows you to interact with these objects as if they were on the main thread, though there actions are carried out on the main thread. - There are currently limitations to proxies. Connection proxy do not receive events on the non thread side. So connection metamethods do not work! thread.hold(proxy.conn) does work! The backend to get this to work was annoying :P + Connection proxies break the rules a bit. Normally methods should always work on the thread side, however for connections in order to have actions work on the thread side you would call the connection using `obj._connName` instead of calling `obj.connName`. This allows you to have more control over connection events. See example below: + ```lua + package.path = "?/init.lua;?.lua;"..package.path - This event is subscribed to on the proxy threads side of things! + multi, thread = require("multi"):init({print=true}) + THREAD, GLOBAL = require("multi.integration.lanesManager"):init() + + stp = multi:newSystemThreadedProcessor(8) + + alarm = stp:newAlarm(3) + + -- This doesn't work since this event has already been subscribed to internally on the thread to get thread.hold(alarm.OnRing) to work. But as many events to alarm.OnRing can be made! + thread:newThread(function() + print("Hold on proxied connection", thread.hold(alarm._OnRing)) + end) + + alarm.OnRing(function(a) + print("OnRing",a, THREAD_NAME, THREAD_ID) + end) + + print("alarm.OnRing", alarm.OnRing.Type) + print("alarm._OnRing", alarm._OnRing.Type) + + thread:newThread(function() + print("Hold on proxied no proxy connection", thread.hold(alarm.OnRing)) + end) + + thread:newThread(function() + print("Hold on proxied no proxy connection", thread.hold(alarm.OnRing)) + end) + + -- This doesn't work since this event has already been subscribed to internally on the thread to get thread.hold(alarm.OnRing) to work. But as many events to alarm.OnRing can be made! + thread:newThread(function() + print("Hold on proxied connection", thread.hold(alarm._OnRing)) + end) + + alarm._OnRing(function(a) + print("_OnRing",a, THREAD_NAME, THREAD_ID) + a:Reset(1) + end) + + multi:mainloop() + ``` + Output: + ``` + INFO: Integrated Lanes Threading! + alarm.OnRing connector + alarm._OnRing proxy + _OnRing table: 025EB128 STJQ_cjKsEZHg 1 <-- This can change each time you run this example! + OnRing table: 018BC0C0 MAIN_THREAD 0 + Hold on proxied no proxy connection table: 018BC0C0 nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil + Hold on proxied no proxy connection table: 018BC0C0 nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil + _OnRing table: 025EB128 STJQ_cjKsEZHg 1 + OnRing table: 018BC0C0 MAIN_THREAD 0 + _OnRing table: 025EB128 STJQ_cjKsEZHg 1 + OnRing table: 018BC0C0 MAIN_THREAD 0 + + ... (Will repeat ever second now) + _OnRing table: 025EB128 STJQ_cjKsEZHg 1 + + OnRing table: 018BC0C0 MAIN_THREAD 0 + ``` + + The proxy version can only subscribe to events on the proxy thread, which means that connection metamethods will not work with the proxy version (`_OnRing` on the non proxy thread side), but the (`OnRing`) version will work. Cleverly handling the proxy thread and the non proxy thread will allow powerful connection logic. Also this is not a full system threaded connection. **Proxies should only be used between 2 threads!** To keep things fast I'm using simple queues to transfer data. There is no guarantee that things will work! Currently supporting: - proxyLoop = STP:newLoop(...) diff --git a/init.lua b/init.lua index 2398697..881f832 100644 --- a/init.lua +++ b/init.lua @@ -80,6 +80,7 @@ multi.STEP = "step" multi.TSTEP = "tstep" multi.THREAD = "thread" multi.SERVICE = "service" +multi.PROXY = "proxy" if not _G["$multi"] then _G["$multi"] = {multi = multi, thread = thread} diff --git a/integration/lanesManager/init.lua b/integration/lanesManager/init.lua index 8e9fe59..547c7c6 100644 --- a/integration/lanesManager/init.lua +++ b/integration/lanesManager/init.lua @@ -36,6 +36,9 @@ lanes = require("lanes").configure() multi.SystemThreads = {} multi.isMainThread = true +_G.THREAD_NAME = "MAIN_THREAD" +_G.THREAD_ID = 0 + function multi:canSystemThread() return true end diff --git a/integration/loveManager/init.lua b/integration/loveManager/init.lua index 5300a92..7c5289f 100644 --- a/integration/loveManager/init.lua +++ b/integration/loveManager/init.lua @@ -32,15 +32,13 @@ THREAD = require("multi.integration.loveManager.threads") sThread = THREAD __IMPORTS = {...} __FUNC__=table.remove(__IMPORTS,1) -__THREADID__=table.remove(__IMPORTS,1) -__THREADNAME__=table.remove(__IMPORTS,1) -THREAD_NAME = __THREADNAME__ -THREAD_ID = __THREADID__ -math.randomseed(__THREADID__) +THREAD_ID=table.remove(__IMPORTS,1) +THREAD_NAME=table.remove(__IMPORTS,1) +math.randomseed(THREAD_ID) math.random() math.random() math.random() -stab = THREAD.createStaticTable(__THREADNAME__ .. __THREADID__) +stab = THREAD.createStaticTable(THREAD_NAME .. THREAD_ID) GLOBAL = THREAD.getGlobal() if GLOBAL["__env"] then local env = THREAD.unpackENV(GLOBAL["__env"]) @@ -60,15 +58,15 @@ stab["returns"] = {THREAD.loadDump(__FUNC__)(multi.unpack(__IMPORTS))} local multi, thread = require("multi"):init() local THREAD = {} -__THREADID__ = 0 -__THREADNAME__ = "MainThread" +_G.THREAD_NAME = "MAIN_THREAD" +_G.THREAD_ID = 0 multi.integration = {} local THREAD = require("multi.integration.loveManager.threads") local GLOBAL = THREAD.getGlobal() -local THREAD_ID = 1 multi.isMainThread = true function multi:newSystemThread(name, func, ...) + THREAD_ID = THREAD_ID + 1 local c = {} c.name = name c.ID = THREAD_ID @@ -79,7 +77,7 @@ function multi:newSystemThread(name, func, ...) c.OnError = multi:newConnection() GLOBAL["__THREAD_" .. c.ID] = {ID = c.ID, Name = c.name, Thread = c.thread} GLOBAL["__THREAD_COUNT"] = THREAD_ID - THREAD_ID = THREAD_ID + 1 + function c:getName() return c.name end thread:newThread(name .. "_System_Thread_Handler",function() if name == "SystemThreaded Function Handler" then diff --git a/integration/lovrManager/init.lua b/integration/lovrManager/init.lua index d09b2d9..6a64c98 100644 --- a/integration/lovrManager/init.lua +++ b/integration/lovrManager/init.lua @@ -42,6 +42,8 @@ local multi, thread = require("multi.compat.lovr2d"):init() local THREAD = {} __THREADID__ = 0 __THREADNAME__ = "MainThread" +_G.THREAD_NAME = "MAIN_THREAD" +_G.THREAD_ID = 0 multi.integration={} multi.integration.lovr2d={} local THREAD = require("multi.integration.lovrManager.threads") diff --git a/integration/pseudoManager/init.lua b/integration/pseudoManager/init.lua index 30879f3..490cff9 100644 --- a/integration/pseudoManager/init.lua +++ b/integration/pseudoManager/init.lua @@ -35,6 +35,9 @@ multi.isMainThread = true local activator = require("multi.integration.pseudoManager.threads") local GLOBAL, THREAD = activator.init(thread) +_G.THREAD_NAME = "MAIN_THREAD" +_G.THREAD_ID = 0 + function multi:canSystemThread() -- We are emulating system threading return true end diff --git a/integration/sharedExtensions/init.lua b/integration/sharedExtensions/init.lua index bbd3691..33ab91c 100644 --- a/integration/sharedExtensions/init.lua +++ b/integration/sharedExtensions/init.lua @@ -67,7 +67,7 @@ function multi:newProxy(list) function c:init() local multi, thread = nil, nil - if THREAD_NAME then + if THREAD_ID>0 then local multi, thread = require("multi"):init() local function check() return self.send:pop() @@ -113,11 +113,19 @@ function multi:newProxy(list) self.Type = multi.PROXY for _,v in pairs(self.funcs) do if type(v) == "table" then + -- We have a connection v[2]:init() - self[v[1]] = v[2] + self["_"..v[1]] = v[2] v[2].Parent = self + setmetatable(v[2],getmetatable(multi:newConnection())) + self[v[1]] = multi:newConnection() + + thread:newThread(function() + while true do + self[v[1]]:Fire(thread.hold(alarm["_"..v[1]])) + end + end) else - lastObj = self self[v] = thread:newFunction(function(self,...) if self == me then me.send:push({v, true, ...}) @@ -146,8 +154,6 @@ function multi:newProxy(list) return c end -multi.PROXY = "proxy" - local targets = {} local nFunc = 0 @@ -416,6 +422,7 @@ end -- Modify thread.hold to handle proxies local thread_ref = thread.hold function thread.hold(n, opt) + --if type(n) == "table" then print(n.Type, n.isConnection()) end if type(n) == "table" and n.Type == multi.PROXY and n.isConnection() then local ready = false local args