From afdd98ab13c03c6ac663d193181dc29f7cd9dcdd Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 15 Jul 2023 01:16:03 -0400 Subject: [PATCH] Rewriting love2d threading binding --- init.lua | 15 ++-- integration/lanesManager/init.lua | 13 ++-- integration/loveManager/init.lua | 116 +++++++++++++++++++++++++++- integration/loveManager/threads.lua | 29 ++++--- lovethreads/main.lua | 26 ++++++- 5 files changed, 164 insertions(+), 35 deletions(-) diff --git a/init.lua b/init.lua index 7c79a4a..59172fe 100644 --- a/init.lua +++ b/init.lua @@ -1630,14 +1630,11 @@ function thread:newThread(name, func, ...) c.Destroy = c.Kill if thread.isThread() then - multi:newLoop(function(loop) - if self.Type == multi.PROCESS then - table.insert(self.startme, c) - else - table.insert(threadManager.startme, c) - end - loop:Break() - end) + if self.Type == multi.PROCESS then + table.insert(self.startme, c) + else + table.insert(threadManager.startme, c) + end else if self.Type == multi.PROCESS then table.insert(self.startme, c) @@ -2411,8 +2408,8 @@ function multi.error(self, err) else io.write("\x1b[91mERROR:\x1b[0m " .. err .. " ?\n") end - error("^^^ " .. multi:getCurrentProcess():getFullName() .. " " .. multi:getCurrentTask().Type .. "\n" .. debug.traceback().."\n") if multi.defaultSettings.error then + error("^^^ " .. multi:getCurrentProcess():getFullName() .. " " .. multi:getCurrentTask().Type .. "\n" .. debug.traceback().."\n") os.exit(1) end end diff --git a/integration/lanesManager/init.lua b/integration/lanesManager/init.lua index 014294e..dbf7eb3 100644 --- a/integration/lanesManager/init.lua +++ b/integration/lanesManager/init.lua @@ -70,9 +70,8 @@ function multi:newSystemThread(name, func, ...) local rand = math.random(1, 10000000) local return_linda = lanes.linda() c = {} - c.name = name c.Name = name - c.Id = count + c.ID = count c.loadString = {"base","package","os","io","math","table","string","coroutine"} livingThreads[count] = {true, name} c.returns = return_linda @@ -162,13 +161,13 @@ function multi.InitSystemThreadErrorHandler() for i = #threads, 1, -1 do temp = threads[i] status = temp.thread.status - push = __StatusLinda:get(temp.Id) + push = __StatusLinda:get(temp.ID) if push then - temp.statusconnector:Fire(multi.unpack(({__StatusLinda:receive(nil, temp.Id)})[2])) + temp.statusconnector:Fire(multi.unpack(({__StatusLinda:receive(nil, temp.ID)})[2])) end if status == "done" or temp.returns:get("returns") then returns = ({temp.returns:receive(0, "returns")})[2] - livingThreads[temp.Id] = {false, temp.Name} + livingThreads[temp.ID] = {false, temp.Name} temp.alive = false if returns[1] == false then temp.OnError:Fire(temp, returns[2]) @@ -185,13 +184,13 @@ function multi.InitSystemThreadErrorHandler() elseif status == "error" then -- The thread never really errors, we handle this through our linda object elseif status == "cancelled" then - livingThreads[temp.Id] = {false, temp.Name} + livingThreads[temp.ID] = {false, temp.Name} temp.alive = false temp.OnError:Fire(temp,"thread_cancelled") GLOBAL["__THREADS__"] = livingThreads table.remove(threads, i) elseif status == "killed" then - livingThreads[temp.Id] = {false, temp.Name} + livingThreads[temp.ID] = {false, temp.Name} temp.alive = false temp.OnError:Fire(temp,"thread_killed") GLOBAL["__THREADS__"] = livingThreads diff --git a/integration/loveManager/init.lua b/integration/loveManager/init.lua index 3177adc..849249a 100644 --- a/integration/loveManager/init.lua +++ b/integration/loveManager/init.lua @@ -1,8 +1,122 @@ +if ISTHREAD then + error("You cannot require the loveManager from within a thread!") +end + +local ThreadFileData = [[ +ISTHREAD = true +__FUNC, THREAD_ID, THREAD_NAME, __PACK = ... +GLOBAL, THREAD = require("multi.integration.loveManager.threads"):init() +__FUNC = THREAD.unpackValue(__FUNC) +__PACK = THREAD.unpackValue(__PACK) +math.randomseed(THREAD_ID) +math.random() +math.random() +math.random() +stab = THREAD.createTable(THREAD_NAME .. THREAD_ID) +if GLOBAL["__env"] then + local env = THREAD.unpackENV(GLOBAL["__env"]) + for i,v in pairs(env) do + _G[i] = v + end +end +multi, thread = require("multi"):init() +require("multi.integration.loveManager.extensions") +require("multi.integration.sharedExtensions") +stab["returns"] = {__FUNC(multi.unpack(__PACK))} +]] + +_G.THREAD_NAME = "MAIN_THREAD" +_G.THREAD_ID = 0 + +local multi, thread = require("multi"):init() local GLOBAL, THREAD = require("multi.integration.loveManager.threads"):init() +multi.integration = {} +multi.isMainThread = true +local threads = {} +local tid = 0 +function multi:newSystemThread(name, func, ...) + multi.InitSystemThreadErrorHandler() + local name = name or multi.randomString(16) + tid = tid + 1 + local c = {} + c.Type = multi.STHREAD + c.Name = name + c.ID = tid + c.thread = love.thread.newThread(ThreadFileData) + c.thread:start(THREAD.packValue(func), c.ID, c.Name, THREAD.packValue({...})) + c.stab = THREAD.createTable(name .. c.ID) + c.creationTime = os.clock() + c.OnDeath = multi:newConnection() + c.OnError = multi:newConnection() + c.status_channel = love.thread.getChannel("__status_channel__" .. c.ID) + + function c:getName() return c.name end + + table.insert(threads, c) + + if self.isActor then + self:create(c) + else + multi.create(multi, c) + end + + return c +end + +local started = false +local console_channel = love.thread.getChannel("__console_channel__") + +function THREAD:newFunction(func, holdme) + return thread:newFunctionBase(function(...) + return multi:newSystemThread("SystemThreaded Function Handler", func, ...) + end, holdme, multi.SFUNCTION)() +end + +function love.threaderror(thread, errorstr) + multi.error("Thread error! " .. errorstr) +end + +function multi.InitSystemThreadErrorHandler() + if started == true then return end + started = true + thread:newThread("Love System Thread Handler", function() + while true do + thread.yield() + for i = #threads, 1, -1 do + local th = threads[i] + if th.status_channel:peek() ~= nil then + th.statusconnector:Fire(multi.unpack(th.status_channel:pop())) + end + local th_err = th.thread:getError() + if th_err == "Thread Killed!\1" then + th.OnDeath:Fire("Thread Killed!") + table.remove(threads, i) + elseif th_err then + th.OnError:Fire(th, th_err) + table.remove(threads, i) + elseif th.stab.returns then + th.OnDeath:Fire(multi.unpack(th.stab.returns)) + th.stab.returns = nil + table.remove(threads, i) + end + end + end + end) +end + +THREAD.newSystemThread = function(...) + multi:newSystemThread(...) +end + +multi.integration.GLOBAL = GLOBAL +multi.integration.THREAD = THREAD +require("multi.integration.loveManager.extensions") +require("multi.integration.sharedExtensions") +multi.print("Integrated Love Threading!") return { - init = function(global_channel, console_channel, status_channel) + init = function() return GLOBAL, THREAD end } diff --git a/integration/loveManager/threads.lua b/integration/loveManager/threads.lua index eedcc02..e7c049e 100644 --- a/integration/loveManager/threads.lua +++ b/integration/loveManager/threads.lua @@ -65,7 +65,7 @@ local function unpackValue(d) if not status then multi.error(data) end - return serpent.load(data:sub(2,-1))[1] + return data[1] else return d end @@ -101,8 +101,10 @@ local function createTable(n) ) end -function INIT(global_channel, console_channel, status_channel) - local GLOBAL, THREAD = createTable("GLOBAL"), {} +function INIT() + local GLOBAL, THREAD = createTable("__GLOBAL__"), {} + local status_channel, console_channel = love.thread.getChannel("__status_channel__" .. THREAD_ID), + love.thread.getChannel("__console_channel__") -- Non portable methods, shouldn't be used unless you know what you are doing THREAD.packValue = packValue @@ -117,16 +119,16 @@ function INIT(global_channel, console_channel, status_channel) return GLOBAL[name] end - function THREAD.waitFor(name) + THREAD.waitFor = thread:newFunction(function(name) local function wait() math.randomseed(os.time()) - love.timer.sleep(.001) + thread.yield() end repeat wait() until GLOBAL[name] return GLOBAL[name] - end + end, true) function THREAD.getCores() return love.system.getProcessorCount() @@ -154,18 +156,16 @@ function INIT(global_channel, console_channel, status_channel) end function THREAD.pushStatus(...) - status_channel:push({THREAD_ID, multi.pack(...)}) + status_channel:push(multi.pack(...)) end - _G.THREAD_ID = 0 - function THREAD.sleep(n) love.timer.sleep(n) end - function THREAD.hold(n) - -- - end + THREAD.hold = thread:newFunction(function(n) + thread.hold(n) + end, true) function THREAD.setENV(env, name) GLOBAL[name or "__env"] = env @@ -187,8 +187,7 @@ function INIT(global_channel, console_channel, status_channel) end return { - -- These are the acutal channels - init = function(global_channel, console_channel, status_channel) - return INIT(global_channel, console_channel, status_channel) + init = function() + return INIT() end } \ No newline at end of file diff --git a/lovethreads/main.lua b/lovethreads/main.lua index 5016548..c06a9ff 100644 --- a/lovethreads/main.lua +++ b/lovethreads/main.lua @@ -1,7 +1,7 @@ package.path = "../?/init.lua;../?.lua;"..package.path local multi, thread = require("multi"):init() -local GLOBAL, THREAD = require("multi.integration.loveManager"):init() +GLOBAL, THREAD = require("multi.integration.loveManager"):init() GLOBAL["Test"] = {1,2,3, function() print("HI") end} @@ -10,8 +10,28 @@ for i,v in pairs(GLOBAL["Test"]) do if type(v) == "function" then v() end end -multi:newAlarm(3):OnRing(function() - GLOBAL["Test2"] = "We got a value!" +local test = multi:newSystemThread("Test",function() + print("THREAD_ID:",THREAD_ID) + GLOBAL["Test2"] = "We did it!" + eror("hahaha") + return 1,2,3 +end) + +test.OnDeath(function(a,b,c) + print("Thread finished!",a,b,c) +end) + +test.OnError(function(self, err) + print("Got Error!",err) +end) + +local func = THREAD:newFunction(function(a,b,c) + print("let's do this!",1,2,3) + return true +end) + +func(1,2,3).OnReturn(function(ret) + print("Done",ret) end) thread:newThread(function()