From 61dcb9da01b958d457d1e9d95836f8c72f357bd7 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sun, 29 Mar 2020 11:50:15 -0400 Subject: [PATCH 1/8] Working on 15.0.0 --- README.md | 4 +- changes.md | 37 +++++ multi/init.lua | 5 +- multi/integration/lanesManager/extensions.lua | 2 +- multi/integration/lanesManager/init.lua | 4 +- multi/integration/lanesManager/threads.lua | 2 +- multi/integration/loveManager/extensions.lua | 2 +- multi/integration/loveManager/init.lua | 1 + .../integration/pesudoManager/extensions.lua | 126 ++++++++++++++++++ multi/integration/pesudoManager/init.lua | 57 ++++++++ multi/integration/pesudoManager/threads.lua | 96 +++++++++++++ multi/integration/threading.lua | 13 ++ test.lua | 43 +----- 13 files changed, 344 insertions(+), 48 deletions(-) create mode 100644 multi/integration/pesudoManager/extensions.lua create mode 100644 multi/integration/pesudoManager/init.lua create mode 100644 multi/integration/pesudoManager/threads.lua create mode 100644 multi/integration/threading.lua diff --git a/README.md b/README.md index 7c80ef8..c955b01 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Multi Version: 14.2.0 Documentation Complete, Bloat removed! +# Multi Version: 15.0.0 Fake it, until you make it +**Key Changes** +- Emulating system threading on a single thread Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and I'll look into it! diff --git a/changes.md b/changes.md index 3a60bcc..b359dfd 100644 --- a/changes.md +++ b/changes.md @@ -4,6 +4,43 @@ Table of contents --- [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.0.0 - The art of faking it +Full Update Showcase +--- +```lua +package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path +multi,thread = require("multi"):init() +GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available + +jq = multi:newSystemThreadedJobQueue(100) -- Job queue with 4 worker threads +func = jq:newFunction("test",function(a,b) + THREAD.sleep(2) + return a+b +end) + +for i = 1,100 do + func(i,i*3).connect(function(data) + print(data) + end) +end + +multi:lightloop() +``` + +Added: +--- +- Added new integration: pesudoManager, functions just like lanesManager and loveManager, but it's actually single threaded + - This was implemented because, you may want to build your code around being multi threaded, but some systems/implemetations of lua may not permit this. Since we now have a "single threaded" implementation of multi threading. We can actually create scalable code where things automatcally are threaded if built correctly. I am planning on adding more threadedOjbects. +- In addition to adding pesudo Threading `multi.integration.threading` can now be used to autodetect which enviroment you are on and use the threading features. + ``` + GLOBAL,THREAD = require("multi.integration.threading"):init() + ``` + If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. And it's my job to ensure everything works properly within reason. + +Fixed: +--- +- lanesManager THREAD:get(STRING: name) not returning the value + # Update 14.2.0 - Bloatware Removed Full Update Showcase --- diff --git a/multi/init.lua b/multi/init.lua index 896d209..4d0547c 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -30,7 +30,7 @@ if not _G["$multi"] then _G["$multi"] = {multi=multi,thread=thread} end -multi.Version = "14.2.0" +multi.Version = "15.0.0" multi.stage = "stable" multi.Name = "multi.root" multi.Mainloop = {} @@ -1430,7 +1430,8 @@ end function multi:threadloop() multi.initThreads(true) end -function multi:lightloop() +function multi:lightloop(settings) + multi.defaultSettings = settings or multi.defaultSettings if not isRunning then local Loop=self.Mainloop while true do diff --git a/multi/integration/lanesManager/extensions.lua b/multi/integration/lanesManager/extensions.lua index 2fafd5b..ddb6525 100644 --- a/multi/integration/lanesManager/extensions.lua +++ b/multi/integration/lanesManager/extensions.lua @@ -100,7 +100,7 @@ function multi:newSystemThreadedJobQueue(n) link = c.OnJobCompleted(function(jid,...) if id==jid then rets = {...} - link:Remove() + link:Destroy() end end) return thread.hold(function() diff --git a/multi/integration/lanesManager/init.lua b/multi/integration/lanesManager/init.lua index 995cf0e..db771d4 100644 --- a/multi/integration/lanesManager/init.lua +++ b/multi/integration/lanesManager/init.lua @@ -23,7 +23,7 @@ SOFTWARE. ]] package.path = "?/init.lua;?.lua;" .. package.path multi, thread = require("multi").init() -- get it all and have it on all lanes -if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassel +if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassle return { init = function() return multi.integration.GLOBAL, multi.integration.THREAD @@ -145,7 +145,7 @@ function multi.InitSystemThreadErrorHandler() end ) end -multi.print("Integrated Lanes!") +print("Integrated Lanes!") multi.integration = {} -- for module creators multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD diff --git a/multi/integration/lanesManager/threads.lua b/multi/integration/lanesManager/threads.lua index 33abc88..d038f1e 100644 --- a/multi/integration/lanesManager/threads.lua +++ b/multi/integration/lanesManager/threads.lua @@ -41,7 +41,7 @@ local function INIT(__GlobalLinda,__SleepingLinda) __GlobalLinda:set(name, val) end function THREAD.get(name) - __GlobalLinda:get(name) + return __GlobalLinda:get(name) end function THREAD.waitFor(name) local function wait() diff --git a/multi/integration/loveManager/extensions.lua b/multi/integration/loveManager/extensions.lua index add2234..cbeecf3 100644 --- a/multi/integration/loveManager/extensions.lua +++ b/multi/integration/loveManager/extensions.lua @@ -116,7 +116,7 @@ function multi:newSystemThreadedJobQueue(n) link = c.OnJobCompleted(function(jid,...) if id==jid then rets = {...} - link:Remove() + link:Destroy() end end) return thread.hold(function() diff --git a/multi/integration/loveManager/init.lua b/multi/integration/loveManager/init.lua index cf2054d..b88ffd3 100644 --- a/multi/integration/loveManager/init.lua +++ b/multi/integration/loveManager/init.lua @@ -81,6 +81,7 @@ end multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD require("multi.integration.loveManager.extensions") +print("Integrated Love Threading!") return {init=function() return GLOBAL,THREAD end} \ No newline at end of file diff --git a/multi/integration/pesudoManager/extensions.lua b/multi/integration/pesudoManager/extensions.lua new file mode 100644 index 0000000..92c5889 --- /dev/null +++ b/multi/integration/pesudoManager/extensions.lua @@ -0,0 +1,126 @@ +--[[ +MIT License + +Copyright (c) 2020 Ryan Ward + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sub-license, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +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 +function multi:newSystemThreadedQueue(name) + local c = {} + function c:push(v) + table.insert(self,v) + end + function c:pop() + return table.remove(self,1) + end + function c:peek() + return self[1] + end + function c:init() + return self + end + GLOBAL[name or "_"] = c + return c +end +function multi:newSystemThreadedTable(name) + local c = {} + function c:init() + return self + end + GLOBAL[name or "_"] = c + return c +end +local setfenv = setfenv +if not setfenv then + if not debug then + multi.print("Unable to implement setfenv in lua 5.2+ the debug module is not available!") + else + setfenv = function(f, env) + return load(string.dump(f), nil, nil, env) + end + end +end +function multi:newSystemThreadedJobQueue(n) + local c = {} + c.cores = n or THREAD.getCores()*2 + c.OnJobCompleted = multi:newConnection() + local jobs = {} + local ID=1 + local jid = 1 + local env = {} + setmetatable(env,{ + __index = _G + }) + local funcs = {} + function c:doToAll(func) + setfenv(func,env)() + return self + end + function c:registerFunction(name,func) + funcs[name] = setfenv(func,env) + return self + end + function c:pushJob(name,...) + table.insert(jobs,{name,jid,{...}}) + jid = jid + 1 + return jid-1 + end + local nFunc = 0 + function c:newFunction(name,func,holup) -- This registers with the queue + if type(name)=="function" then + holup = func + func = name + name = "JQ_Function_"..nFunc + end + nFunc = nFunc + 1 + c:registerFunction(name,func) + return thread:newFunction(function(...) + local id = c:pushJob(name,...) + local link + local rets + link = c.OnJobCompleted(function(jid,...) + if id==jid then + rets = {...} + link:Destroy() + end + end) + return thread.hold(function() + if rets then + return unpack(rets) or multi.NIL + end + end) + end,holup),name + end + for i=1,c.cores do + multi:newThread("PesudoThreadedJobQueue_"..i,function() + while true do + thread.yield() + if #jobs>0 then + local j = table.remove(jobs,1) + c.OnJobCompleted:Fire(j[2],funcs[j[1]](unpack(j[3]))) + else + thread.sleep(.05) + end + end + end) + end + return c +end \ No newline at end of file diff --git a/multi/integration/pesudoManager/init.lua b/multi/integration/pesudoManager/init.lua new file mode 100644 index 0000000..eb9b66f --- /dev/null +++ b/multi/integration/pesudoManager/init.lua @@ -0,0 +1,57 @@ +--[[ +MIT License + +Copyright (c) 2020 Ryan Ward + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sub-license, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] +package.path = "?/init.lua;?.lua;" .. package.path +local multi, thread = require("multi").init() + +if multi.integration then + return { + init = function() + return multi.integration.GLOBAL, multi.integration.THREAD + end + } +end + +local GLOBAL, THREAD = require("multi.integration.pesudoManager.threads"):init() + +function multi:canSystemThread() -- We are emulating system threading + return true +end + +function multi:getPlatform() + return "pesudo" +end + +THREAD.newFunction=thread.newFunction +multi.newSystemThread = multi.newThread + +print("Integrated Pesudo Threading!") +multi.integration = {} -- for module creators +multi.integration.GLOBAL = GLOBAL +multi.integration.THREAD = THREAD +require("multi.integration.pesudoManager.extensions") +return { + init = function() + return GLOBAL, THREAD + end +} diff --git a/multi/integration/pesudoManager/threads.lua b/multi/integration/pesudoManager/threads.lua new file mode 100644 index 0000000..0c4f802 --- /dev/null +++ b/multi/integration/pesudoManager/threads.lua @@ -0,0 +1,96 @@ +--[[ +MIT License + +Copyright (c) 2020 Ryan Ward + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sub-license, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] +local function getOS() + if package.config:sub(1, 1) == "\\" then + return "windows" + else + return "unix" + end +end +local function INIT() + local THREAD = {} + local GLOBAL = {} + THREAD.Priority_Core = 3 + THREAD.Priority_High = 2 + THREAD.Priority_Above_Normal = 1 + THREAD.Priority_Normal = 0 + THREAD.Priority_Below_Normal = -1 + THREAD.Priority_Low = -2 + THREAD.Priority_Idle = -3 + function THREAD.set(name, val) + GLOBAL[name] = val + end + function THREAD.get(name) + return GLOBAL[name] + end + function THREAD.waitFor(name) + return thread.hold(function() return GLOBAL[name] end) + end + if getOS() == "windows" then + THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS")) + else + THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n")) + end + function THREAD.getCores() + return THREAD.__CORES + end + function THREAD.getConsole() + local c = {} + function c.print(...) + print(...) + end + function c.error(err) + error("ERROR in <"..__THREADNAME__..">: "..err) + end + return c + end + function THREAD.getThreads() + return {}--GLOBAL.__THREADS__ + end + if os.getOS() == "windows" then + THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS")) + else + THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n")) + end + function THREAD.kill() + error("Thread was killed!") + end + function THREAD.getName() + return THREAD_NAME + end + function THREAD.getID() + return THREAD_ID + end + _G.THREAD_ID = 0 + function THREAD.sleep(n) + thread.sleep(n) + end + function THREAD.hold(n) + return thread.hold(n) + end + return GLOBAL, THREAD +end +return {init = function() + return INIT() +end} \ No newline at end of file diff --git a/multi/integration/threading.lua b/multi/integration/threading.lua new file mode 100644 index 0000000..b75dbc6 --- /dev/null +++ b/multi/integration/threading.lua @@ -0,0 +1,13 @@ +-- We need to detect what enviroment we are running our code in. +return { + init = function() + if love then + return require("multi.integration.loveManager"):init() + else + if pcall(require,"lanes") then + return require("multi.integration.lanesManager"):init() + end + return require("multi.integration.pesudoManager"):init() + end + end +} \ No newline at end of file diff --git a/test.lua b/test.lua index e8b20d5..1f840a4 100644 --- a/test.lua +++ b/test.lua @@ -1,44 +1,7 @@ package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path -local multi,thread = require("multi"):init() +multi,thread = require("multi"):init() +GLOBAL,THREAD = require("multi.integration.pesudoManager"):init() --- Testing destroying and fixed connections -c = multi:newConnection() -c1 = c(function() - print("called 1") -end) -c2 = c(function() - print("called 2") -end) -c3 = c(function() - print("called 3") -end) -print(c1,c2.Type,c3) -c:Fire() -c2:Destroy() -print(c1,c2.Type,c3) -c:Fire() -c1:Destroy() -print(c1,c2.Type,c3) -c:Fire() --- Destroying alarms and threads -local test = multi:newThread(function() - while true do - thread.sleep(1) - print("Hello!") - end -end) - -test.OnDeath(function() - os.exit() -- This is the last thing called. -end) - -local alarm = multi:newAlarm(4):OnRing(function(a) - print(a.Type) - a:Destroy() - print(a.Type) - test:Destroy() -end) - -multi:lightloop() \ No newline at end of file +multi:mainloop({print=true}) \ No newline at end of file -- 2.43.0 From 9d97eac1460bab93502b7ce59b7d4ca2982e674f Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Fri, 8 May 2020 11:41:52 -0400 Subject: [PATCH 2/8] Working on psuedo threading --- changes.md | 32 +++++- multi/init.lua | 128 ++++++++++++++++++++++- multi/integration/pesudoManager/init.lua | 4 +- test.lua | 15 ++- 4 files changed, 171 insertions(+), 8 deletions(-) diff --git a/changes.md b/changes.md index b359dfd..dab229f 100644 --- a/changes.md +++ b/changes.md @@ -24,18 +24,48 @@ for i = 1,100 do end) end +test = true +local haha = true +multi:newThread("Standard Thread 1",function() + print(self.Name,haha) + while true do + thread.sleep(1) + print("Testing "..self.Name..":",test) + thread.sleep(.1) + prient("...") + end +end) +multi:newThread("Standard Thread 2",function() + print(self.Name) + while true do + thread.sleep(1) + print("Testing "..self.Name..":",test) + end +end) +multi:newISOThread("Isolated Thread",function() + while true do + thread.sleep(1) + print("Testing Isolated:",test) + --errhor("huh") + end +end).OnError(function(...) + print(...) +end) + multi:lightloop() ``` Added: --- +- multi:newISOThread(name,func) + - Creates an isolated thread that prevents both locals and globals from being accessed. - Added new integration: pesudoManager, functions just like lanesManager and loveManager, but it's actually single threaded - This was implemented because, you may want to build your code around being multi threaded, but some systems/implemetations of lua may not permit this. Since we now have a "single threaded" implementation of multi threading. We can actually create scalable code where things automatcally are threaded if built correctly. I am planning on adding more threadedOjbects. - In addition to adding pesudo Threading `multi.integration.threading` can now be used to autodetect which enviroment you are on and use the threading features. ``` GLOBAL,THREAD = require("multi.integration.threading"):init() ``` - If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. And it's my job to ensure everything works properly within reason. + If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. Fixed: --- diff --git a/multi/init.lua b/multi/init.lua index 4d0547c..a069849 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -1072,13 +1072,21 @@ function thread:newFunction(func,holdme) return temp end end +-- A cross version way to set enviroments, not the same as fenv though +function multi.setEnv(func,env) + local f = string.dump(func) + local chunk = load(f,"env","bt",env) + return chunk +end + function multi:newThread(name,func,...) multi.OnLoad:Fire() local func = func or name if type(name) == "function" then name = "Thread#"..threadCount end - local env = {} + local c={} + local env = {self=c} setmetatable(env,{ __index = Gref, __newindex = function(t,k,v) @@ -1090,7 +1098,117 @@ function multi:newThread(name,func,...) end }) setfenv(func,env) + c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} + c.startArgs = {...} + c.ref={} + c.Name=name + c.thread=coroutine.create(func) + c.sleep=1 + c.Type="thread" + c.TID = threadid + c.firstRunDone=false + c.timer=multi:newTimer() + c._isPaused = false + c.returns = {} + c.OnError = multi:newConnection(true,nil,true) + c.OnDeath = multi:newConnection(true,nil,true) + function c:isPaused() + return self._isPaused + end + local resumed = false + function c:Pause() + if not self._isPaused then + thread.request(self,"exec",function() + thread.hold(function() + return resumed + end) + resumed = false + self._isPaused = false + end) + self._isPaused = true + end + return self + end + function c:Resume() + resumed = true + return self + end + function c:Kill() + thread.request(self,"kill") + return self + end + c.Destroy = c.Kill + function c.ref:send(name,val) + ret=coroutine.yield({Name=name,Value=val}) + end + function c.ref:get(name) + return self.Globals[name] + end + function c.ref:kill() + dRef[1] = "_kill_" + dRef[2] = "I Was killed by You!" + err = coroutine.yield(dRef) + if err then + error("Failed to kill a thread! Exiting...") + end + end + function c.ref:sleep(n) + if type(n)=="function" then + ret=thread.hold(n) + elseif type(n)=="number" then + ret=thread.sleep(tonumber(n) or 0) + else + error("Invalid Type for sleep!") + end + end + function c.ref:syncGlobals(v) + self.Globals=v + end + table.insert(threads,c) + if initT==false then + multi.initThreads() + end + c.creationTime = os.clock() + threadid = threadid + 1 + self:create(c) + return c +end +function multi:newISOThread(name,func,...) + multi.OnLoad:Fire() + local func = func or name + if type(name) == "function" then + name = "Thread#"..threadCount + end + local env = { + thread = thread, + multi = multi, + coroutine = coroutine, + debug = debug, + io = io, + math = math, + os = os, + package = package, + string = string, + table = table, + utf8 = utf8 + } + for i,v in pairs(_G) do + if tostring(v):match("builtin") then + env[i]=v + end + end + setmetatable(env,{ + __newindex = function(t,k,v) + if type(v)=="function" then + rawset(t,k,thread:newFunction(v)) + else + Gref[k]=v + end + end + }) + local func = multi.setEnv(func,env) local c={} + env.self = c c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} c.startArgs = {...} c.ref={} @@ -1255,9 +1373,11 @@ function multi.initThreads(justThreads) multi.scheduler:OnLoop(function(self) for i=#threads,1,-1 do if threads[i].isError then - threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) - multi.setType(threads[i],multi.DestroyedObj) - table.remove(threads,i) + if coroutine.status(threads[i].thread)=="dead" then + threads[i].OnError:Fire(threads[i],unpack(threads[i].TempRets)) + multi.setType(threads[i],multi.DestroyedObj) + table.remove(threads,i) + end end if threads[i] and not threads[i].__started then if coroutine.running() ~= threads[i].thread then diff --git a/multi/integration/pesudoManager/init.lua b/multi/integration/pesudoManager/init.lua index eb9b66f..8f5a468 100644 --- a/multi/integration/pesudoManager/init.lua +++ b/multi/integration/pesudoManager/init.lua @@ -43,7 +43,9 @@ function multi:getPlatform() end THREAD.newFunction=thread.newFunction -multi.newSystemThread = multi.newThread +multi.newSystemThread = multi.newISOThread +-- System threads as implemented here cannot share memory, but use a message passing system. +-- An isolated thread allows us to mimic that behavior so if access data from the "main" thread happens things will not work. This behavior is in line with how the system threading works print("Integrated Pesudo Threading!") multi.integration = {} -- for module creators diff --git a/test.lua b/test.lua index 1f840a4..ce7e41f 100644 --- a/test.lua +++ b/test.lua @@ -1,7 +1,18 @@ package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path multi,thread = require("multi"):init() GLOBAL,THREAD = require("multi.integration.pesudoManager"):init() +test = true +local haha = true +jq = multi:newSystemThreadedJobQueue(100) -- Job queue with 4 worker threads +func = jq:newFunction("test",function(a,b) + THREAD.sleep(2) + return a+b +end) +for i = 1,100 do + func(i,i*3).connect(function(data) + print(data) + end) +end - -multi:mainloop({print=true}) \ No newline at end of file +multi:mainloop() -- 2.43.0 From 300827b7bd74953383c4a1a970ee1417798e8c42 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Fri, 8 May 2020 17:44:16 -0400 Subject: [PATCH 3/8] cleaning up --- changes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changes.md b/changes.md index dab229f..01f1e1c 100644 --- a/changes.md +++ b/changes.md @@ -54,7 +54,6 @@ end) multi:lightloop() ``` - Added: --- - multi:newISOThread(name,func) @@ -69,6 +68,7 @@ Added: Fixed: --- +- pseudoThreading and threads had an issue where they weren't executing properly - lanesManager THREAD:get(STRING: name) not returning the value # Update 14.2.0 - Bloatware Removed -- 2.43.0 From d2ce7e070bdf0bf78f410dd38fa831391c66161b Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Fri, 15 May 2020 01:38:24 -0400 Subject: [PATCH 4/8] Fixing pseudothreading --- multi/init.lua | 32 ++++++++++++++------- multi/integration/pesudoManager/init.lua | 12 +++++++- multi/integration/pesudoManager/threads.lua | 1 - test.lua | 24 +++++++++------- 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/multi/init.lua b/multi/init.lua index a069849..84bdb8f 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -1179,7 +1179,9 @@ function multi:newISOThread(name,func,...) if type(name) == "function" then name = "Thread#"..threadCount end + local Gref = {} local env = { + THREAD_NAME = name, thread = thread, multi = multi, coroutine = coroutine, @@ -1197,23 +1199,31 @@ function multi:newISOThread(name,func,...) env[i]=v end end - setmetatable(env,{ - __newindex = function(t,k,v) - if type(v)=="function" then - rawset(t,k,thread:newFunction(v)) - else - Gref[k]=v - end - end - }) - local func = multi.setEnv(func,env) local c={} + function c:inject(tab) + for i,v in pairs(tab) do + Gref[i] = v + env[i] = v + end + end + function c:start() + setmetatable(env,{ + __index = Gref, + __newindex = function(t,k,v) + if type(v)=="function" then + rawset(t,k,thread:newFunction(v)) + else + Gref[k]=v + end + end + }) + self.thread=coroutine.create(multi.setEnv(func,env)) + end env.self = c c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} c.startArgs = {...} c.ref={} c.Name=name - c.thread=coroutine.create(func) c.sleep=1 c.Type="thread" c.TID = threadid diff --git a/multi/integration/pesudoManager/init.lua b/multi/integration/pesudoManager/init.lua index 8f5a468..ea9325a 100644 --- a/multi/integration/pesudoManager/init.lua +++ b/multi/integration/pesudoManager/init.lua @@ -43,7 +43,17 @@ function multi:getPlatform() end THREAD.newFunction=thread.newFunction -multi.newSystemThread = multi.newISOThread +local id = 0 +function multi:newSystemThread(name,func,...) + local t = multi:newISOThread(name,func,...) + t:inject{ + GLOBAL = GLOBAL, + THREAD = THREAD, + THREAD_ID = id + } + id = id + 1 + t:start() +end -- System threads as implemented here cannot share memory, but use a message passing system. -- An isolated thread allows us to mimic that behavior so if access data from the "main" thread happens things will not work. This behavior is in line with how the system threading works diff --git a/multi/integration/pesudoManager/threads.lua b/multi/integration/pesudoManager/threads.lua index 0c4f802..76a807d 100644 --- a/multi/integration/pesudoManager/threads.lua +++ b/multi/integration/pesudoManager/threads.lua @@ -82,7 +82,6 @@ local function INIT() function THREAD.getID() return THREAD_ID end - _G.THREAD_ID = 0 function THREAD.sleep(n) thread.sleep(n) end diff --git a/test.lua b/test.lua index ce7e41f..1b053eb 100644 --- a/test.lua +++ b/test.lua @@ -3,16 +3,20 @@ multi,thread = require("multi"):init() GLOBAL,THREAD = require("multi.integration.pesudoManager"):init() test = true local haha = true -jq = multi:newSystemThreadedJobQueue(100) -- Job queue with 4 worker threads -func = jq:newFunction("test",function(a,b) - THREAD.sleep(2) - return a+b +local test = multi:newSystemThreadedTable("test"):init() +test['hi'] = "Hello World!!!" +test['bye'] = "Bye World!!!" +multi:newSystemThread("test_1",function() + print(THREAD_NAME,THREAD_ID,THREAD.getName()) + print("thread",GLOBAL,THREAD,test,haha) + tab = THREAD.waitFor("test"):init() + print(tab["hi"]) +end) +multi:newSystemThread("test_2",function() + print(THREAD_NAME,THREAD_ID,THREAD.getName()) + print("thread",GLOBAL,THREAD,test,haha) + tab = THREAD.waitFor("test"):init() + print(tab["bye"]) end) -for i = 1,100 do - func(i,i*3).connect(function(data) - print(data) - end) -end - multi:mainloop() -- 2.43.0 From ed924a3d9d7481e397a05ad862171e1b2f645c47 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 1 May 2021 15:52:32 -0400 Subject: [PATCH 5/8] Version 15.0.0 --- .gitignore | 1 + Documentation.md | 81 ++++----- README.md | 23 ++- changes.md | 60 +++---- multi/init.lua | 158 ++++-------------- .../integration/pesudoManager/extensions.lua | 11 ++ multi/integration/pesudoManager/init.lua | 31 +++- multi/integration/pesudoManager/threads.lua | 9 +- test.lua | 55 ++++-- tests/runtests.lua | 17 ++ 10 files changed, 217 insertions(+), 229 deletions(-) create mode 100644 tests/runtests.lua diff --git a/.gitignore b/.gitignore index e1b9406..fa5e1d5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ test2.lua *.mp3 *.exe +*.dll lanestestclient.lua lanestest.lua sample-node.lua diff --git a/Documentation.md b/Documentation.md index d89cda7..6d37b54 100644 --- a/Documentation.md +++ b/Documentation.md @@ -1,4 +1,4 @@ -Current Multi Version: 14.2.0 +Current Multi Version: 15.0.0 # Multi static variables `multi.Version` — The current version of the library @@ -153,7 +153,7 @@ The connect feature has some syntax sugar to it as seen below Example: ```lua -local multi = require("multi") +multi,thread = require("multi"):init() -- Let’s create the events yawn={} OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls in case something goes wrong default @@ -201,7 +201,7 @@ Timeouts are a collection of methods that allow you to handle timeouts. These on ```lua package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path -multi = require("multi") +multi,thread = require("multi"):init() loop = multi:newLoop(function() -- do stuff @@ -224,7 +224,7 @@ loop:OnTimerResolved(function(self,...) print(...) end) -multi:lightloop() +multi:mainloop() ``` As mentioned above this is made much easier using threads ```lua @@ -255,14 +255,14 @@ print(func(0)) Example: ```lua package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path -multi = require("multi") +multi,thread = require("multi"):init() multi:scheduleJob({min = 30},function() -- Every hour at minute 30 this event will be triggered! You can mix and match as well! print("Hi") end) multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered print("Hi") end) -multi:lightloop() +multi:mainloop() ``` # Universal Actor methods @@ -287,7 +287,7 @@ All of these functions are found on actors Example: ```lua -local multi = require("multi") +multi,thread = require("multi"):init() count=0 -- A loop object is used to demostrate how one could use an event object. loop=multi:newLoop(function(self,dt) @@ -298,7 +298,7 @@ event:OnEvent(function(self) -- connect to the event object loop:Destroy() -- destroys the loop from running! print("Stopped that loop!",count) end) -- events like alarms need to be reset the Reset() command works here as well -multi:lightloop() +multi:mainloop() ``` # Actor: Updaters @@ -311,12 +311,12 @@ Updaters are a mix between both loops and steps. They were a way to add basic pr Example: ```lua -local multi = require("multi") +multi,thread = require("multi"):init() updater=multi:newUpdater(5000) -- simple, think of a loop with the skip feature of a step updater:OnUpdate(function(self) print("updating...") end) -multi:lightloop() +multi:mainloop() ``` # Actor: Alarms @@ -328,13 +328,13 @@ Alarms ring after a certain amount of time, but you need to reset the alarm ever Example: ```lua -local multi = require("multi") +multi,thread = require("multi"):init() alarm=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock() alarm:OnRing(function(a) print("3 Seconds have passed!") a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds end) -multi:lightloop() +multi:mainloop() ``` # Actor: Loops @@ -346,7 +346,7 @@ Loops are events that happen over and over until paused. They act like a while l Example: ```lua package.path="?/init.lua;?.lua;"..package.path -local multi = require("multi") +multi,thread = require("multi"):init() local a = 0 loop = multi:newLoop(function() a = a + 1 @@ -355,7 +355,7 @@ loop = multi:newLoop(function() loop:Pause() end end) -multi:lightloop() +multi:mainloop() ``` # Actor: TLoops @@ -366,7 +366,7 @@ multi:lightloop() Example: ```lua package.path="?/init.lua;?.lua;"..package.path -local multi = require("multi") +multi,thread = require("multi"):init() local a = 0 loop = multi:newTLoop(function() a = a + 1 @@ -375,7 +375,7 @@ loop = multi:newTLoop(function() loop:Pause() end end,1) -multi:lightloop() +multi:mainloop() ``` # Actor: Steps @@ -390,13 +390,13 @@ multi:lightloop() Example: ```lua package.path="?/init.lua;?.lua;"..package.path -local multi = require("multi") +multi,thread = require("multi"):init() multi:newStep(1,10,1,0):OnStep(function(step,pos) print(step,pos) end):OnEnd(fucntion(step) step:Destroy() end) -multi:lightloop() +multi:mainloop() ``` # Actor: TSteps @@ -411,13 +411,13 @@ multi:lightloop() Example: ```lua package.path="?/init.lua;?.lua;"..package.path -local multi = require("multi") +multi,thread = require("multi"):init() multi:newTStep(1,10,1,1):OnStep(function(step,pos) print(step,pos) end):OnEnd(fucntion(step) step:Destroy() end) -multi:lightloop() +multi:mainloop() ``` # Coroutine based Threading (CBT) @@ -474,7 +474,7 @@ Example: -- Jobs are not natively part of the multi library. I planned on adding them, but decided against it. Below is the code that would have been used. -- Implementing a job manager using services package.path="?/init.lua;?.lua;"..package.path -local multi = require("multi") +multi,thread = require("multi"):init() multi.Jobs = multi:newService(function(self,jobs) local job = table.remove(jobs,1) if job and job.removed==nil then @@ -532,6 +532,7 @@ jobsn[1]:removeJob() -- Select a job and remove it multi.Jobs:removeJobs("test2") -- Remove all jobs names 'test2' multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority multi.Jobs.SetPriority(multi.Priority_Core) +multi:mainloop() ``` # CBT: newThread() @@ -556,21 +557,21 @@ Constants Examples: ```lua package.path="?/init.lua;?.lua;"..package.path -local multi = require("multi") +multi,thread = require("multi"):init() multi:newThread("Example of basic usage",function() while true do thread.sleep(1) print("We just made an alarm!") end end) -multi:lightloop() +multi:mainloop() ``` # System Threads (ST) - Multi-Integration Getting Started The system threads need to be required seperatly. ```lua -- I recommend keeping these as globals. When using lanes you can use local and things will work, but if you use love2d and locals, upvalues are not transfered over threads and this can be an issue -GLOBAL, THREAD = require("multi.integration.lanesManager"):init() -- We will talk about the global and thread interface that is returned +GLOBAL, THREAD = require("multi.integration.threading"):init() -- We will talk about the global and thread interface that is returned GLOBAL, THREAD = require("multi.integration.loveManager"):init() GLOBAL, THREAD = require("luvitManager") --* ``` @@ -609,8 +610,8 @@ ST - System Threads System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below: ```lua -local multi = require("multi") -- keep this global when using lanes or implicitly define multi within the spawned thread -local GLOBAL, THREAD = require("multi.integration.lanesManager").init() +multi,thread = require("multi"):init() -- keep this global when using lanes or implicitly define multi within the spawned thread +local GLOBAL, THREAD = require("multi.integration.threading").init() multi:newSystemThread("Example thread",function() local multi = require("multi") -- we are in a thread so lets not refer to that upvalue! print("We have spawned a thread!") @@ -624,7 +625,7 @@ end,"A message that we are passing") -- There are restrictions on what can be pa tloop = multi:newTLoop(function() print("I'm still kicking!") end,1) -multi:lightloop() +multi:mainloop() ``` *This isn't entirely true, as of right now the compatiablity with the lanes library and love2d engine have their own methods to share data, but if you would like to have your code work in both enviroments then using the GLOBAL table and the data structures provided by the multi library will ensure this happens. If you do not plan on having support for both platforms then feel free to use linda's in lanes and channels in love2d. @@ -638,8 +639,8 @@ When creating objects with a name they are automatically exposed to the GLOBAL t ```lua -- Exposing a queue -multi = require("multi") -local GLOBAL, THREAD = require("multi.integration.lanesManager").init() -- The standard setup above +multi,thread = require("multi"):init() +local GLOBAL, THREAD = require("multi.integration.threading").init() -- The standard setup above queue = multi:newSystemThreadedQueue("myQueue"):init() -- We create and initiate the queue for the main thread queue:push("This is a test!") -- We push some data onto the queue that other threads can consume and do stuff with multi:newSystemThread("Example thread",function() -- Create a system thread @@ -647,7 +648,7 @@ multi:newSystemThread("Example thread",function() -- Create a system thread local data = queue:pop() -- Get the data print(data) -- print the data end) -multi:lightloop() +multi:mainloop() ``` # ST - SystemThreadedQueue @@ -659,9 +660,9 @@ multi:lightloop() Let's get into some examples: ```lua -multi = require("multi") +multi,thread = require("multi"):init() thread_names = {"Thread_A","Thread_B","Thread_C","Thread_D"} -local GLOBAL, THREAD = require("multi.integration.lanesManager"):init() +local GLOBAL, THREAD = require("multi.integration.threading"):init() queue = multi:newSystemThreadedQueue("myQueue"):init() for _,n in pairs(thread_names) do multi:newSystemThread(n,function() @@ -683,11 +684,13 @@ end):OnEvent(function() print("No more data within the queue!") os.exit() end) -multi:lightloop() +multi:mainloop() ``` You have probable noticed that the output from this is a total mess! Well I though so too, and created the system threaded console! +# ST - Using the Console + # ST - SystemThreadedJobQueue `jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads - `jq.cores = (supplied number) or (the number of cores on your system*2)` @@ -703,8 +706,8 @@ You have probable noticed that the output from this is a total mess! Well I thou Example: ```lua package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path -multi = require("multi") -GLOBAL, THREAD = require("multi.integration.lanesManager"):init() +multi,thread = require("multi"):init() +GLOBAL, THREAD = require("multi.integration.threading"):init() local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores jq:doToAll(function() Important = 15 @@ -727,7 +730,7 @@ func(5,5).connect(function(ret) print("Connected",ret) os.exit() end) -multi:lightloop() +multi:mainloop() ``` # ST - SystemThreadedTable `stt = multi:newSystemThreadedTable(STRING: name)` @@ -738,15 +741,15 @@ multi:lightloop() Example: ```lua package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path -multi = require("multi") -GLOBAL, THREAD = require("multi.integration.lanesManager"):init() +multi,thread = require("multi"):init() +GLOBAL, THREAD = require("multi.integration.threading"):init() local stt = multi:newSystemThreadedTable("stt") stt["hello"] = "world" multi:newSystemThread("test thread",function() local stt = GLOBAL["stt"]:init() print(stt["hello"]) end) -multi:lightloop() +multi:mainloop() ``` # Network Threads - Multi-Integration WIP Being Reworked More of a fun project of mine then anything core to to the library it will be released and documented when it is ready. I do not have a timeframe for this diff --git a/README.md b/README.md index c955b01..265992c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# Multi Version: 15.0.0 Fake it, until you make it +# Multi Version: 15.0.0 Fake it till you make it **Key Changes** - Emulating system threading on a single thread + - Purpose to allow consistant code that can scale when threading is available. Check out the changelog for more details Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and I'll look into it! @@ -26,20 +27,28 @@ https://discord.gg/U8UspuA
Planned features/TODO --------------------- - [x] ~~Finish Documentation~~ Finished +- [ ] Create test suite - [ ] Network Parallelism rework Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)
----- ```lua -local multi, thread = require("multi").init() -mutli:newThread("Example",function() +package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path +local multi, thread = require("multi"):init() +GLOBAL, THREAD = require("multi.integration.threading"):init() +multi:newSystemThread("System Thread",function() while true do - thread.sleep(1) - print("Hello!") + THREAD.sleep(1) + print("World!") end end) -multi:lightloop() ---multi:mainloop() +multi:newThread("Coroutine Based Thread",function() + while true do + print("Hello") + thread.sleep(1) + end +end) +multi:mainloop() --[[ while true do multi:uManager() diff --git a/changes.md b/changes.md index 01f1e1c..a592227 100644 --- a/changes.md +++ b/changes.md @@ -12,65 +12,67 @@ package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path multi,thread = require("multi"):init() GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available -jq = multi:newSystemThreadedJobQueue(100) -- Job queue with 4 worker threads +jq = multi:newSystemThreadedJobQueue(4) -- Job queue with 4 worker threads func = jq:newFunction("test",function(a,b) THREAD.sleep(2) return a+b end) -for i = 1,100 do +for i = 1,10 do func(i,i*3).connect(function(data) print(data) end) end -test = true -local haha = true multi:newThread("Standard Thread 1",function() - print(self.Name,haha) while true do thread.sleep(1) - print("Testing "..self.Name..":",test) - thread.sleep(.1) - prient("...") + print("Testing 1 ...") end end) -multi:newThread("Standard Thread 2",function() - print(self.Name) - while true do - thread.sleep(1) - print("Testing "..self.Name..":",test) - end -end) -multi:newISOThread("Isolated Thread",function() - while true do - thread.sleep(1) - print("Testing Isolated:",test) - --errhor("huh") - end -end).OnError(function(...) - print(...) -end) -multi:lightloop() +multi:newISOThread("ISO Thread 2",{test=true},function() + while true do + thread.sleep(1) + print("Testing 2 ...") + end +end) + +multi:mainloop() ``` +Note: +--- +This was supposed to be released over a year ago, but work and other things got in my way. Pesudo Threading now works. The goal of this is so you can write modules that can be scaled up to utlize threading features when available. Added: --- -- multi:newISOThread(name,func) - - Creates an isolated thread that prevents both locals and globals from being accessed. +- multi:newISOThread(name,func,env) + - Creates an isolated thread that prevents both locals and globals from being accessed. + - Was designed for the pesudoManager so it can emulate threads. You can use it as a super sandbox, but remember upvalues are also stripped which was intened for what I wanted them to do! - Added new integration: pesudoManager, functions just like lanesManager and loveManager, but it's actually single threaded - This was implemented because, you may want to build your code around being multi threaded, but some systems/implemetations of lua may not permit this. Since we now have a "single threaded" implementation of multi threading. We can actually create scalable code where things automatcally are threaded if built correctly. I am planning on adding more threadedOjbects. - In addition to adding pesudo Threading `multi.integration.threading` can now be used to autodetect which enviroment you are on and use the threading features. ``` GLOBAL,THREAD = require("multi.integration.threading"):init() ``` - If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. + If you are using love2d it will use that, if you have lanes avaialble then it will use lanes. Otherwise it will use pesudo threading. This allows module creators to implement scalable features without having to worry about which enviroment they are in. Can now require a consistant module: `require("multi.integration.threading"):init()` + +Changed: +--- +- Documentation to reflect the changes made + +Removed: +--- +- CBT (Coroutine Based threading) has lost a feature, one that hasn't been used much, but broke compatiblity with anything above lua 5.1. My goal is to make my library work with all versions of lua above 5.1, including 5.4. Lua 5.2+ changed how enviroments worked which means that you can no longer modify an enviroment of function without using the debug library. This isn't ideal for how things in my library worked, but it is what it is. The feature lost is the one that converted all functions within a threaded enviroment into a threadedfunction. This in hindsight wasn't the best pratice and if it is the desired state you as the user can manually do that anyway. This shouldn't affect anyones code in a massive way. Fixed: --- - pseudoThreading and threads had an issue where they weren't executing properly - lanesManager THREAD:get(STRING: name) not returning the value +Todo: +--- +- Add more details to the documentation + # Update 14.2.0 - Bloatware Removed Full Update Showcase --- @@ -131,7 +133,7 @@ Quality Of Life: Added: --- - Type: destroyed - - A special state of an object that causes that object to become immutable and callable. The object Type is always "destroyed" it cannot be changed. The object can be indexed to infinity without issue. Every part of the object can be called as if it were a function including the indexed parts. This is done incase you destroy an object and still use it somewhere. However, if you are expecting something from the object then you may still encounter an error, though the returned type is an instance of the destroyed object which can be indexed and called like normal. This object can be used in any way and no errors will come about with it. + - A special state of an object that causes that object to become immutable and callable. The object Type is always "destroyed" it cannot be changed. The object can be indexed to infinity without issue. Every part of the object can be called as if it were a function including the indexed parts. This is done incase you destroy an object and still "use" it somewhere. However, if you are expecting something from the object then you may still encounter an error, though the returned type is an instance of the destroyed object which can be indexed and called like normal. This object can be used in any way and no errors will come about with it. Fixed: --- diff --git a/multi/init.lua b/multi/init.lua index 84bdb8f..127d2ac 100644 --- a/multi/init.lua +++ b/multi/init.lua @@ -163,6 +163,20 @@ function multi:getTasksDetails(t) end --Helpers + +-- Used with ISO Threads +local function isolateFunction(func,env) + local dmp = string.dump(func) + local env = env or {} + if setfenv then + local f = loadstring(dmp,"IsolatedThread_PesudoThreading") + setfenv(f,env) + return f + else + return load(dmp,"IsolatedThread_PesudoThreading","bt",env) + end +end + function multi:Break() self:Pause() self.Active=nil @@ -963,7 +977,12 @@ function thread.yield() return thread.sleep(0) end function thread.isThread() - return coroutine.running()~=nil + if _VERSION~="Lua 5.1" then + local a,b = coroutine.running() + return not(b) + else + return coroutine.running()~=nil + end end function thread.getCores() return thread.__CORES @@ -1087,17 +1106,6 @@ function multi:newThread(name,func,...) end local c={} local env = {self=c} - setmetatable(env,{ - __index = Gref, - __newindex = function(t,k,v) - if type(v)=="function" then - rawset(t,k,thread:newFunction(v)) - else - Gref[k]=v - end - end - }) - setfenv(func,env) c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} c.startArgs = {...} c.ref={} @@ -1110,6 +1118,7 @@ function multi:newThread(name,func,...) c.timer=multi:newTimer() c._isPaused = false c.returns = {} + c.isError = false c.OnError = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true) function c:isPaused() @@ -1173,126 +1182,21 @@ function multi:newThread(name,func,...) self:create(c) return c end -function multi:newISOThread(name,func,...) +function multi:newISOThread(name,func,_env,...) multi.OnLoad:Fire() local func = func or name + local env = _env or {} + if not env.thread then + env.thread = thread + end + if not env.multi then + env.multi = multi + end if type(name) == "function" then name = "Thread#"..threadCount end - local Gref = {} - local env = { - THREAD_NAME = name, - thread = thread, - multi = multi, - coroutine = coroutine, - debug = debug, - io = io, - math = math, - os = os, - package = package, - string = string, - table = table, - utf8 = utf8 - } - for i,v in pairs(_G) do - if tostring(v):match("builtin") then - env[i]=v - end - end - local c={} - function c:inject(tab) - for i,v in pairs(tab) do - Gref[i] = v - env[i] = v - end - end - function c:start() - setmetatable(env,{ - __index = Gref, - __newindex = function(t,k,v) - if type(v)=="function" then - rawset(t,k,thread:newFunction(v)) - else - Gref[k]=v - end - end - }) - self.thread=coroutine.create(multi.setEnv(func,env)) - end - env.self = c - c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil} - c.startArgs = {...} - c.ref={} - c.Name=name - c.sleep=1 - c.Type="thread" - c.TID = threadid - c.firstRunDone=false - c.timer=multi:newTimer() - c._isPaused = false - c.returns = {} - c.OnError = multi:newConnection(true,nil,true) - c.OnDeath = multi:newConnection(true,nil,true) - function c:isPaused() - return self._isPaused - end - local resumed = false - function c:Pause() - if not self._isPaused then - thread.request(self,"exec",function() - thread.hold(function() - return resumed - end) - resumed = false - self._isPaused = false - end) - self._isPaused = true - end - return self - end - function c:Resume() - resumed = true - return self - end - function c:Kill() - thread.request(self,"kill") - return self - end - c.Destroy = c.Kill - function c.ref:send(name,val) - ret=coroutine.yield({Name=name,Value=val}) - end - function c.ref:get(name) - return self.Globals[name] - end - function c.ref:kill() - dRef[1] = "_kill_" - dRef[2] = "I Was killed by You!" - err = coroutine.yield(dRef) - if err then - error("Failed to kill a thread! Exiting...") - end - end - function c.ref:sleep(n) - if type(n)=="function" then - ret=thread.hold(n) - elseif type(n)=="number" then - ret=thread.sleep(tonumber(n) or 0) - else - error("Invalid Type for sleep!") - end - end - function c.ref:syncGlobals(v) - self.Globals=v - end - table.insert(threads,c) - if initT==false then - multi.initThreads() - end - c.creationTime = os.clock() - threadid = threadid + 1 - self:create(c) - return c + local func = isolateFunction(func,env) + return self:newThread(name,func) end function multi.initThreads(justThreads) initT = true diff --git a/multi/integration/pesudoManager/extensions.lua b/multi/integration/pesudoManager/extensions.lua index 92c5889..a64e67b 100644 --- a/multi/integration/pesudoManager/extensions.lua +++ b/multi/integration/pesudoManager/extensions.lua @@ -23,6 +23,16 @@ SOFTWARE. ]] local multi, thread = require("multi"):init() local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD + +local function stripUpValues(func) + local dmp = string.dump(func) + if setfenv then + return loadstring(dmp,"IsolatedThread_PesudoThreading") + else + return load(dmp,"IsolatedThread_PesudoThreading","bt") + end +end + function multi:newSystemThreadedQueue(name) local c = {} function c:push(v) @@ -85,6 +95,7 @@ function multi:newSystemThreadedJobQueue(n) end local nFunc = 0 function c:newFunction(name,func,holup) -- This registers with the queue + local func = stripUpValues(func) if type(name)=="function" then holup = func func = name diff --git a/multi/integration/pesudoManager/init.lua b/multi/integration/pesudoManager/init.lua index ea9325a..0cafe79 100644 --- a/multi/integration/pesudoManager/init.lua +++ b/multi/integration/pesudoManager/init.lua @@ -41,18 +41,39 @@ end function multi:getPlatform() return "pesudo" end - +local function split(str) + local tab = {} + for word in string.gmatch(str, '([^,]+)') do + table.insert(tab,word) + end + return tab +end THREAD.newFunction=thread.newFunction local id = 0 function multi:newSystemThread(name,func,...) - local t = multi:newISOThread(name,func,...) - t:inject{ + GLOBAL["$THREAD_NAME"] = name + GLOBAL["$__THREADNAME__"] = name + GLOBAL["$THREAD_ID"] = id + --GLOBAL["$thread"] = thread + local env = { GLOBAL = GLOBAL, THREAD = THREAD, - THREAD_ID = id + THREAD_NAME = name, + __THREADNAME__ = name, + THREAD_ID = id, + thread = thread } + + local tab = [[_VERSION,io,os,require,load,debug,assert,collectgarbage,error,getfenv,getmetatable,ipairs,loadstring,module,next,pairs,pcall,print,rawequal,rawget,rawset,select,setfenv,setmetatable,tonumber,tostring,type,unpack,xpcall,math,coroutine,string,table]] + tab = split(tab) + for i = 1,#tab do + env[tab[i]] = _G[tab[i]] + end + --setmetatable(env,{__index=env}) + multi:newISOThread(name,func,env,...).OnError(function(self,msg) + print("ERROR:",msg) + end) id = id + 1 - t:start() end -- System threads as implemented here cannot share memory, but use a message passing system. -- An isolated thread allows us to mimic that behavior so if access data from the "main" thread happens things will not work. This behavior is in line with how the system threading works diff --git a/multi/integration/pesudoManager/threads.lua b/multi/integration/pesudoManager/threads.lua index 76a807d..febac47 100644 --- a/multi/integration/pesudoManager/threads.lua +++ b/multi/integration/pesudoManager/threads.lua @@ -28,7 +28,7 @@ local function getOS() return "unix" end end -local function INIT() +local function INIT(env) local THREAD = {} local GLOBAL = {} THREAD.Priority_Core = 3 @@ -45,6 +45,7 @@ local function INIT() return GLOBAL[name] end function THREAD.waitFor(name) + print("Waiting",thread) return thread.hold(function() return GLOBAL[name] end) end if getOS() == "windows" then @@ -61,7 +62,7 @@ local function INIT() print(...) end function c.error(err) - error("ERROR in <"..__THREADNAME__..">: "..err) + error("ERROR in <"..GLOBAL["$__THREADNAME__"]..">: "..err) end return c end @@ -77,10 +78,10 @@ local function INIT() error("Thread was killed!") end function THREAD.getName() - return THREAD_NAME + return GLOBAL["$THREAD_NAME"] end function THREAD.getID() - return THREAD_ID + return GLOBAL["$THREAD_ID"] end function THREAD.sleep(n) thread.sleep(n) diff --git a/test.lua b/test.lua index 1b053eb..4887ade 100644 --- a/test.lua +++ b/test.lua @@ -1,22 +1,41 @@ package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path multi,thread = require("multi"):init() -GLOBAL,THREAD = require("multi.integration.pesudoManager"):init() -test = true -local haha = true -local test = multi:newSystemThreadedTable("test"):init() -test['hi'] = "Hello World!!!" -test['bye'] = "Bye World!!!" -multi:newSystemThread("test_1",function() - print(THREAD_NAME,THREAD_ID,THREAD.getName()) - print("thread",GLOBAL,THREAD,test,haha) - tab = THREAD.waitFor("test"):init() - print(tab["hi"]) -end) -multi:newSystemThread("test_2",function() - print(THREAD_NAME,THREAD_ID,THREAD.getName()) - print("thread",GLOBAL,THREAD,test,haha) - tab = THREAD.waitFor("test"):init() - print(tab["bye"]) +GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available + +jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads +func = jq:newFunction("test",function(a,b) + THREAD.sleep(2) + return a+b end) -multi:mainloop() +for i = 1,10 do + func(i,i*3).connect(function(data) + print(data) + end) +end + +local a = true +b = false + +multi:newThread("Standard Thread 1",function() + while true do + thread.sleep(1) + print("Testing 1 ...",a,b,test) + end +end).OnError(function(self,msg) + print(msg) +end) + +-- All upvalues are stripped! no access to the global, multi and thread are exposed however +multi:newISOThread("ISO Thread 2",function() + while true do + thread.sleep(1) + print("Testing 2 ...",a,b,test) -- a and b are nil, but test is true + end +end,{test=true,print=print}) + +.OnError(function(self,msg) + print(msg) +end) + +multi:mainloop() \ No newline at end of file diff --git a/tests/runtests.lua b/tests/runtests.lua new file mode 100644 index 0000000..a4c3263 --- /dev/null +++ b/tests/runtests.lua @@ -0,0 +1,17 @@ +package.path="../?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path +--[[ + This file runs all tests. + Format: + Expected: + ... + ... + ... + Actual: + ... + ... + ... + + Each test that is ran should have a 5 second pause after the test is complete + The expected and actual should "match" (Might be impossible when playing with threads) + This will be pushed directly to the master as tests start existing. +]] \ No newline at end of file -- 2.43.0 From 9a9d28f62f59d3e0b0155a6d24cf175c7e6eb08d Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 1 May 2021 16:21:34 -0400 Subject: [PATCH 6/8] Cleaning up... --- Documentation.md | 71 ++++++++++++++++++++++++++++++++- README.md | 1 + changes.md | 5 +-- rockspecs/multi-15.0-0.rockspec | 37 +++++++++++++++++ 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 rockspecs/multi-15.0-0.rockspec diff --git a/Documentation.md b/Documentation.md index 6d37b54..4216445 100644 --- a/Documentation.md +++ b/Documentation.md @@ -14,7 +14,7 @@ Current Multi Version: 15.0.0
`multi.Priority_Idle` — Lowest level of pirority that can be given to a process # Multi Runners -`multi:lightloop()` — A light version of the mainloop +`multi:lightloop()` — A light version of the mainloop doesn't run Coroutine based threads
`multi:loveloop([BOOLEAN: light true])` — Run's all the love related features as well
`multi:mainloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running
`multi:threadloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running, but prioritizes threads over multi-objects @@ -567,6 +567,67 @@ end) multi:mainloop() ``` +# CBT: newISOThread() +`th = multi:newThread([STRING name,] FUNCTION func, TABLE: env)` — Creates a new thread with name and function func. Sets the enviroment of the func to env. Both the thread.* and multi.* are automatically placed in the enviroment. + +When within a thread, if you have any holding code you will want to use thread.* to give time to other threads while your code is running. This type of thread does not have access to outside local or globals. Only what is in the env can be seen. (This thread was made so pesudo threading could work) +Constants +--- +- `th.Name` — Name of thread +- `th.Type` — Type="thread" +- `th.TID` — Thread ID +- `conn = th.OnError(FUNCTION: callback)` — Connect to an event which is triggered when an error is encountered within a thread +- `conn = th.OnDeath(FUNCTION: callback)` — Connect to an event which is triggered when the thread had either been killed or stopped running. (Not triggered when there is an error!) +- `boolean = th:isPaused()`\* — Returns true if a thread has been paused +- `self = th:Pause()`\* — Pauses a thread +- `self = th:Resume()`\* — Resumes a paused thread +- `self = th:Kill()`\* — Kills a thread +- `self = th:Destroy()`\* — Destroys a thread + +*Using these methods on a thread directly you are making a request to a thread! The thread may not accept your request, but it most likely will. You can contorl the thread flow within the thread's function itself +```lua +package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path +multi,thread = require("multi"):init() +GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available + +jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads +func = jq:newFunction("test",function(a,b) + THREAD.sleep(2) + return a+b +end) + +for i = 1,10 do + func(i,i*3).connect(function(data) + print(data) + end) +end + +local a = true +b = false + +multi:newThread("Standard Thread 1",function() + while true do + thread.sleep(1) + print("Testing 1 ...",a,b,test) + end +end).OnError(function(self,msg) + print(msg) +end) + +-- All upvalues are stripped! no access to the global, multi and thread are exposed however +multi:newISOThread("ISO Thread 2",function() + while true do + thread.sleep(1) + print("Testing 2 ...",a,b,test) -- a and b are nil, but test is true + end +end,{test=true,print=print}) + +.OnError(function(self,msg) + print(msg) +end) + +multi:mainloop() +``` # System Threads (ST) - Multi-Integration Getting Started The system threads need to be required seperatly. ```lua @@ -690,7 +751,15 @@ multi:mainloop() You have probable noticed that the output from this is a total mess! Well I though so too, and created the system threaded console! # ST - Using the Console +`console = THREAD.getConsole()` +This does guarantee an order to console output, it does ensure that all things are on nice neat lines +```lua +multi,thread = require("multi"):init() +local GLOBAL, THREAD = require("multi.integration.threading"):init() + +console.print("Hello World!") +``` # ST - SystemThreadedJobQueue `jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads - `jq.cores = (supplied number) or (the number of cores on your system*2)` diff --git a/README.md b/README.md index 265992c..ed250a7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ **Key Changes** - Emulating system threading on a single thread - Purpose to allow consistant code that can scale when threading is available. Check out the changelog for more details +- Proper support for lua versions above 5.1 (More testing is needed, a full test suite is being developed and should be made available soon) Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and I'll look into it! diff --git a/changes.md b/changes.md index a592227..1a3330f 100644 --- a/changes.md +++ b/changes.md @@ -1,8 +1,7 @@ # Changelog - Table of contents --- -[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.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.0.0 - The art of faking it Full Update Showcase @@ -42,7 +41,7 @@ multi:mainloop() ``` Note: --- -This was supposed to be released over a year ago, but work and other things got in my way. Pesudo Threading now works. The goal of this is so you can write modules that can be scaled up to utlize threading features when available. +This was supposed to be released over a year ago, but work and other things got in my way. Pesudo Threading now works. The goal of this is so you can write modules that can be scaled up to utilize threading features when available. Added: --- - multi:newISOThread(name,func,env) diff --git a/rockspecs/multi-15.0-0.rockspec b/rockspecs/multi-15.0-0.rockspec new file mode 100644 index 0000000..d8df36e --- /dev/null +++ b/rockspecs/multi-15.0-0.rockspec @@ -0,0 +1,37 @@ +package = "multi" +version = "15.0-0" +source = { + url = "git://github.com/rayaman/multi.git", + tag = "v15.0.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 how to use. + ]], + homepage = "https://github.com/rayaman/multi", + license = "MIT" +} +dependencies = { + "lua >= 5.1", + "lanes", +} +build = { + type = "builtin", + modules = { + ["multi.init"] = "multi/init.lua", + ["multi.compat.love2d"] = "multi/compat/love2d.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.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.networkManager"] = "multi/integration/networkManager.lua", + } +} \ No newline at end of file -- 2.43.0 From 726707eb8a822178d5f9ab9c264df767809d07fe Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 1 May 2021 16:35:31 -0400 Subject: [PATCH 7/8] Modifying rockspec --- multi/integration/shared.lua | 2 -- rockspecs/multi-15.0-0.rockspec | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 multi/integration/shared.lua diff --git a/multi/integration/shared.lua b/multi/integration/shared.lua deleted file mode 100644 index 64359e5..0000000 --- a/multi/integration/shared.lua +++ /dev/null @@ -1,2 +0,0 @@ --- Planned system threaded objects --- multi:newSystemThreadedConnection(name, protect) diff --git a/rockspecs/multi-15.0-0.rockspec b/rockspecs/multi-15.0-0.rockspec index d8df36e..9dee9ed 100644 --- a/rockspecs/multi-15.0-0.rockspec +++ b/rockspecs/multi-15.0-0.rockspec @@ -20,8 +20,9 @@ dependencies = { build = { type = "builtin", modules = { - ["multi.init"] = "multi/init.lua", + ["multi"] = "multi/init.lua", ["multi.compat.love2d"] = "multi/compat/love2d.lua", + ["multi.integration"] = "multi/integration/threading.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", -- 2.43.0 From cea6508d683f1373df80b38d9ad0b66ac8fa6c06 Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Sat, 1 May 2021 17:31:14 -0400 Subject: [PATCH 8/8] Release ready --- rockspecs/multi-14.0-0.rockspec | 10 +++++----- test.lua | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rockspecs/multi-14.0-0.rockspec b/rockspecs/multi-14.0-0.rockspec index 0ee4381..fd85aff 100644 --- a/rockspecs/multi-14.0-0.rockspec +++ b/rockspecs/multi-14.0-0.rockspec @@ -23,11 +23,11 @@ build = { ["multi"] = "multi/init.lua", ["multi.compat.love2d"] = "multi/compat/love2d.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.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.luvitManager"] = "multi/integration/luvitManager.lua", --["multi.integration.networkManager"] = "multi/integration/networkManager.lua", ["multi.integration.shared"] = "multi/integration/shared.lua" diff --git a/test.lua b/test.lua index 4887ade..af7e39c 100644 --- a/test.lua +++ b/test.lua @@ -1,4 +1,3 @@ -package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path multi,thread = require("multi"):init() GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available -- 2.43.0