diff --git a/.gitignore b/.gitignore index c9636a2..f3ede8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,2 @@ -*lua5.1 -*lua5.2 -*lua5.3 -*lua5.4 -*luajit *.code-workspace -*.dat \ No newline at end of file +lua5.4/* \ No newline at end of file diff --git a/README.md b/README.md index 3b99fd1..753e2d3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# Multi Version: 15.2.1 +# Multi Version: 15.3.0 A world of Connections **Key Changes** -- Bug fix +- SystemThreadedConnections +- Restructured the directory structure of the repo (Allows for keeping multi as a submodule and being able to require it as is) +- Bug fixes Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it! @@ -8,7 +10,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
-Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3.0) +Progress is being made in [v15.4.0](https://github.com/rayaman/multi/tree/v15.4.0) ---
@@ -16,12 +18,16 @@ Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3. INSTALLING ---------- Link to optional dependencies: -[lanes](https://github.com/LuaLanes/lanes) -[love2d](https://love2d.org/) +- [lanes](https://github.com/LuaLanes/lanes) + +- [love2d](https://love2d.org/) To install copy the multi folder into your environment and you are good to go
If you want to use the system threads, then you'll need to install lanes or love2d game engine! -**or** use luarocks `luarocks install multi` + +``` +luarocks install multi +``` Discord ------- diff --git a/Documentation.md b/docs/Documentation.md similarity index 100% rename from Documentation.md rename to docs/Documentation.md diff --git a/changes.md b/docs/changes.md similarity index 95% rename from changes.md rename to docs/changes.md index 2539005..314a545 100644 --- a/changes.md +++ b/docs/changes.md @@ -1,11 +1,173 @@ # Changelog Table of contents --- +[Update 15.3.0 - A world of connections](#update-1530---a-world-of-connections)
[Update 15.2.1 - Bug fix](#update-1521---bug-fix)
[Update 15.2.0 - Upgrade Complete](#update-1520---upgrade-complete)
[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)
[Update 15.0.0 - The art of faking it](#update-1500---the-art-of-faking-it)
[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)
[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)
[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)
[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)
[Update 13.0.0 - Added some documentation, and some new features too check it out!](#update-1300---added-some-documentation-and-some-new-features-too-check-it-out)
[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)
[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)
[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)
[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)
[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)
[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)
[Update: 1.11.0](#update-1110)
[Update: 1.10.0](#update-1100)
[Update: 1.9.2](#update-192)
[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)
[Update: 1.9.0](#update-190)
[Update: 1.8.7](#update-187)
[Update: 1.8.6](#update-186)
[Update: 1.8.5](#update-185)
[Update: 1.8.4](#update-184)
[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)
[Update: 1.8.2](#update-182)
[Update: 1.8.1](#update-181)
[Update: 1.7.6](#update-176)
[Update: 1.7.5](#update-175)
[Update: 1.7.4](#update-174)
[Update: 1.7.3](#update-173)
[Update: 1.7.2](#update-172)
[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)
[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)
[Update: 1.6.0](#update-160)
[Update: 1.5.0](#update-150)
[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)
[Update: 1.4.0 (3/20/2017)](#update-140-3202017)
[Update: 1.3.0 (1/29/2017)](#update-130-1292017)
[Update: 1.2.0 (12.31.2016)](#update-120-12312016)
[Update: 1.1.0](#update-110)
[Update: 1.0.0](#update-100)
[Update: 0.6.3](#update-063)
[Update: 0.6.2](#update-062)
[Update: 0.6.1-6](#update-061-6)
[Update: 0.5.1-6](#update-051-6)
[Update: 0.4.1](#update-041)
[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)
[Update: EventManager 2.0.0](#update-eventmanager-200)
[Update: EventManager 1.2.0](#update-eventmanager-120)
[Update: EventManager 1.1.0](#update-eventmanager-110)
[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)
[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different) +# Update 15.3.0 - A world of Connections + +Full Update Showcase +```lua +multi, thread = require("multi"):init{print=true} +GLOBAL, THREAD = require("multi.integration.lanesManager"):init() + +local conn = multi:newSystemThreadedConnection("conn"):init() + +multi:newSystemThread("Thread_Test_1",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + conn(function() + print(THREAD:getName().." was triggered!") + end) + multi:mainloop() +end) + +multi:newSystemThread("Thread_Test_2",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + conn(function(a,b,c) + print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + print("Fire 2!!!") + conn:Fire(4,5,6) + THREAD.kill() + end) + + multi:mainloop() +end) + +conn(function(a,b,c) + print("Mainloop conn got triggered!",a,b,c) +end) + +alarm = multi:newAlarm(1) +alarm:OnRing(function() + print("Fire 1!!!") + conn:Fire(1,2,3) +end) + +alarm = multi:newAlarm(3):OnRing(function() + multi:newSystemThread("Thread_Test_3",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + conn(function(a,b,c) + print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + print("Fire 3!!!") + conn:Fire(7,8,9) + end) + multi:mainloop() + end) +end) + +multi:newSystemThread("Thread_Test_4",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + local conn2 = multi:newConnection() + multi:newAlarm(2):OnRing(function() + conn2:Fire() + end) + multi:newThread(function() + print("Conn Test!") + thread.hold(conn + conn2) + print("It held!") + end) + multi:mainloop() +end) + +multi:mainloop() +``` + +Added +--- +- `multi:newConnection():Unconnect(conn_link)` Fastmode previously didn't have the ability to be unconnected to. This method works with both fastmode and non fastmode. `fastMode` will be made the default in v16.0.0 (This is a breaking change for those using the Destroy method, use this time to migrate to using `Unconnect()`) +- `thread.chain(...)` allows you to chain `thread.hold(FUNCTIONs)` together + ```lua + while true do + thread.chain(hold_function_1, hold_function_2) + end + ``` + If the first function returns true, it moves on to the next one. if expanded it follows: + ```lua + while true do + thread.hold(hold_function_1) + thread.hold(hold_function_2) + end + ``` +- Experimental option to multi settings: `findopt`. When set to `true` it will print out a message when certain pattern are used with this library. For example if an anonymous function is used in thread.hold() within a loop. The library will trigger a message alerting you that this isn't the most performant way to use thread.hold(). +- `multi:newSystemThreadedConnection()` + + Allows one to trigger connection events across threads. Works like how any connection would work. Supports all of the features, can even be `added` with non SystemThreadedConnections as demonstrated in the full showcase. +- `multi:newConnection():SetHelper(func)` + + Sets the helper function that the connection object uses when creating connection links. + +- `multi.ForEach(table, callback_function)` + + Loops through the table and calls callback_function with each element of the array. + +- If a name is not supplied when creating threads and threaded objects; a name is randomly generated. Unless sending through an established channel/queue you might not be able to easily init the object. + +Changed +--- +- Internally all `OnError` events are now connected to with multi.print, you must pass `print=true` to the init settings when initializing the multi object. `require("multi"):init{print=true}` +- All actors now use fastmode on connections +- Performance enhancement with processes that are pumped. Instead of automatically running, by suppressing the creation of an internal loop object that would manage the process, we bypass that freeing up memory and adding a bit more speed. +- `Connection:fastMode() or Connection:SetHelper()` now returns a reference to itself +- `Connection:[connect, hasConnections, getConnection]` changed to be `Connection:[Connect, HasConnections, getConnections]`. This was done in an attempt to follow a consistent naming scheme. The old methods still will work to prevent old code breaking. +- `Connections when added(+) together now act like 'or', to get the 'and' feature multiply(*) them together.` + + **Note:** This is a potentially breaking change for using connections. + + ```lua + multi, thread = require("multi"):init{print=true} + -- GLOBAL, THREAD = require("multi.integration.lanesManager"):init() + + local conn1, conn2, conn3 = multi:newConnection(), multi:newConnection(), multi:newConnection() + + thread:newThread(function() + print("Awaiting status") + thread.hold(conn1 + (conn2 * conn3)) + print("Conn or Conn2 and Conn3") + end) + + multi:newAlarm(1):OnRing(function() + print("Conn") + conn1:Fire() + end) + + multi:newAlarm(2):OnRing(function() + print("Conn2") + conn2:Fire() + end) + + multi:newAlarm(3):OnRing(function() + print("Conn3") + conn3:Fire() + end) + ``` + +Removed +--- +- Connection objects methods removed: + - holdUT(), HoldUT() -- With the way `thread.hold(conn)` interacts with connections this method was no longer needed. To emulate this use `multi.hold(conn)`. `multi.hold()` is able to emulate what `thread.hold()` outside of a thread, albeit with some drawbacks. + +Fixed +--- +- SystemThreaded Objects variables weren't consistent. +- Issue with connections being multiplied only being able to have a combined fire once + +ToDo +--- + +- Work on network parallelism (I am really excited to start working on this. Not because it will have much use, but because it seems like a cool addition/project to work on. I just need time to actually do work on stuff) + # Update 15.2.1 - Bug fix Fixed issue #41 +--- # Update 15.2.0 - Upgrade Complete diff --git a/multi/init.lua b/init.lua similarity index 86% rename from multi/init.lua rename to init.lua index 5716186..bf65b70 100644 --- a/multi/init.lua +++ b/init.lua @@ -29,12 +29,13 @@ local clock = os.clock local thread = {} local in_proc = false local processes = {} +local find_optimization = false if not _G["$multi"] then _G["$multi"] = {multi=multi,thread=thread} end -multi.Version = "15.2.1" +multi.Version = "15.3.0" multi.Name = "root" multi.NIL = {Type="NIL"} local NIL = multi.NIL @@ -58,15 +59,15 @@ multi.Priority_Very_Low = 16384 multi.Priority_Idle = 65536 multi.PriorityResolve = { - [1]="Core", - [4]="Very High", - [16]="High", - [64]="Above Normal", - [256]="Normal", - [1024]="Below Normal", - [4096]="Low", - [16384]="Very Low", - [65536]="Idle", + [1] = "Core", + [4] = "Very High", + [16] = "High", + [64] = "Above Normal", + [256] = "Normal", + [1024] = "Below Normal", + [4096] = "Low", + [16384] = "Very Low", + [65536] = "Idle", } local PList = {multi.Priority_Core,multi.Priority_Very_High,multi.Priority_High,multi.Priority_Above_Normal,multi.Priority_Normal,multi.Priority_Below_Normal,multi.Priority_Low,multi.Priority_Very_Low,multi.Priority_Idle} @@ -109,6 +110,15 @@ function multi:getStats() end --Helpers + +function multi.ForEach(tab,func) + for i=1,#tab do func(tab[i]) end +end +local CRef = { + Fire = function() end +} + +local optimization_stats = {} local ignoreconn = true function multi:newConnection(protect,func,kill) local c={} @@ -116,70 +126,75 @@ function multi:newConnection(protect,func,kill) local lock = false c.callback = func c.Parent=self + setmetatable(c,{__call=function(self,...) local t = ... if type(t)=="table" then for i,v in pairs(t) do if v==self then - local ref = self:connect(select(2,...)) - ref.root_link = select(1,...) - return ref + local ref = self:Connect(select(2,...)) + if ref then + ref.root_link = select(1,...) + return ref + end + return self end end - return self:connect(...) + return self:Connect(...) else - return self:connect(...) + return self:Connect(...) end end, - __add = function(c1,c2) - cn = multi:newConnection() - if not c1.__hasInstances then - cn.__hasInstances = 2 - cn.__count = 0 - else - cn.__hasInstances = c1.__hasInstances + 1 - cn.__count = c1.__count - end + __add = function(c1,c2) -- Or + local cn = multi:newConnection() c1(function(...) - cn.__count = cn.__count + 1 - if cn.__count == cn.__hasInstances then - cn:Fire(...) - end + cn:Fire(...) end) c2(function(...) - cn.__count = cn.__count + 1 - if cn.__count == cn.__hasInstances then + cn:Fire(...) + end) + return cn + end, + __mul = function(c1,c2) -- And + local cn = multi:newConnection() + if c1.__hasInstances == nil then + cn.__hasInstances = {2} + cn.__count = {0} + else + cn.__hasInstances = c1.__hasInstances + cn.__hasInstances[1] = cn.__hasInstances[1] + 1 + cn.__count = c1.__count + end + + c1(function(...) + cn.__count[1] = cn.__count[1] + 1 + if cn.__count[1] == cn.__hasInstances[1] then cn:Fire(...) + cn.__count[1] = 0 + end + end) + + c2(function(...) + cn.__count[1] = cn.__count[1] + 1 + if cn.__count[1] == cn.__hasInstances[1] then + cn:Fire(...) + cn.__count[1] = 0 end end) return cn end}) + c.Type='connector' c.func={} c.ID=0 local protect=protect or false local connections={} c.FC=0 + function c:hasConnections() return #call_funcs~=0 end - function c:holdUT(n) - local n=n or 0 - self.waiting=true - local count=0 - local id=self:connect(function() - count = count + 1 - if n<=count then - self.waiting=false - end - end) - repeat - self.Parent:uManager() - until self.waiting==false - id:Destroy() - return self - end - c.HoldUT=c.holdUT + function c:getConnection(name,ignore) if ignore then return connections[name] or CRef @@ -187,20 +202,26 @@ function multi:newConnection(protect,func,kill) return connections[name] or self end end + function c:Lock() lock = true return self end + function c:Unlock() lock = false return self end + if protect then function c:Fire(...) if lock then return end for i=#call_funcs,1,-1 do if not call_funcs[i] then return end - pcall(call_funcs[i],...) + local suc, err = pcall(call_funcs[i],...) + if not suc then + print(err) + end if kill then table.remove(call_funcs,i) end @@ -208,6 +229,7 @@ function multi:newConnection(protect,func,kill) end else function c:Fire(...) + if lock then return end for i=#call_funcs,1,-1 do call_funcs[i](...) if kill then @@ -216,48 +238,90 @@ function multi:newConnection(protect,func,kill) end end end + local fast = {} function c:getConnections() return call_funcs end + + function c:Unconnect(conn) + if conn.fast then + for i = 1, #fast do + if conn.ref == fast[i] then + table.remove(fast, i) + end + end + elseif conn.Destroy then + conn:Destroy() + end + end + function c:fastMode() + if find_optimization then return self end function self:Fire(...) for i=1,#fast do fast[i](...) end end - function self:connect(func) - table.insert(fast,func) + function self:Connect(func) + table.insert(fast, func) + local temp = {fast = true} + setmetatable(temp,{ + __call=function(s,...) + return self:Connect(...) + end, + __index = function(t,k) + if rawget(t,"root_link") then + return t["root_link"][k] + end + return nil + end, + __newindex = function(t,k,v) + if rawget(t,"root_link") then + t["root_link"][k] = v + end + rawset(t,k,v) + end, + }) + temp.ref = func + return temp end + return self end + function c:Bind(t) local temp = call_funcs call_funcs=t return temp end + function c:Remove() local temp = call_funcs call_funcs={} return temp end + local function conn_helper(self,func,name,num) self.ID=self.ID+1 + if num then table.insert(call_funcs,num,func) else table.insert(call_funcs,1,func) end + local temp = { func=func, Type="connector_link", Parent=self, connect = function(s,...) - return self:connect(...) + return self:Connect(...) end } + setmetatable(temp,{ __call=function(s,...) - return self:connect(...) + return self:Connect(...) end, __index = function(t,k) if rawget(t,"root_link") then @@ -272,18 +336,13 @@ function multi:newConnection(protect,func,kill) rawset(t,k,v) end, }) + function temp:Fire(...) - if lock then return end - if protect then - local t=pcall(call_funcs,...) - if t then - return t - end - else - return call_funcs(...) - end + return call_funcs(...) end + function temp:Destroy() + multi.print("Calling Destroy on a connection link is deprecated and will be removed in v16.0.0") for i=#call_funcs,1,-1 do if call_funcs[i]~=nil then if call_funcs[i]==self.func then @@ -294,15 +353,19 @@ function multi:newConnection(protect,func,kill) end end end + if name then connections[name]=temp end + if self.callback then self.callback(temp) end + return temp end - function c:connect(...)--func,name,num + + function c:Connect(...)--func,name,num local tab = {...} local funcs={} for i=1,#tab do @@ -320,14 +383,45 @@ function multi:newConnection(protect,func,kill) return conn_helper(self,tab[1],tab[2],tab[3]) end end - c.Connect=c.connect + + function c:SetHelper(func) + conn_helper = func + return self + end + + if find_optimization then + -- + end + + c.connect=c.Connect c.GetConnection=c.getConnection + c.HasConnections = c.hasConnections + c.GetConnection = c.getConnection + if not(ignoreconn) then multi:create(c) end return c end +multi.enableOptimization = multi:newConnection() +multi.optConn = multi:newConnection(true) +multi.optConn(function(msg) + table.insert(optimization_stats, msg) +end) + +function multi:getOptimizationConnection() + return multi.optConn +end + +function multi:getOptimizationStats() + return optimization_stats +end + +function multi:isFindingOptimizing() + return find_optimization +end + -- Used with ISO Threads local function isolateFunction(func,env) local dmp = string.dump(func) @@ -508,10 +602,6 @@ function multi:newConnector() return c end -local CRef = { - Fire = function() end -} - multi.OnObjectCreated=multi:newConnection() multi.OnObjectDestroyed=multi:newConnection() multi.OnLoad = multi:newConnection(nil,nil,true) @@ -565,7 +655,7 @@ function multi:newEvent(task) task=func return self end - c.OnEvent = self:newConnection() + c.OnEvent = self:newConnection():fastMode() self:setPriority("core") c:SetName(c.Type) multi:create(c) @@ -588,7 +678,7 @@ function multi:newUpdater(skip) skip=n return self end - c.OnUpdate = self:newConnection() + c.OnUpdate = self:newConnection():fastMode() c:SetName(c.Type) multi:create(c) return c @@ -620,7 +710,7 @@ function multi:newAlarm(set) t = clock() return self end - c.OnRing = self:newConnection() + c.OnRing = self:newConnection():fastMode() function c:Pause() count = clock() self.Parent.Pause(self) @@ -644,15 +734,12 @@ function multi:newLoop(func,notime) self.OnLoop:Fire(self,clock()-start) end end - c.OnLoop = self:newConnection() - function c:fastMode() - self.OnLoop:fastMode() - end + c.OnLoop = self:newConnection():fastMode() if func then c.OnLoop(func) end - + multi:create(c) c:SetName(c.Type) return c @@ -694,9 +781,9 @@ function multi:newStep(start,reset,count,skip) end end c.Reset=c.Resume - c.OnStart = self:newConnection() - c.OnStep = self:newConnection() - c.OnEnd = self:newConnection() + c.OnStart = self:newConnection():fastMode() + c.OnStep = self:newConnection():fastMode() + c.OnEnd = self:newConnection():fastMode() function c:Break() self.Active=nil return self @@ -744,7 +831,7 @@ function multi:newTLoop(func,set) self.Parent.Pause(self) return self end - c.OnLoop = self:newConnection() + c.OnLoop = self:newConnection():fastMode() if func then c.OnLoop(func) end @@ -803,6 +890,32 @@ function multi:newTStep(start,reset,count,set) return c end +local tasks = {} +local _tasks = 0 + +local function _task_handler() + tasks[#tasks + 1] = func + _tasks = _tasks + 1 +end + +function multi:newTask(func) + multi:newThread("Task Handler",function() + while true do + thread.hold(function() + return _tasks > 0 + end) + for i=1,_tasks do + tasks[i]() + end + _tasks = 0 + end + end) + -- Re bind this method to use the one that doesn't init a thread! + multi.newTask = _task_handler + tasks[#tasks + 1] = func + _tasks = _tasks + 1 +end + local scheduledjobs = {} local sthread @@ -864,25 +977,28 @@ function multi:newProcessor(name,nothread) c.Type = "process" local Active = nothread or false c.Name = name or "" - c.pump = false c.threads = {} c.startme = {} c.parent = self local handler = c:createHandler(c.threads,c.startme) - c.process = self:newLoop(function() - if Active then - c:uManager() - handler() - end - end) + if not nothread then -- Don't create a loop if we are triggering this manually + c.process = self:newLoop(function() + if Active then + c:uManager() + handler() + end + end) + c.process.__ignore = true + c.process.isProcessThread = true + c.process.PID = sandcount + c.OnError = c.process.OnError + else + c.OnError = multi:newConnection() + end + c.OnError(multi.print) - c.process.__ignore = true - - c.process.isProcessThread = true - c.process.PID = sandcount - c.OnError = c.process.OnError function c:getThreads() return c.threads @@ -911,10 +1027,8 @@ function multi:newProcessor(name,nothread) function c.run() if not Active then return end - c.pump = true c:uManager() handler() - c.pump = false return c end @@ -1033,6 +1147,28 @@ function thread.sleep(n) return yield(CMD, t_sleep, n or 1) end +local function conn_test(conn) + local ready = false + local args + local func = function(...) + ready = true + args = {...} + end + conn(func) + return function() + if ready then + return unpack(args) or multi.NIL + end + end +end + +function thread.chain(...) + local args = select("#",...) + for i=1,args do + thread.hold(select(i,...)) + end +end + function thread.hold(n,opt) thread._Requests() local opt = opt or {} @@ -1051,20 +1187,7 @@ function thread.hold(n,opt) thread.getRunningThread().lastSleep = clock() return yield(CMD, t_sleep, n or 0, nil, interval) elseif type(n) == "table" and n.Type == "connector" then - local rdy = function() - return false - end - n(function(a1,a2,a3,a4,a5,a6) - rdy = function() - if a1==nil then - return NIL,a2,a3,a4,a5,a6 - end - return a1,a2,a3,a4,a5,a6 - end - end) - return yield(CMD, t_hold, function() - return rdy() - end, nil, interval) + return yield(CMD, t_hold, conn_test(n), nil, interval) elseif type(n) == "function" then return yield(CMD, t_hold, n or dFunc, nil, interval) else @@ -1180,7 +1303,7 @@ function thread:newFunctionBase(generator,holdme) end end tfunc.__call = function(t,...) - if not t.Active then + if t.Active == false then if holdme then return nil, "Function is paused" end @@ -1244,9 +1367,8 @@ local startme_len = 0 function thread:newThread(name,func,...) multi.OnLoad:Fire() -- This was done incase a threaded function was called before mainloop/uManager was called local func = func or name - - if type(name) == "function" then - name = "Thread#"..threadCount + if func == name then + name = name or multi.randomString(16) end local c={nil,nil,nil,nil,nil,nil,nil} local env = {self=c} @@ -1264,6 +1386,7 @@ function thread:newThread(name,func,...) c.isError = false c.OnError = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true) + c.OnError(multi.print) function c:getName() return c.Name @@ -1471,7 +1594,6 @@ co_status = { switch[task](ref,thd) cmds[r1](ref,r2,r3,r4,r5) if ret ~= CMD and _ ~= nil then -- The rework makes this necessary - print("Hello") co_status["dead"](thd,ref,task,i,th) end r1=nil r2=nil r3=nil r4=nil r5=nil @@ -1725,6 +1847,10 @@ function multi.init(settings, realsettings) else multi.mainloop = mainloop end + if settings.findopt then + find_optimization = true + multi.enableOptimization:Fire(multi, thread) + end end return _G["$multi"].multi,_G["$multi"].thread end @@ -2046,14 +2172,14 @@ function multi.print(...) end end -multi.GetType=multi.getType -multi.IsPaused=multi.isPaused -multi.IsActive=multi.isActive -multi.Reallocate=multi.Reallocate -multi.ConnectFinal=multi.connectFinal -multi.ResetTime=multi.SetTime -multi.IsDone=multi.isDone -multi.SetName = multi.setName +multi.GetType = multi.getType +multi.IsPaused = multi.isPaused +multi.IsActive = multi.isActive +multi.Reallocate = multi.Reallocate +multi.ConnectFinal = multi.connectFinal +multi.ResetTime = multi.SetTime +multi.IsDone = multi.isDone +multi.SetName = multi.setName -- Special Events local _os = os.exit @@ -2074,5 +2200,64 @@ else multi.m.sentinel = newproxy(true) getmetatable(multi.m.sentinel).__gc = multi.m.onexit end +local func_cache = {} +multi:newThread(function() + thread.skip() + if find_optimization then + + function thread.hold(n,opt) + thread._Requests() + local opt = opt or {} + if type(opt)=="table" then + interval = opt.interval + if opt.cycles then + return yield(CMD, t_holdW, opt.cycles or 1, n or dFunc, interval) + elseif opt.sleep then + return yield(CMD, t_holdF, opt.sleep, n or dFunc, interval) + elseif opt.skip then + return yield(CMD, t_skip, opt.skip or 1, nil, interval) + end + end + + if type(n) == "number" then + thread.getRunningThread().lastSleep = clock() + return yield(CMD, t_sleep, n or 0, nil, interval) + elseif type(n) == "table" and n.Type == "connector" then + local rdy = function() + return false + end + n(function(a1,a2,a3,a4,a5,a6) + rdy = function() + if a1==nil then + return NIL,a2,a3,a4,a5,a6 + end + return a1,a2,a3,a4,a5,a6 + end + end) + return yield(CMD, t_hold, function() + return rdy() + end, nil, interval) + elseif type(n) == "function" then + local cache = string.dump(n) + local f_str = tostring(n) + local good = true + for i=1,#func_cache do + if func_cache[i][1] == cache and func_cache[i][2] ~= f_str and not func_cache[i][3] then + multi:getOptimizationConnection():Fire("It's better to store a function to a variable than to use an anonymous function within the hold method!\n" .. debug.traceback()) + func_cache[i][3] = true + good = false + end + end + if good then + table.insert(func_cache, {cache, f_str}) + end + return yield(CMD, t_hold, n or dFunc, nil, interval) + else + error("Invalid argument passed to thread.hold(...)!") + end + end + -- Add more Overrides + end +end) return multi \ No newline at end of file diff --git a/multi/integration/lanesManager/extensions.lua b/integration/lanesManager/extensions.lua similarity index 57% rename from multi/integration/lanesManager/extensions.lua rename to integration/lanesManager/extensions.lua index 198e551..9600dc6 100644 --- a/multi/integration/lanesManager/extensions.lua +++ b/integration/lanesManager/extensions.lua @@ -22,9 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] local multi, thread = require("multi"):init() -local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD +if not (GLOBAL and THREAD) then + local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD +else + lanes = require("lanes") +end function multi:newSystemThreadedQueue(name) + local name = name or multi.randomString(16) local c = {} + c.Name = name c.linda = lanes.linda() function c:push(v) self.linda:send("Q", v) @@ -41,9 +47,12 @@ function multi:newSystemThreadedQueue(name) GLOBAL[name or "_"] = c return c end + function multi:newSystemThreadedTable(name) + local name = name or multi.randomString(16) local c = {} c.link = lanes.linda() + c.Name = name setmetatable(c,{ __index = function(t,k) return c.link:get(k) @@ -58,14 +67,15 @@ function multi:newSystemThreadedTable(name) GLOBAL[name or "_"] = c return c end + function multi:newSystemThreadedJobQueue(n) local c = {} c.cores = n or THREAD.getCores()*2 c.OnJobCompleted = multi:newConnection() - local funcs = multi:newSystemThreadedTable() - local queueJob = multi:newSystemThreadedQueue() - local queueReturn = multi:newSystemThreadedQueue() - local doAll = multi:newSystemThreadedQueue() + local funcs = multi:newSystemThreadedTable():init() + local queueJob = multi:newSystemThreadedQueue():init() + local queueReturn = multi:newSystemThreadedQueue():init() + local doAll = multi:newSystemThreadedQueue():init() local ID=1 local jid = 1 function c:isEmpty() @@ -168,4 +178,135 @@ function multi:newSystemThreadedJobQueue(n) end,i).priority = thread.Priority_Core end return c -end \ No newline at end of file +end + +function multi:newSystemThreadedConnection(name) + local name = name or multi.randomString(16) + local c = {} + c.CONN = 0x00 + c.TRIG = 0x01 + c.PING = 0x02 + c.PONG = 0x03 + local function remove(a, b) + local ai = {} + local r = {} + for k,v in pairs(a) do ai[v]=true end + for k,v in pairs(b) do + if ai[v]==nil then table.insert(r,a[k]) end + end + return r + end + c.CID = THREAD.getID() + c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init() + c.Name = name + c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. + -- Locals will only live in the thread that creates the original object + local ping + local pong = function(link, links) + local res = thread.hold(function() + return link:peek()[1] == c.PONG + end,{sleep=3}) + + if not res then + for i=1,#links do + if links[i] == link then + table.remove(links,i,link) + break + end + end + else + link:pop() + end + end + + ping = thread:newFunction(function(self) + ping:Pause() + multi.ForEach(self.links, function(link) -- Sync new connections + link:push{self.PING} + multi:newThread("pong Thread", pong, link, self.links) + end) + + thread.sleep(3) + + ping:Resume() + end,false) + + local function fire(...) + for _, link in pairs(c.links) do + link:push {c.TRIG, {...}} + end + end + + thread:newThread("STC_SUB_MAN"..name,function() + local item + local sub_func = function() -- This will keep things held up until there is something to process + return c.subscribe:pop() + end + while true do + thread.yield() + -- We need to check on broken connections + ping(c) -- Should return instantlly and process this in another thread + item = thread.hold(sub_func) + if item[1] == c.CONN then + multi.ForEach(c.links, function(link) -- Sync new connections + item[2]:push{c.CONN, link} + end) + c.links[#c.links+1] = item[2] + elseif item[1] == c.TRIG then + fire(unpack(item[2])) + c.proxy_conn:Fire(unpack(item[2])) + end + end + end) + --- ^^^ This will only exist in the init thread + + function c:Fire(...) + local args = {...} + if self.CID == THREAD.getID() then -- Host Call + for _, link in pairs(self.links) do + link:push {self.TRIG, args} + end + self.proxy_conn:Fire(...) + else + self.subscribe:push {self.TRIG, args} + end + end + + function c:init() + local multi, thread = require("multi"):init() + self.links = {} + self.proxy_conn = multi:newConnection() + local mt = getmetatable(self.proxy_conn) + setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add}) + if self.CID == THREAD.getID() then return self end + thread:newThread("STC_CONN_MAN"..name,function() + local item + local link_self_ref = multi:newSystemThreadedQueue() + self.subscribe:push{self.CONN, link_self_ref} + while true do + item = thread.hold(function() + return link_self_ref:peek() + end) + if item[1] == self.PING then + link_self_ref:push{self.PONG} + link_self_ref:pop() + elseif item[1] == self.CONN then + if item[2].Name ~= link_self_ref.Name then + table.insert(self.links, item[2]) + end + link_self_ref:pop() + elseif item[1] == self.TRIG then + self.proxy_conn:Fire(unpack(item[2])) + link_self_ref:pop() + else + -- This shouldn't be the case + end + end + end) + return self + end + + GLOBAL[name] = c + + return c +end \ No newline at end of file diff --git a/multi/integration/lanesManager/init.lua b/integration/lanesManager/init.lua similarity index 93% rename from multi/integration/lanesManager/init.lua rename to integration/lanesManager/init.lua index 4bada5a..d53e12f 100644 --- a/multi/integration/lanesManager/init.lua +++ b/integration/lanesManager/init.lua @@ -50,7 +50,7 @@ local __SleepingLinda = lanes.linda() -- handles sleeping stuff local __ConsoleLinda = lanes.linda() -- handles console stuff local __StatusLinda = lanes.linda() -- handles pushstatus for stfunctions -local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda, __SleepingLinda, __StatusLinda) +local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda, __SleepingLinda, __StatusLinda, __ConsoleLinda) local count = 1 local started = false local livingThreads = {} @@ -62,6 +62,7 @@ function THREAD:newFunction(func,holdme) end function multi:newSystemThread(name, func, ...) + local name = name or multi.randomString(16) multi.InitSystemThreadErrorHandler() local rand = math.random(1, 10000000) local return_linda = lanes.linda() @@ -76,6 +77,10 @@ function multi:newSystemThread(name, func, ...) c.creationTime = os.clock() c.alive = true c.priority = THREAD.Priority_Normal + local multi_settings = multi.defaultSettings + for i,v in pairs(multi_settings) do + print(i,v) + end c.thread = lanes.gen("*", { globals={ -- Set up some globals @@ -87,6 +92,8 @@ function multi:newSystemThread(name, func, ...) }, priority=c.priority },function(...) + require("multi"):init(multi_settings) + require("multi.integration.lanesManager.extensions") local has_error = true return_linda:set("returns",{func(...)}) has_error = false @@ -119,6 +126,7 @@ function multi.InitSystemThreadErrorHandler() while true do thread.yield() _,data = __ConsoleLinda:receive(0, "Q") + if data then print(unpack(data)) end for i = #threads, 1, -1 do temp = threads[i] status = temp.thread.status diff --git a/multi/integration/lanesManager/threads.lua b/integration/lanesManager/threads.lua similarity index 95% rename from multi/integration/lanesManager/threads.lua rename to integration/lanesManager/threads.lua index 68cb733..f1b8da1 100644 --- a/multi/integration/lanesManager/threads.lua +++ b/integration/lanesManager/threads.lua @@ -29,7 +29,7 @@ local function getOS() end end -local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda) +local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console) local THREAD = {} THREAD.Priority_Core = 3 THREAD.Priority_High = 2 @@ -70,12 +70,12 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda) function THREAD.getConsole() local c = {} - c.queue = _Console + c.queue = __Console function c.print(...) c.queue:send("Q", {...}) end function c.error(err) - c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__} + c.queue:push("Q",{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}) error(err) end return c @@ -137,6 +137,6 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda) return GLOBAL, THREAD end -return {init = function(g,s,st) - return INIT(g,s,st) +return {init = function(g,s,st,c) + return INIT(g,s,st,c) end} \ No newline at end of file diff --git a/multi/integration/loveManager/extensions.lua b/integration/loveManager/extensions.lua similarity index 58% rename from multi/integration/loveManager/extensions.lua rename to integration/loveManager/extensions.lua index cf6beb2..da5b560 100644 --- a/multi/integration/loveManager/extensions.lua +++ b/integration/loveManager/extensions.lua @@ -22,11 +22,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] --- TODO make compatible with lovr -local multi, thread = require("multi").init() -GLOBAL = multi.integration.GLOBAL -THREAD = multi.integration.THREAD +if not ISTHREAD then + multi, thread = require("multi").init() + GLOBAL = multi.integration.GLOBAL + THREAD = multi.integration.THREAD +else + GLOBAL = multi.integration.GLOBAL + THREAD = multi.integration.THREAD +end + function multi:newSystemThreadedQueue(name) + local name = name or multi.randomString(16) local c = {} c.Name = name local fRef = {"func",nil} @@ -64,10 +70,11 @@ function multi:newSystemThreadedQueue(name) return c end function multi:newSystemThreadedTable(name) + local name = name or multi.randomString(16) local c = {} - c.name = name + c.Name = name function c:init() - return THREAD.createTable(self.name) + return THREAD.createTable(self.Name) end THREAD.package(name,c) return c @@ -201,5 +208,144 @@ function multi:newSystemThreadedJobQueue(n) end,jqc) end jqc = jqc + 1 + return c +end + +function multi:newSystemThreadedConnection(name) + local name = name or multi.randomString(16) + local c = {} + c.CONN = 0x00 + c.TRIG = 0x01 + c.PING = 0x02 + c.PONG = 0x03 + + local subscribe = love.thread.getChannel("SUB_STC_" .. name) + + function c:init() + + self.subscribe = love.thread.getChannel("SUB_STC_" .. self.Name) + + function self:Fire(...) + local args = {...} + if self.CID == THREAD.getID() then -- Host Call + for _, link in pairs(self.links) do + love.thread.getChannel(link):push{self.TRIG, args} + end + self.proxy_conn:Fire(...) + else + self.subscribe:push{self.TRIG, args} + end + end + + local multi, thread = require("multi"):init() + self.links = {} + self.proxy_conn = multi:newConnection() + local mt = getmetatable(self.proxy_conn) + setmetatable(self, {__index = self.proxy_conn, __call = function(t,func) self.proxy_conn(func) end, __add = mt.__add}) + if self.CID == THREAD.getID() then return self end + thread:newThread("STC_CONN_MAN" .. self.Name,function() + local item + local string_self_ref = "LSF_" .. multi.randomString(16) + local link_self_ref = love.thread.getChannel(string_self_ref) + self.subscribe:push{self.CONN, string_self_ref} + while true do + item = thread.hold(function() + return link_self_ref:peek() + end) + if item[1] == self.PING then + link_self_ref:push{self.PONG} + link_self_ref:pop() + elseif item[1] == self.CONN then + if string_self_ref ~= item[2] then + table.insert(self.links, love.thread.getChannel(item[2])) + end + link_self_ref:pop() + elseif item[1] == self.TRIG then + self.proxy_conn:Fire(unpack(item[2])) + link_self_ref:pop() + else + -- This shouldn't be the case + end + end + end).OnError(print) + return self + end + + local function remove(a, b) + local ai = {} + local r = {} + for k,v in pairs(a) do ai[v]=true end + for k,v in pairs(b) do + if ai[v]==nil then table.insert(r,a[k]) end + end + return r + end + c.CID = THREAD.getID() + c.Name = name + c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out. + -- Locals will only live in the thread that creates the original object + local ping + local pong = function(link, links) + local res = thread.hold(function() + return love.thread.getChannel(link):peek()[1] == c.PONG + end,{sleep=3}) + + if not res then + for i=1,#links do + if links[i] == link then + table.remove(links,i,link) + break + end + end + else + love.thread.getChannel(link):pop() + end + end + + ping = thread:newFunction(function(self) + ping:Pause() + + multi.ForEach(self.links, function(link) -- Sync new connections + love.thread.getChannel(link):push{self.PING} + multi:newThread("pong Thread", pong, link, self.links) + end) + + thread.sleep(3) + + ping:Resume() + end,false) + + local function fire(...) + for _, link in pairs(c.links) do + love.thread.getChannel(link):push {c.TRIG, {...}} + end + end + + thread:newThread("STC_SUB_MAN"..name,function() + local item + while true do + thread.yield() + -- We need to check on broken connections + ping(c) -- Should return instantlly and process this in another thread + item = thread.hold(function() -- This will keep things held up until there is something to process + return c.subscribe:pop() + end) + if item[1] == c.CONN then + + multi.ForEach(c.links, function(link) -- Sync new connections + love.thread.getChannel(item[2]):push{c.CONN, link} + end) + c.links[#c.links+1] = item[2] + + elseif item[1] == c.TRIG then + fire(unpack(item[2])) + c.proxy_conn:Fire(unpack(item[2])) + end + end + end).OnError(print) + --- ^^^ This will only exist in the init thread + + THREAD.package(name,c) + return c end \ No newline at end of file diff --git a/multi/integration/loveManager/init.lua b/integration/loveManager/init.lua similarity index 91% rename from multi/integration/loveManager/init.lua rename to integration/loveManager/init.lua index 93a5a2c..dd0e929 100644 --- a/multi/integration/loveManager/init.lua +++ b/integration/loveManager/init.lua @@ -26,15 +26,23 @@ if ISTHREAD then end local ThreadFileData = [[ ISTHREAD = true -THREAD = require("multi.integration.loveManager.threads") -- order is important! +THREAD = require("multi.integration.loveManager.threads") sThread = THREAD __IMPORTS = {...} __FUNC__=table.remove(__IMPORTS,1) __THREADID__=table.remove(__IMPORTS,1) __THREADNAME__=table.remove(__IMPORTS,1) +math.randomseed(__THREADID__) +math.random() +math.random() +math.random() stab = THREAD.createStaticTable(__THREADNAME__) GLOBAL = THREAD.getGlobal() multi, thread = require("multi").init() +multi.integration={} +multi.integration.GLOBAL = GLOBAL +multi.integration.THREAD = THREAD +pcall(require,"multi.integration.loveManager.extensions") stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))} ]] local multi, thread = require("multi"):init() @@ -42,7 +50,6 @@ local THREAD = {} __THREADID__ = 0 __THREADNAME__ = "MainThread" multi.integration={} -multi.integration.love2d={} local THREAD = require("multi.integration.loveManager.threads") local GLOBAL = THREAD.getGlobal() local THREAD_ID = 1 @@ -103,13 +110,13 @@ end THREAD.newSystemThread = multi.newSystemThread function love.threaderror(thread, errorstr) - mulit.print("Thread error!\n"..errorstr) + multi.print("Thread error!\n"..errorstr) end multi.integration.GLOBAL = GLOBAL multi.integration.THREAD = THREAD require("multi.integration.loveManager.extensions") -mulit.print("Integrated Love Threading!") +multi.print("Integrated Love Threading!") return {init=function() return GLOBAL,THREAD end} \ No newline at end of file diff --git a/multi/integration/loveManager/threads.lua b/integration/loveManager/threads.lua similarity index 99% rename from multi/integration/loveManager/threads.lua rename to integration/loveManager/threads.lua index c66e7dc..d867d6d 100644 --- a/multi/integration/loveManager/threads.lua +++ b/integration/loveManager/threads.lua @@ -106,7 +106,7 @@ function threads.kill() error("Thread Killed!\1") end -function THREAD.pushStatus(...) +function threads.pushStatus(...) local status_channel = love.thread.getChannel("__"..__THREADID__.."__MULTI__STATUS_CHANNEL__") local args = {...} status_channel:push(__THREADID__, args) diff --git a/multi/integration/lovrManager/extensions.lua b/integration/lovrManager/extensions.lua similarity index 100% rename from multi/integration/lovrManager/extensions.lua rename to integration/lovrManager/extensions.lua diff --git a/multi/integration/lovrManager/init.lua b/integration/lovrManager/init.lua similarity index 100% rename from multi/integration/lovrManager/init.lua rename to integration/lovrManager/init.lua diff --git a/multi/integration/lovrManager/threads.lua b/integration/lovrManager/threads.lua similarity index 100% rename from multi/integration/lovrManager/threads.lua rename to integration/lovrManager/threads.lua diff --git a/multi/integration/luvitManager.lua b/integration/luvitManager.lua similarity index 100% rename from multi/integration/luvitManager.lua rename to integration/luvitManager.lua diff --git a/multi/integration/networkManager/channel.lua b/integration/networkManager/channel.lua similarity index 100% rename from multi/integration/networkManager/channel.lua rename to integration/networkManager/channel.lua diff --git a/multi/integration/networkManager/childNode.lua b/integration/networkManager/childNode.lua similarity index 100% rename from multi/integration/networkManager/childNode.lua rename to integration/networkManager/childNode.lua diff --git a/multi/integration/networkManager/clientSide.lua b/integration/networkManager/clientSide.lua similarity index 100% rename from multi/integration/networkManager/clientSide.lua rename to integration/networkManager/clientSide.lua diff --git a/multi/integration/networkManager/cmds.lua b/integration/networkManager/cmds.lua similarity index 100% rename from multi/integration/networkManager/cmds.lua rename to integration/networkManager/cmds.lua diff --git a/multi/integration/networkManager/extensions.lua b/integration/networkManager/extensions.lua similarity index 100% rename from multi/integration/networkManager/extensions.lua rename to integration/networkManager/extensions.lua diff --git a/multi/integration/networkManager/init.lua b/integration/networkManager/init.lua similarity index 98% rename from multi/integration/networkManager/init.lua rename to integration/networkManager/init.lua index 23fcd7e..78e10e2 100644 --- a/multi/integration/networkManager/init.lua +++ b/integration/networkManager/init.lua @@ -23,7 +23,7 @@ SOFTWARE. ]] local multi, thread = require("multi"):init() local net = require("net") -local bin = require("bin") +--local bin = require("bin") local char = string.char local byte = string.byte bin.setBitsInterface(infinabits) diff --git a/multi/integration/networkManager/masterNode.lua b/integration/networkManager/masterNode.lua similarity index 100% rename from multi/integration/networkManager/masterNode.lua rename to integration/networkManager/masterNode.lua diff --git a/multi/integration/networkManager/node.lua b/integration/networkManager/node.lua similarity index 100% rename from multi/integration/networkManager/node.lua rename to integration/networkManager/node.lua diff --git a/multi/integration/networkManager/nodeManager.lua b/integration/networkManager/nodeManager.lua similarity index 100% rename from multi/integration/networkManager/nodeManager.lua rename to integration/networkManager/nodeManager.lua diff --git a/multi/integration/networkManager/serverSide.lua b/integration/networkManager/serverSide.lua similarity index 100% rename from multi/integration/networkManager/serverSide.lua rename to integration/networkManager/serverSide.lua diff --git a/multi/integration/networkManager/threads.lua b/integration/networkManager/threads.lua similarity index 100% rename from multi/integration/networkManager/threads.lua rename to integration/networkManager/threads.lua diff --git a/multi/integration/networkManager/utils.lua b/integration/networkManager/utils.lua similarity index 100% rename from multi/integration/networkManager/utils.lua rename to integration/networkManager/utils.lua diff --git a/multi/integration/pesudoManager/extensions.lua b/integration/pesudoManager/extensions.lua similarity index 100% rename from multi/integration/pesudoManager/extensions.lua rename to integration/pesudoManager/extensions.lua diff --git a/multi/integration/pesudoManager/init.lua b/integration/pesudoManager/init.lua similarity index 100% rename from multi/integration/pesudoManager/init.lua rename to integration/pesudoManager/init.lua diff --git a/multi/integration/pesudoManager/threads.lua b/integration/pesudoManager/threads.lua similarity index 100% rename from multi/integration/pesudoManager/threads.lua rename to integration/pesudoManager/threads.lua diff --git a/multi/integration/threading.lua b/integration/threading.lua similarity index 100% rename from multi/integration/threading.lua rename to integration/threading.lua diff --git a/makeENV.lua b/makeENV.lua deleted file mode 100644 index acb6fc0..0000000 --- a/makeENV.lua +++ /dev/null @@ -1,19 +0,0 @@ -commands = [[ -mkdir luajit && python -m hererocks -j 2.1.0-beta3 -r latest --patch --compat all ./luajit && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.1 && python -m hererocks -l 5.1 -r latest --patch --compat all ./lua5.1 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.2 && python -m hererocks -l 5.2 -r latest --patch --compat all ./lua5.2 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.3 && python -m hererocks -l 5.3 -r latest --patch --compat all ./lua5.3 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -mkdir lua5.4 && python -m hererocks -l 5.4 -r latest --patch --compat all ./lua5.4 && set "PATH=G:\VSCWorkspace\multi\luajit\bin;%PATH%" && lua -v && luarocks install multi -]] -function string.split (inputstr, sep) - local sep = sep or "\n" - local t={} - for str in string.gmatch(inputstr, "([^"..sep.."]+)") do - table.insert(t, str) - end - return t -end -local run = commands:split() -for i=1,#run do - os.execute(run[i]) -end \ No newline at end of file diff --git a/makeENV.sh b/makeENV.sh deleted file mode 100755 index 2840d20..0000000 --- a/makeENV.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -mkdir luajit -hererocks -j 2.1.0-beta3 -r latest --compat all ./luajit -. luajit/bin/activate -echo | lua -v -luarocks install multi -deactivate-lua -mkdir lua5.1 -hererocks -l 5.1 -r latest --patch --compat all ./lua5.1 -. lua5.1/bin/activate -echo | lua -v -luarocks install multi -deactivate-lua -mkdir lua5.2 -hererocks -l 5.2 -r latest --patch --compat all ./lua5.2 -. lua5.2/bin/activate -echo | lua -v -luarocks install multi -deactivate-lua -mkdir lua5.3 -hererocks -l 5.3 -r latest --patch --compat all ./lua5.3 -. lua5.3/bin/activate -echo | lua -v -luarocks install multi -deactivate-lua -mkdir lua5.4 -hererocks -l 5.4 -r latest --patch --compat all ./lua5.4 -. lua5.4/bin/activate -echo | lua -v -luarocks install multi -deactivate-lua \ No newline at end of file diff --git a/rockspecs/multi-15.3-0.rockspec b/rockspecs/multi-15.3-0.rockspec new file mode 100644 index 0000000..2cc71ba --- /dev/null +++ b/rockspecs/multi-15.3-0.rockspec @@ -0,0 +1,39 @@ +package = "multi" +version = "15.3-0" +source = { + url = "git://github.com/rayaman/multi.git", + tag = "v15.3.0", +} +description = { + summary = "Lua Multi tasking library", + detailed = [[ + This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration). + Check github for documentation. + ]], + homepage = "https://github.com/rayaman/multi", + license = "MIT" +} +dependencies = { + "lua >= 5.1" +} +build = { + type = "builtin", + modules = { + ["multi"] = "init.lua", + ["multi.integration.lanesManager"] = "integration/lanesManager/init.lua", + ["multi.integration.lanesManager.extensions"] = "integration/lanesManager/extensions.lua", + ["multi.integration.lanesManager.threads"] = "integration/lanesManager/threads.lua", + ["multi.integration.loveManager"] = "integration/loveManager/init.lua", + ["multi.integration.loveManager.extensions"] = "integration/loveManager/extensions.lua", + ["multi.integration.loveManager.threads"] = "integration/loveManager/threads.lua", + --["multi.integration.lovrManager"] = "integration/lovrManager/init.lua", + --["multi.integration.lovrManager.extensions"] = "integration/lovrManager/extensions.lua", + --["multi.integration.lovrManager.threads"] = "integration/lovrManager/threads.lua", + ["multi.integration.pesudoManager"] = "integration/pesudoManager/init.lua", + ["multi.integration.pesudoManager.extensions"] = "integration/pesudoManager/extensions.lua", + ["multi.integration.pesudoManager.threads"] = "integration/pesudoManager/threads.lua", + ["multi.integration.luvitManager"] = "integration/luvitManager.lua", + ["multi.integration.threading"] = "integration/threading.lua", + --["multi.integration.networkManager"] = "integration/networkManager.lua", + } +} \ No newline at end of file diff --git a/test.lua b/test.lua deleted file mode 100644 index fc484f9..0000000 --- a/test.lua +++ /dev/null @@ -1,20 +0,0 @@ -package.path = "./?/init.lua;?.lua;lua5.4/share/lua/?/init.lua;lua5.4/share/lua/?.lua;"..package.path -package.cpath = "lua5.4/lib/lua/?/core.dll;"..package.cpath -multi, thread = require("multi"):init{print=true} -GLOBAL, THREAD = require("multi.integration.lanesManager"):init() - -test = THREAD:newFunction(function() - PNT() - return 1,2 -end,true) -multi:newThread(function() - while true do - print("...") - thread.sleep(1) - end -end) -multi:newAlarm(.1):OnRing(function() os.exit() end) -print(test()) -print("Hi!") - -multi:mainloop() \ No newline at end of file diff --git a/tests/runtests.lua b/tests/runtests.lua index 07d6e85..ae8b098 100644 --- a/tests/runtests.lua +++ b/tests/runtests.lua @@ -2,7 +2,7 @@ if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then package.path="multi/?.lua;multi/?/init.lua;multi/?.lua;multi/?/?/init.lua;"..package.path require("lldebugger").start() else - package.path="./?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path + package.path = "../?/init.lua;../?.lua;"..package.path end --[[ This file runs all tests. @@ -36,12 +36,12 @@ runTest = thread:newFunction(function() end) proc:newTStep(1,10,1,.1):OnStep(function(t) tsteps = tsteps + 1 - end).OnEnd(function(step) + end):OnEnd(function(step) step:Destroy() end) proc:newStep(1,10):OnStep(function(s) steps = steps + 1 - end).OnEnd(function(step) + end):OnEnd(function(step) step:Destroy() end) local loop = proc:newLoop(function(l) @@ -78,7 +78,7 @@ runTest = thread:newFunction(function() thread.pushStatus(a,count) if a == count then break end end - return "Done" + return "Done", true, math.random(1,10000) end) local ret = func(10) local ret2 = func(15) @@ -102,26 +102,35 @@ runTest = thread:newFunction(function() ret3.OnStatus(function(part,whole) s3 = math.ceil((part/whole)*1000)/10 end) - ret.OnReturn(function() - print("Done 1") + + ret.OnReturn(function(...) + print("Done 1",...) end) - ret2.OnReturn(function() - print("Done 2") + ret2.OnReturn(function(...) + print("Done 2",...) end) - ret3.OnReturn(function() - print("Done 3") + ret3.OnReturn(function(...) + print("Done 3",...) end) - local err, timeout = thread.hold(ret.OnReturn + ret2.OnReturn + ret3.OnReturn) + + local err, timeout = thread.hold(ret.OnReturn * ret2.OnReturn * ret3.OnReturn) + if s1 == 100 and s2 == 100 and s3 == 100 then - print("Threads: Ok") + print("Threads: All tests Ok") else - print("Threads OnStatus or thread.hold(conn) Error!") - end - if timeout then - print("Threads or Connection Error!") - else - print("Connection Test 1: Ok") + if s1>0 and s2>0 and s3 > 0 then + print("Thread OnStatus: Ok") + else + print("Threads OnStatus or thread.hold(conn) Error!") + end + if timeout then + print("Connection Error!") + else + print("Connection Test 1: Ok") + end + print("Connection holding Error!") end + conn1 = proc:newConnection() conn2 = proc:newConnection() conn3 = proc:newConnection() @@ -164,11 +173,11 @@ runTest = thread:newFunction(function() os.exit() -- End of tests end) -runTest().OnError(function(...) +print(runTest().OnError(function(...) print("Error: Something went wrong with the test!") print(...) os.exit(1) -end) +end)) print("Pumping proc") while true do diff --git a/tests/test.lua b/tests/test.lua new file mode 100644 index 0000000..4387b11 --- /dev/null +++ b/tests/test.lua @@ -0,0 +1,133 @@ +package.path = "../?/init.lua;../?.lua;"..package.path +multi, thread = require("multi"):init{print=true,findopt=true} +GLOBAL, THREAD = require("multi.integration.lanesManager"):init() +multi:getOptimizationConnection()(function(msg) + print(msg) +end) + +-- local conn1, conn2, conn3 = multi:newConnection(), multi:newConnection():fastMode(), multi:newConnection() + +-- local link = conn1(function() +-- print("Conn1, first") +-- end) + +-- local link2 = conn1(function() +-- print("Conn1, second") +-- end) + +-- local link3 = conn1(function() +-- print("Conn1, third") +-- end) + +-- local link4 = conn2(function() +-- print("Conn2, first") +-- end) + +-- local link5 = conn2(function() +-- print("Conn2, second") +-- end) + +-- local link6 = conn2(function() +-- print("Conn2, third") +-- end) + +-- print("All conns\n-------------") +-- conn1:Fire() +-- conn2:Fire() + +-- conn1:Unconnect(link3) +-- conn2:Unconnect(link6) +-- print("All conns Edit\n---------------------") +-- conn1:Fire() +-- conn2:Fire() + +-- thread:newThread(function() +-- print("Awaiting status") +-- thread.hold(conn1 + (conn2 * conn3)) +-- print("Conn or Conn2 and Conn3") +-- end) + +-- multi:newAlarm(1):OnRing(function() +-- print("Conn") +-- conn1:Fire() +-- end) +-- multi:newAlarm(2):OnRing(function() +-- print("Conn2") +-- conn2:Fire() +-- end) +-- multi:newAlarm(3):OnRing(function() +-- print("Conn3") +-- conn3:Fire() +-- end) + +local conn = multi:newSystemThreadedConnection("conn"):init() + +multi:newSystemThread("Thread_Test_1", function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + local console = THREAD.getConsole() + conn(function(a,b,c) + console.print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:mainloop() +end) + +multi:newSystemThread("Thread_Test_2", function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + local console = THREAD.getConsole() + conn(function(a,b,c) + console.print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(2):OnRing(function() + console.print("Fire 2!!!") + conn:Fire(4,5,6) + THREAD.kill() + end) + + multi:mainloop() +end) +local console = THREAD.getConsole() +conn(function(a,b,c) + console.print("Mainloop conn got triggered!",a,b,c) +end) + +alarm = multi:newAlarm(1) +alarm:OnRing(function() + console.print("Fire 1!!!") + conn:Fire(1,2,3) +end) + +alarm = multi:newAlarm(3):OnRing(function() + multi:newSystemThread("Thread_Test_3",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + local console = THREAD.getConsole() + conn(function(a,b,c) + console.print(THREAD:getName().." was triggered!",a,b,c) + end) + multi:newAlarm(4):OnRing(function() + console.print("Fire 3!!!") + conn:Fire(7,8,9) + end) + multi:mainloop() + end) +end) + +multi:newSystemThread("Thread_Test_4",function() + local multi, thread = require("multi"):init() + local conn = GLOBAL["conn"]:init() + local conn2 = multi:newConnection() + local console = THREAD.getConsole() + multi:newAlarm(2):OnRing(function() + conn2:Fire() + end) + multi:newThread(function() + console.print("Conn Test!") + thread.hold(conn + conn2) + console.print("It held!") + end) + multi:mainloop() +end) + +multi:mainloop() \ No newline at end of file