V15.3.0 #51

Merged
rayaman merged 52 commits from v15.3.0 into master 2022-12-31 02:22:23 -05:00
37 changed files with 1007 additions and 246 deletions

7
.gitignore vendored
View File

@ -1,7 +1,2 @@
*lua5.1
*lua5.2
*lua5.3
*lua5.4
*luajit
*.code-workspace *.code-workspace
*.dat lua5.4/*

View File

@ -1,6 +1,8 @@
# Multi Version: 15.2.1 # Multi Version: 15.3.0 A world of Connections
**Key Changes** **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! 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
</br> </br>
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)
--- ---
</br> </br>
@ -16,12 +18,16 @@ Progress is being made in [v15.3.0](https://github.com/rayaman/multi/tree/v15.3.
INSTALLING INSTALLING
---------- ----------
Link to optional dependencies: Link to optional dependencies:
[lanes](https://github.com/LuaLanes/lanes) - [lanes](https://github.com/LuaLanes/lanes)
[love2d](https://love2d.org/)
- [love2d](https://love2d.org/)
To install copy the multi folder into your environment and you are good to go</br> To install copy the multi folder into your environment and you are good to go</br>
If you want to use the system threads, then you'll need to install lanes or love2d game engine! 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 Discord
------- -------

View File

@ -1,11 +1,173 @@
# Changelog # Changelog
Table of contents Table of contents
--- ---
[Update 15.3.0 - A world of connections](#update-1530---a-world-of-connections)</br>
[Update 15.2.1 - Bug fix](#update-1521---bug-fix)</br> [Update 15.2.1 - Bug fix](#update-1521---bug-fix)</br>
[Update 15.2.0 - Upgrade Complete](#update-1520---upgrade-complete)</br>[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)</br>[Update 15.0.0 - The art of faking it](#update-1500---the-art-of-faking-it)</br>[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)</br>[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)</br>[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)</br>[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)</br>[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)</br>[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)</br>[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)</br>[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)</br>[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)</br>[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)</br>[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)</br>[Update: 1.11.0](#update-1110)</br>[Update: 1.10.0](#update-1100)</br>[Update: 1.9.2](#update-192)</br>[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)</br>[Update: 1.9.0](#update-190)</br>[Update: 1.8.7](#update-187)</br>[Update: 1.8.6](#update-186)</br>[Update: 1.8.5](#update-185)</br>[Update: 1.8.4](#update-184)</br>[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)</br>[Update: 1.8.2](#update-182)</br>[Update: 1.8.1](#update-181)</br>[Update: 1.7.6](#update-176)</br>[Update: 1.7.5](#update-175)</br>[Update: 1.7.4](#update-174)</br>[Update: 1.7.3](#update-173)</br>[Update: 1.7.2](#update-172)</br>[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)</br>[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)</br>[Update: 1.6.0](#update-160)</br>[Update: 1.5.0](#update-150)</br>[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)</br>[Update: 1.4.0 (3/20/2017)](#update-140-3202017)</br>[Update: 1.3.0 (1/29/2017)](#update-130-1292017)</br>[Update: 1.2.0 (12.31.2016)](#update-120-12312016)</br>[Update: 1.1.0](#update-110)</br>[Update: 1.0.0](#update-100)</br>[Update: 0.6.3](#update-063)</br>[Update: 0.6.2](#update-062)</br>[Update: 0.6.1-6](#update-061-6)</br>[Update: 0.5.1-6](#update-051-6)</br>[Update: 0.4.1](#update-041)</br>[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)</br>[Update: EventManager 2.0.0](#update-eventmanager-200)</br>[Update: EventManager 1.2.0](#update-eventmanager-120)</br>[Update: EventManager 1.1.0](#update-eventmanager-110)</br>[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</br>[Version: EventManager 0.0.1 - In The Beginning things were very different](#version-eventmanager-001---in-the-beginning-things-were-very-different) [Update 15.2.0 - Upgrade Complete](#update-1520---upgrade-complete)</br>[Update 15.1.0 - Hold the thread!](#update-1510---hold-the-thread)</br>[Update 15.0.0 - The art of faking it](#update-1500---the-art-of-faking-it)</br>[Update 14.2.0 - Bloatware Removed](#update-1420---bloatware-removed)</br>[Update 14.1.0 - A whole new world of possibilities](#update-1410---a-whole-new-world-of-possibilities)</br>[Update 14.0.0 - Consistency, Additions and Stability](#update-1400---consistency-additions-and-stability)</br>[Update 13.1.0 - Bug fixes and features added](#update-1310---bug-fixes-and-features-added)</br>[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)</br>[Update 12.2.2 - Time for some more bug fixes!](#update-1222---time-for-some-more-bug-fixes)</br>[Update 12.2.1 - Time for some bug fixes!](#update-1221---time-for-some-bug-fixes)</br>[Update 12.2.0 - The chains of binding](#update-1220---the-chains-of-binding)</br>[Update 12.1.0 - Threads just can't hold on anymore](#update-1210---threads-just-cant-hold-on-anymore)</br>[Update: 12.0.0 - Big update (Lots of additions some changes)](#update-1200---big-update-lots-of-additions-some-changes)</br>[Update: 1.11.1 - Small Clarification on Love](#update-1111---small-clarification-on-love)</br>[Update: 1.11.0](#update-1110)</br>[Update: 1.10.0](#update-1100)</br>[Update: 1.9.2](#update-192)</br>[Update: 1.9.1 - Threads can now argue](#update-191---threads-can-now-argue)</br>[Update: 1.9.0](#update-190)</br>[Update: 1.8.7](#update-187)</br>[Update: 1.8.6](#update-186)</br>[Update: 1.8.5](#update-185)</br>[Update: 1.8.4](#update-184)</br>[Update: 1.8.3 - Mainloop recieves some needed overhauling](#update-183---mainloop-recieves-some-needed-overhauling)</br>[Update: 1.8.2](#update-182)</br>[Update: 1.8.1](#update-181)</br>[Update: 1.7.6](#update-176)</br>[Update: 1.7.5](#update-175)</br>[Update: 1.7.4](#update-174)</br>[Update: 1.7.3](#update-173)</br>[Update: 1.7.2](#update-172)</br>[Update: 1.7.1 - Bug Fixes Only](#update-171---bug-fixes-only)</br>[Update: 1.7.0 - Threading the systems](#update-170---threading-the-systems)</br>[Update: 1.6.0](#update-160)</br>[Update: 1.5.0](#update-150)</br>[Update: 1.4.1 (4/10/2017) - First Public release of the library](#update-141-4102017---first-public-release-of-the-library)</br>[Update: 1.4.0 (3/20/2017)](#update-140-3202017)</br>[Update: 1.3.0 (1/29/2017)](#update-130-1292017)</br>[Update: 1.2.0 (12.31.2016)](#update-120-12312016)</br>[Update: 1.1.0](#update-110)</br>[Update: 1.0.0](#update-100)</br>[Update: 0.6.3](#update-063)</br>[Update: 0.6.2](#update-062)</br>[Update: 0.6.1-6](#update-061-6)</br>[Update: 0.5.1-6](#update-051-6)</br>[Update: 0.4.1](#update-041)</br>[Update: 0.3.0 - The update that started it all](#update-030---the-update-that-started-it-all)</br>[Update: EventManager 2.0.0](#update-eventmanager-200)</br>[Update: EventManager 1.2.0](#update-eventmanager-120)</br>[Update: EventManager 1.1.0](#update-eventmanager-110)</br>[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</br>[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 # Update 15.2.1 - Bug fix
Fixed issue #41 Fixed issue #41
---
# Update 15.2.0 - Upgrade Complete # Update 15.2.0 - Upgrade Complete

View File

@ -29,12 +29,13 @@ local clock = os.clock
local thread = {} local thread = {}
local in_proc = false local in_proc = false
local processes = {} local processes = {}
local find_optimization = false
if not _G["$multi"] then if not _G["$multi"] then
_G["$multi"] = {multi=multi,thread=thread} _G["$multi"] = {multi=multi,thread=thread}
end end
multi.Version = "15.2.1" multi.Version = "15.3.0"
multi.Name = "root" multi.Name = "root"
multi.NIL = {Type="NIL"} multi.NIL = {Type="NIL"}
local NIL = multi.NIL local NIL = multi.NIL
@ -109,6 +110,15 @@ function multi:getStats()
end end
--Helpers --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 local ignoreconn = true
function multi:newConnection(protect,func,kill) function multi:newConnection(protect,func,kill)
local c={} local c={}
@ -116,70 +126,75 @@ function multi:newConnection(protect,func,kill)
local lock = false local lock = false
c.callback = func c.callback = func
c.Parent=self c.Parent=self
setmetatable(c,{__call=function(self,...) setmetatable(c,{__call=function(self,...)
local t = ... local t = ...
if type(t)=="table" then if type(t)=="table" then
for i,v in pairs(t) do for i,v in pairs(t) do
if v==self then if v==self then
local ref = self:connect(select(2,...)) local ref = self:Connect(select(2,...))
if ref then
ref.root_link = select(1,...) ref.root_link = select(1,...)
return ref return ref
end end
return self
end end
return self:connect(...) end
return self:Connect(...)
else else
return self:connect(...) return self:Connect(...)
end end
end, end,
__add = function(c1,c2) __add = function(c1,c2) -- Or
cn = multi:newConnection() local 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
c1(function(...) c1(function(...)
cn.__count = cn.__count + 1
if cn.__count == cn.__hasInstances then
cn:Fire(...) cn:Fire(...)
end
end) end)
c2(function(...) c2(function(...)
cn.__count = cn.__count + 1
if cn.__count == cn.__hasInstances then
cn:Fire(...) 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
end) end)
return cn return cn
end}) end})
c.Type='connector' c.Type='connector'
c.func={} c.func={}
c.ID=0 c.ID=0
local protect=protect or false local protect=protect or false
local connections={} local connections={}
c.FC=0 c.FC=0
function c:hasConnections() function c:hasConnections()
return #call_funcs~=0 return #call_funcs~=0
end 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) function c:getConnection(name,ignore)
if ignore then if ignore then
return connections[name] or CRef return connections[name] or CRef
@ -187,20 +202,26 @@ function multi:newConnection(protect,func,kill)
return connections[name] or self return connections[name] or self
end end
end end
function c:Lock() function c:Lock()
lock = true lock = true
return self return self
end end
function c:Unlock() function c:Unlock()
lock = false lock = false
return self return self
end end
if protect then if protect then
function c:Fire(...) function c:Fire(...)
if lock then return end if lock then return end
for i=#call_funcs,1,-1 do for i=#call_funcs,1,-1 do
if not call_funcs[i] then return end 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 if kill then
table.remove(call_funcs,i) table.remove(call_funcs,i)
end end
@ -208,6 +229,7 @@ function multi:newConnection(protect,func,kill)
end end
else else
function c:Fire(...) function c:Fire(...)
if lock then return end
for i=#call_funcs,1,-1 do for i=#call_funcs,1,-1 do
call_funcs[i](...) call_funcs[i](...)
if kill then if kill then
@ -216,48 +238,37 @@ function multi:newConnection(protect,func,kill)
end end
end end
end end
local fast = {} local fast = {}
function c:getConnections() function c:getConnections()
return call_funcs return call_funcs
end 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() function c:fastMode()
if find_optimization then return self end
function self:Fire(...) function self:Fire(...)
for i=1,#fast do for i=1,#fast do
fast[i](...) fast[i](...)
end end
end end
function self:connect(func) function self:Connect(func)
table.insert(fast, func) table.insert(fast, func)
end local temp = {fast = true}
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(...)
end
}
setmetatable(temp,{ setmetatable(temp,{
__call=function(s,...) __call=function(s,...)
return self:connect(...) return self:Connect(...)
end, end,
__index = function(t,k) __index = function(t,k)
if rawget(t,"root_link") then if rawget(t,"root_link") then
@ -272,18 +283,66 @@ function multi:newConnection(protect,func,kill)
rawset(t,k,v) rawset(t,k,v)
end, end,
}) })
function temp:Fire(...) temp.ref = func
if lock then return end return temp
if protect then
local t=pcall(call_funcs,...)
if t then
return t
end 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 else
table.insert(call_funcs,1,func)
end
local temp = {
func=func,
Type="connector_link",
Parent=self,
connect = function(s,...)
return self:Connect(...)
end
}
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,
})
function temp:Fire(...)
return call_funcs(...) return call_funcs(...)
end end
end
function temp:Destroy() 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 for i=#call_funcs,1,-1 do
if call_funcs[i]~=nil then if call_funcs[i]~=nil then
if call_funcs[i]==self.func then if call_funcs[i]==self.func then
@ -294,15 +353,19 @@ function multi:newConnection(protect,func,kill)
end end
end end
end end
if name then if name then
connections[name]=temp connections[name]=temp
end end
if self.callback then if self.callback then
self.callback(temp) self.callback(temp)
end end
return temp return temp
end end
function c:connect(...)--func,name,num
function c:Connect(...)--func,name,num
local tab = {...} local tab = {...}
local funcs={} local funcs={}
for i=1,#tab do 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]) return conn_helper(self,tab[1],tab[2],tab[3])
end end
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.GetConnection=c.getConnection
c.HasConnections = c.hasConnections
c.GetConnection = c.getConnection
if not(ignoreconn) then if not(ignoreconn) then
multi:create(c) multi:create(c)
end end
return c return c
end 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 -- Used with ISO Threads
local function isolateFunction(func,env) local function isolateFunction(func,env)
local dmp = string.dump(func) local dmp = string.dump(func)
@ -508,10 +602,6 @@ function multi:newConnector()
return c return c
end end
local CRef = {
Fire = function() end
}
multi.OnObjectCreated=multi:newConnection() multi.OnObjectCreated=multi:newConnection()
multi.OnObjectDestroyed=multi:newConnection() multi.OnObjectDestroyed=multi:newConnection()
multi.OnLoad = multi:newConnection(nil,nil,true) multi.OnLoad = multi:newConnection(nil,nil,true)
@ -565,7 +655,7 @@ function multi:newEvent(task)
task=func task=func
return self return self
end end
c.OnEvent = self:newConnection() c.OnEvent = self:newConnection():fastMode()
self:setPriority("core") self:setPriority("core")
c:SetName(c.Type) c:SetName(c.Type)
multi:create(c) multi:create(c)
@ -588,7 +678,7 @@ function multi:newUpdater(skip)
skip=n skip=n
return self return self
end end
c.OnUpdate = self:newConnection() c.OnUpdate = self:newConnection():fastMode()
c:SetName(c.Type) c:SetName(c.Type)
multi:create(c) multi:create(c)
return c return c
@ -620,7 +710,7 @@ function multi:newAlarm(set)
t = clock() t = clock()
return self return self
end end
c.OnRing = self:newConnection() c.OnRing = self:newConnection():fastMode()
function c:Pause() function c:Pause()
count = clock() count = clock()
self.Parent.Pause(self) self.Parent.Pause(self)
@ -644,10 +734,7 @@ function multi:newLoop(func,notime)
self.OnLoop:Fire(self,clock()-start) self.OnLoop:Fire(self,clock()-start)
end end
end end
c.OnLoop = self:newConnection() c.OnLoop = self:newConnection():fastMode()
function c:fastMode()
self.OnLoop:fastMode()
end
if func then if func then
c.OnLoop(func) c.OnLoop(func)
@ -694,9 +781,9 @@ function multi:newStep(start,reset,count,skip)
end end
end end
c.Reset=c.Resume c.Reset=c.Resume
c.OnStart = self:newConnection() c.OnStart = self:newConnection():fastMode()
c.OnStep = self:newConnection() c.OnStep = self:newConnection():fastMode()
c.OnEnd = self:newConnection() c.OnEnd = self:newConnection():fastMode()
function c:Break() function c:Break()
self.Active=nil self.Active=nil
return self return self
@ -744,7 +831,7 @@ function multi:newTLoop(func,set)
self.Parent.Pause(self) self.Parent.Pause(self)
return self return self
end end
c.OnLoop = self:newConnection() c.OnLoop = self:newConnection():fastMode()
if func then if func then
c.OnLoop(func) c.OnLoop(func)
end end
@ -803,6 +890,32 @@ function multi:newTStep(start,reset,count,set)
return c return c
end 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 scheduledjobs = {}
local sthread local sthread
@ -864,25 +977,28 @@ function multi:newProcessor(name,nothread)
c.Type = "process" c.Type = "process"
local Active = nothread or false local Active = nothread or false
c.Name = name or "" c.Name = name or ""
c.pump = false
c.threads = {} c.threads = {}
c.startme = {} c.startme = {}
c.parent = self c.parent = self
local handler = c:createHandler(c.threads,c.startme) local handler = c:createHandler(c.threads,c.startme)
if not nothread then -- Don't create a loop if we are triggering this manually
c.process = self:newLoop(function() c.process = self:newLoop(function()
if Active then if Active then
c:uManager() c:uManager()
handler() handler()
end end
end) end)
c.process.__ignore = true c.process.__ignore = true
c.process.isProcessThread = true c.process.isProcessThread = true
c.process.PID = sandcount c.process.PID = sandcount
c.OnError = c.process.OnError c.OnError = c.process.OnError
else
c.OnError = multi:newConnection()
end
c.OnError(multi.print)
function c:getThreads() function c:getThreads()
return c.threads return c.threads
@ -911,10 +1027,8 @@ function multi:newProcessor(name,nothread)
function c.run() function c.run()
if not Active then return end if not Active then return end
c.pump = true
c:uManager() c:uManager()
handler() handler()
c.pump = false
return c return c
end end
@ -1033,6 +1147,28 @@ function thread.sleep(n)
return yield(CMD, t_sleep, n or 1) return yield(CMD, t_sleep, n or 1)
end 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) function thread.hold(n,opt)
thread._Requests() thread._Requests()
local opt = opt or {} local opt = opt or {}
@ -1051,20 +1187,7 @@ function thread.hold(n,opt)
thread.getRunningThread().lastSleep = clock() thread.getRunningThread().lastSleep = clock()
return yield(CMD, t_sleep, n or 0, nil, interval) return yield(CMD, t_sleep, n or 0, nil, interval)
elseif type(n) == "table" and n.Type == "connector" then elseif type(n) == "table" and n.Type == "connector" then
local rdy = function() return yield(CMD, t_hold, conn_test(n), nil, interval)
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 elseif type(n) == "function" then
return yield(CMD, t_hold, n or dFunc, nil, interval) return yield(CMD, t_hold, n or dFunc, nil, interval)
else else
@ -1180,7 +1303,7 @@ function thread:newFunctionBase(generator,holdme)
end end
end end
tfunc.__call = function(t,...) tfunc.__call = function(t,...)
if not t.Active then if t.Active == false then
if holdme then if holdme then
return nil, "Function is paused" return nil, "Function is paused"
end end
@ -1244,9 +1367,8 @@ local startme_len = 0
function thread:newThread(name,func,...) function thread:newThread(name,func,...)
multi.OnLoad:Fire() -- This was done incase a threaded function was called before mainloop/uManager was called multi.OnLoad:Fire() -- This was done incase a threaded function was called before mainloop/uManager was called
local func = func or name local func = func or name
if func == name then
if type(name) == "function" then name = name or multi.randomString(16)
name = "Thread#"..threadCount
end end
local c={nil,nil,nil,nil,nil,nil,nil} local c={nil,nil,nil,nil,nil,nil,nil}
local env = {self=c} local env = {self=c}
@ -1264,6 +1386,7 @@ function thread:newThread(name,func,...)
c.isError = false c.isError = false
c.OnError = multi:newConnection(true,nil,true) c.OnError = multi:newConnection(true,nil,true)
c.OnDeath = multi:newConnection(true,nil,true) c.OnDeath = multi:newConnection(true,nil,true)
c.OnError(multi.print)
function c:getName() function c:getName()
return c.Name return c.Name
@ -1471,7 +1594,6 @@ co_status = {
switch[task](ref,thd) switch[task](ref,thd)
cmds[r1](ref,r2,r3,r4,r5) cmds[r1](ref,r2,r3,r4,r5)
if ret ~= CMD and _ ~= nil then -- The rework makes this necessary if ret ~= CMD and _ ~= nil then -- The rework makes this necessary
print("Hello")
co_status["dead"](thd,ref,task,i,th) co_status["dead"](thd,ref,task,i,th)
end end
r1=nil r2=nil r3=nil r4=nil r5=nil r1=nil r2=nil r3=nil r4=nil r5=nil
@ -1725,6 +1847,10 @@ function multi.init(settings, realsettings)
else else
multi.mainloop = mainloop multi.mainloop = mainloop
end end
if settings.findopt then
find_optimization = true
multi.enableOptimization:Fire(multi, thread)
end
end end
return _G["$multi"].multi,_G["$multi"].thread return _G["$multi"].multi,_G["$multi"].thread
end end
@ -2074,5 +2200,64 @@ else
multi.m.sentinel = newproxy(true) multi.m.sentinel = newproxy(true)
getmetatable(multi.m.sentinel).__gc = multi.m.onexit getmetatable(multi.m.sentinel).__gc = multi.m.onexit
end 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 return multi

View File

@ -22,9 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]] ]]
local multi, thread = require("multi"):init() local multi, thread = require("multi"):init()
if not (GLOBAL and THREAD) then
local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD local GLOBAL, THREAD = multi.integration.GLOBAL,multi.integration.THREAD
else
lanes = require("lanes")
end
function multi:newSystemThreadedQueue(name) function multi:newSystemThreadedQueue(name)
local name = name or multi.randomString(16)
local c = {} local c = {}
c.Name = name
c.linda = lanes.linda() c.linda = lanes.linda()
function c:push(v) function c:push(v)
self.linda:send("Q", v) self.linda:send("Q", v)
@ -41,9 +47,12 @@ function multi:newSystemThreadedQueue(name)
GLOBAL[name or "_"] = c GLOBAL[name or "_"] = c
return c return c
end end
function multi:newSystemThreadedTable(name) function multi:newSystemThreadedTable(name)
local name = name or multi.randomString(16)
local c = {} local c = {}
c.link = lanes.linda() c.link = lanes.linda()
c.Name = name
setmetatable(c,{ setmetatable(c,{
__index = function(t,k) __index = function(t,k)
return c.link:get(k) return c.link:get(k)
@ -58,14 +67,15 @@ function multi:newSystemThreadedTable(name)
GLOBAL[name or "_"] = c GLOBAL[name or "_"] = c
return c return c
end end
function multi:newSystemThreadedJobQueue(n) function multi:newSystemThreadedJobQueue(n)
local c = {} local c = {}
c.cores = n or THREAD.getCores()*2 c.cores = n or THREAD.getCores()*2
c.OnJobCompleted = multi:newConnection() c.OnJobCompleted = multi:newConnection()
local funcs = multi:newSystemThreadedTable() local funcs = multi:newSystemThreadedTable():init()
local queueJob = multi:newSystemThreadedQueue() local queueJob = multi:newSystemThreadedQueue():init()
local queueReturn = multi:newSystemThreadedQueue() local queueReturn = multi:newSystemThreadedQueue():init()
local doAll = multi:newSystemThreadedQueue() local doAll = multi:newSystemThreadedQueue():init()
local ID=1 local ID=1
local jid = 1 local jid = 1
function c:isEmpty() function c:isEmpty()
@ -169,3 +179,134 @@ function multi:newSystemThreadedJobQueue(n)
end end
return c return c
end 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

View File

@ -50,7 +50,7 @@ local __SleepingLinda = lanes.linda() -- handles sleeping stuff
local __ConsoleLinda = lanes.linda() -- handles console stuff local __ConsoleLinda = lanes.linda() -- handles console stuff
local __StatusLinda = lanes.linda() -- handles pushstatus for stfunctions 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 count = 1
local started = false local started = false
local livingThreads = {} local livingThreads = {}
@ -62,6 +62,7 @@ function THREAD:newFunction(func,holdme)
end end
function multi:newSystemThread(name, func, ...) function multi:newSystemThread(name, func, ...)
local name = name or multi.randomString(16)
multi.InitSystemThreadErrorHandler() multi.InitSystemThreadErrorHandler()
local rand = math.random(1, 10000000) local rand = math.random(1, 10000000)
local return_linda = lanes.linda() local return_linda = lanes.linda()
@ -76,6 +77,10 @@ function multi:newSystemThread(name, func, ...)
c.creationTime = os.clock() c.creationTime = os.clock()
c.alive = true c.alive = true
c.priority = THREAD.Priority_Normal 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("*", c.thread = lanes.gen("*",
{ {
globals={ -- Set up some globals globals={ -- Set up some globals
@ -87,6 +92,8 @@ function multi:newSystemThread(name, func, ...)
}, },
priority=c.priority priority=c.priority
},function(...) },function(...)
require("multi"):init(multi_settings)
require("multi.integration.lanesManager.extensions")
local has_error = true local has_error = true
return_linda:set("returns",{func(...)}) return_linda:set("returns",{func(...)})
has_error = false has_error = false
@ -119,6 +126,7 @@ function multi.InitSystemThreadErrorHandler()
while true do while true do
thread.yield() thread.yield()
_,data = __ConsoleLinda:receive(0, "Q") _,data = __ConsoleLinda:receive(0, "Q")
if data then print(unpack(data)) end
for i = #threads, 1, -1 do for i = #threads, 1, -1 do
temp = threads[i] temp = threads[i]
status = temp.thread.status status = temp.thread.status

View File

@ -29,7 +29,7 @@ local function getOS()
end end
end end
local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda) local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
local THREAD = {} local THREAD = {}
THREAD.Priority_Core = 3 THREAD.Priority_Core = 3
THREAD.Priority_High = 2 THREAD.Priority_High = 2
@ -70,12 +70,12 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda)
function THREAD.getConsole() function THREAD.getConsole()
local c = {} local c = {}
c.queue = _Console c.queue = __Console
function c.print(...) function c.print(...)
c.queue:send("Q", {...}) c.queue:send("Q", {...})
end end
function c.error(err) function c.error(err)
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__} c.queue:push("Q",{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__})
error(err) error(err)
end end
return c return c
@ -137,6 +137,6 @@ local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda)
return GLOBAL, THREAD return GLOBAL, THREAD
end end
return {init = function(g,s,st) return {init = function(g,s,st,c)
return INIT(g,s,st) return INIT(g,s,st,c)
end} end}

View File

@ -22,11 +22,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]] ]]
-- TODO make compatible with lovr if not ISTHREAD then
local multi, thread = require("multi").init() multi, thread = require("multi").init()
GLOBAL = multi.integration.GLOBAL GLOBAL = multi.integration.GLOBAL
THREAD = multi.integration.THREAD THREAD = multi.integration.THREAD
else
GLOBAL = multi.integration.GLOBAL
THREAD = multi.integration.THREAD
end
function multi:newSystemThreadedQueue(name) function multi:newSystemThreadedQueue(name)
local name = name or multi.randomString(16)
local c = {} local c = {}
c.Name = name c.Name = name
local fRef = {"func",nil} local fRef = {"func",nil}
@ -64,10 +70,11 @@ function multi:newSystemThreadedQueue(name)
return c return c
end end
function multi:newSystemThreadedTable(name) function multi:newSystemThreadedTable(name)
local name = name or multi.randomString(16)
local c = {} local c = {}
c.name = name c.Name = name
function c:init() function c:init()
return THREAD.createTable(self.name) return THREAD.createTable(self.Name)
end end
THREAD.package(name,c) THREAD.package(name,c)
return c return c
@ -203,3 +210,142 @@ function multi:newSystemThreadedJobQueue(n)
jqc = jqc + 1 jqc = jqc + 1
return c return c
end 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

View File

@ -26,15 +26,23 @@ if ISTHREAD then
end end
local ThreadFileData = [[ local ThreadFileData = [[
ISTHREAD = true ISTHREAD = true
THREAD = require("multi.integration.loveManager.threads") -- order is important! THREAD = require("multi.integration.loveManager.threads")
sThread = THREAD sThread = THREAD
__IMPORTS = {...} __IMPORTS = {...}
__FUNC__=table.remove(__IMPORTS,1) __FUNC__=table.remove(__IMPORTS,1)
__THREADID__=table.remove(__IMPORTS,1) __THREADID__=table.remove(__IMPORTS,1)
__THREADNAME__=table.remove(__IMPORTS,1) __THREADNAME__=table.remove(__IMPORTS,1)
math.randomseed(__THREADID__)
math.random()
math.random()
math.random()
stab = THREAD.createStaticTable(__THREADNAME__) stab = THREAD.createStaticTable(__THREADNAME__)
GLOBAL = THREAD.getGlobal() GLOBAL = THREAD.getGlobal()
multi, thread = require("multi").init() 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))} stab["returns"] = {THREAD.loadDump(__FUNC__)(unpack(__IMPORTS))}
]] ]]
local multi, thread = require("multi"):init() local multi, thread = require("multi"):init()
@ -42,7 +50,6 @@ local THREAD = {}
__THREADID__ = 0 __THREADID__ = 0
__THREADNAME__ = "MainThread" __THREADNAME__ = "MainThread"
multi.integration={} multi.integration={}
multi.integration.love2d={}
local THREAD = require("multi.integration.loveManager.threads") local THREAD = require("multi.integration.loveManager.threads")
local GLOBAL = THREAD.getGlobal() local GLOBAL = THREAD.getGlobal()
local THREAD_ID = 1 local THREAD_ID = 1
@ -103,13 +110,13 @@ end
THREAD.newSystemThread = multi.newSystemThread THREAD.newSystemThread = multi.newSystemThread
function love.threaderror(thread, errorstr) function love.threaderror(thread, errorstr)
mulit.print("Thread error!\n"..errorstr) multi.print("Thread error!\n"..errorstr)
end end
multi.integration.GLOBAL = GLOBAL multi.integration.GLOBAL = GLOBAL
multi.integration.THREAD = THREAD multi.integration.THREAD = THREAD
require("multi.integration.loveManager.extensions") require("multi.integration.loveManager.extensions")
mulit.print("Integrated Love Threading!") multi.print("Integrated Love Threading!")
return {init=function() return {init=function()
return GLOBAL,THREAD return GLOBAL,THREAD
end} end}

View File

@ -106,7 +106,7 @@ function threads.kill()
error("Thread Killed!\1") error("Thread Killed!\1")
end end
function THREAD.pushStatus(...) function threads.pushStatus(...)
local status_channel = love.thread.getChannel("__"..__THREADID__.."__MULTI__STATUS_CHANNEL__") local status_channel = love.thread.getChannel("__"..__THREADID__.."__MULTI__STATUS_CHANNEL__")
local args = {...} local args = {...}
status_channel:push(__THREADID__, args) status_channel:push(__THREADID__, args)

View File

@ -23,7 +23,7 @@ SOFTWARE.
]] ]]
local multi, thread = require("multi"):init() local multi, thread = require("multi"):init()
local net = require("net") local net = require("net")
local bin = require("bin") --local bin = require("bin")
local char = string.char local char = string.char
local byte = string.byte local byte = string.byte
bin.setBitsInterface(infinabits) bin.setBitsInterface(infinabits)

View File

@ -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

View File

@ -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

View File

@ -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",
}
}

View File

@ -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()

View File

@ -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 package.path="multi/?.lua;multi/?/init.lua;multi/?.lua;multi/?/?/init.lua;"..package.path
require("lldebugger").start() require("lldebugger").start()
else else
package.path="./?.lua;../?/init.lua;../?.lua;../?/?/init.lua;"..package.path package.path = "../?/init.lua;../?.lua;"..package.path
end end
--[[ --[[
This file runs all tests. This file runs all tests.
@ -36,12 +36,12 @@ runTest = thread:newFunction(function()
end) end)
proc:newTStep(1,10,1,.1):OnStep(function(t) proc:newTStep(1,10,1,.1):OnStep(function(t)
tsteps = tsteps + 1 tsteps = tsteps + 1
end).OnEnd(function(step) end):OnEnd(function(step)
step:Destroy() step:Destroy()
end) end)
proc:newStep(1,10):OnStep(function(s) proc:newStep(1,10):OnStep(function(s)
steps = steps + 1 steps = steps + 1
end).OnEnd(function(step) end):OnEnd(function(step)
step:Destroy() step:Destroy()
end) end)
local loop = proc:newLoop(function(l) local loop = proc:newLoop(function(l)
@ -78,7 +78,7 @@ runTest = thread:newFunction(function()
thread.pushStatus(a,count) thread.pushStatus(a,count)
if a == count then break end if a == count then break end
end end
return "Done" return "Done", true, math.random(1,10000)
end) end)
local ret = func(10) local ret = func(10)
local ret2 = func(15) local ret2 = func(15)
@ -102,26 +102,35 @@ runTest = thread:newFunction(function()
ret3.OnStatus(function(part,whole) ret3.OnStatus(function(part,whole)
s3 = math.ceil((part/whole)*1000)/10 s3 = math.ceil((part/whole)*1000)/10
end) end)
ret.OnReturn(function()
print("Done 1") ret.OnReturn(function(...)
print("Done 1",...)
end) end)
ret2.OnReturn(function() ret2.OnReturn(function(...)
print("Done 2") print("Done 2",...)
end) end)
ret3.OnReturn(function() ret3.OnReturn(function(...)
print("Done 3") print("Done 3",...)
end) 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 if s1 == 100 and s2 == 100 and s3 == 100 then
print("Threads: Ok") print("Threads: All tests Ok")
else
if s1>0 and s2>0 and s3 > 0 then
print("Thread OnStatus: Ok")
else else
print("Threads OnStatus or thread.hold(conn) Error!") print("Threads OnStatus or thread.hold(conn) Error!")
end end
if timeout then if timeout then
print("Threads or Connection Error!") print("Connection Error!")
else else
print("Connection Test 1: Ok") print("Connection Test 1: Ok")
end end
print("Connection holding Error!")
end
conn1 = proc:newConnection() conn1 = proc:newConnection()
conn2 = proc:newConnection() conn2 = proc:newConnection()
conn3 = proc:newConnection() conn3 = proc:newConnection()
@ -164,11 +173,11 @@ runTest = thread:newFunction(function()
os.exit() -- End of tests os.exit() -- End of tests
end) end)
runTest().OnError(function(...) print(runTest().OnError(function(...)
print("Error: Something went wrong with the test!") print("Error: Something went wrong with the test!")
print(...) print(...)
os.exit(1) os.exit(1)
end) end))
print("Pumping proc") print("Pumping proc")
while true do while true do

133
tests/test.lua Normal file
View File

@ -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()