mirror of
https://github.com/rayaman/multi.git
synced 2026-09-05 07:27:35 -04:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b5d10cbb6 | ||
|
|
0796788fcb | ||
|
|
ee5e3bde7f | ||
|
|
c86413351c | ||
|
|
4a52cd5e14 | ||
|
|
14beeb686a | ||
|
|
60783fdb16 | ||
|
|
b1a7e61c03 | ||
|
|
9fa0e7c2cb | ||
|
|
ef243f87d4 | ||
|
|
178283e0b4 | ||
|
|
7f35265a8e | ||
|
|
0501f068e4 | ||
|
|
ffd7ac15f6 | ||
|
|
869368af56 | ||
|
|
eeb75490dc | ||
|
|
2fec5b39b8 | ||
|
|
0a92890290 | ||
|
|
40de9a8d22 | ||
|
|
f8265fbb3b | ||
|
|
b995b1c0a7 |
@@ -1,25 +0,0 @@
|
||||
name: Build & Run tests Love2d
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install love2d
|
||||
run: |
|
||||
sudo apt install fuse
|
||||
wget https://github.com/love2d/love/releases/download/11.4/love-11.4-x86_64.AppImage -O love.AppImage
|
||||
sudo chmod +x love.AppImage
|
||||
- name: Run Tests
|
||||
run: |
|
||||
./love.AppImage tests
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build & Run tests Ubuntu
|
||||
name: Lua Unit Tests (Matrix)
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -7,35 +7,26 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-type: [Release] # Debug
|
||||
lua: ["lua 5.1", "lua 5.2", "lua 5.3", "lua 5.4", "luajit 2.1.0-beta3"]
|
||||
os: ["ubuntu-latest"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
luaVersion: ["5.1", "5.2", "5.3", "5.4"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Lua
|
||||
uses: leafo/gh-actions-lua@v10
|
||||
with:
|
||||
submodules: recursive
|
||||
luaVersion: ${{ matrix.luaVersion }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Verify Lua version
|
||||
run: lua -v
|
||||
|
||||
- name: Setup env
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
pip install hererocks
|
||||
hererocks lua-pkg --${{ matrix.lua }} -rlatest
|
||||
|
||||
- name: Install lanes and multi
|
||||
run: |
|
||||
source ${{github.workspace}}/lua-pkg/bin/activate
|
||||
luarocks install lanes
|
||||
luarocks install rockspecs/multi-16.0-0.rockspec
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
source ${{github.workspace}}/lua-pkg/bin/activate
|
||||
lua tests/runtests.lua
|
||||
# Replace with your actual test file
|
||||
lua tests/multi_test.lua
|
||||
@@ -1,8 +1,8 @@
|
||||
# Multi Version: 16.1.0 - The Flow State
|
||||
# Multi Version: 16.3.0 -
|
||||
**Key Changes**
|
||||
- Updated Processors to have more controlled over scheduled processes
|
||||
- Forwarding connections
|
||||
- New Timeout handling
|
||||
- Fixed connection multiplying
|
||||
- Optimizing the library
|
||||
- Updated task management support
|
||||
|
||||
Refer to the [Change Log](https://github.com/rayaman/multi/blob/master/docs/changes.md) for more infromation
|
||||
|
||||
@@ -12,7 +12,7 @@ My multitasking library for lua. It is a pure lua binding, with exceptions of th
|
||||
|
||||
</br>
|
||||
|
||||
Progress is being made in [v16.2.0](https://github.com/rayaman/multi/tree/v16.2.0)
|
||||
Progress is being made in [v16.3.0](https://github.com/rayaman/multi/tree/v16.3.0)
|
||||
---
|
||||
|
||||
</br>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Changelog
|
||||
Table of contents
|
||||
---
|
||||
[Update 16.3.0 - Proper Priorities](#update-1620---proper-priorities)</br>
|
||||
[Update 16.2.0 - TimeStamps and UUIDs](#update-1620---timestamps-and-uuids)</br>
|
||||
[Update 16.1.0 - The Flow State](#update-1610---the-flow-state)</br>
|
||||
[Update 16.0.1 - Bug fix](#update-1601---bug-fix)</br>
|
||||
@@ -61,6 +62,30 @@ Table of contents
|
||||
[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 16.3.0 - Proper Priorities
|
||||
|
||||
Added
|
||||
---
|
||||
- New processor scoped to track connections that exist
|
||||
- Better Destroy logic for connections
|
||||
- Added UPTIME to all objects in the library via the create hook
|
||||
- added `proc:isPaused()` to processors
|
||||
- `multi.setClock(c)` allows you to set the internal clock used by the multi core. For example on linux os.clock doesn't give time elapsed since the program started like it does on windows. This method allows one to swap that out.
|
||||
|
||||
Changed
|
||||
---
|
||||
- `thread.kill(msg)` now accepts a message. If omitted will use default `thread killed!`
|
||||
- Modified multiple locations to not create a function during a thread.hold() and reuse. Only functions containing an upvalue currently have functions being created during hold. This will eventually be fixed
|
||||
- Updated `multi:getStats()` with more information
|
||||
- `multi.forwardConnection()` now returns the link to the source connection so you can do cleanup
|
||||
- When connecting to a connection you can unconnect using ref:Unconnect() it has an internal reference to the root connection
|
||||
- Unnamed threads have a shorter postfix of only 4 random characters
|
||||
|
||||
Fixed
|
||||
---
|
||||
- [Issue](https://github.com/rayaman/multi/issues/77) with concat of connections where conn .. func did not work properly
|
||||
- [Issue](https://github.com/rayaman/multi/issues/67) with connection multiplying
|
||||
|
||||
# Update 16.2.0 - TimeStamps and UUIDs and bugfixes
|
||||
Added
|
||||
---
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -92,7 +92,7 @@ function multi.getTypes()
|
||||
return types
|
||||
end
|
||||
|
||||
multi.Version = "16.2.0"
|
||||
multi.Version = "16.3.0-testing"
|
||||
multi.Name = "root"
|
||||
multi.NIL = {Type="NIL"}
|
||||
local NIL = multi.NIL
|
||||
@@ -154,11 +154,18 @@ function multi:isType(type)
|
||||
return self.Type == type
|
||||
end
|
||||
|
||||
multi.connection_count = 0
|
||||
multi.connection_subscriptions = 0
|
||||
|
||||
function multi:getStats()
|
||||
local stats = {
|
||||
[multi.Name] = {
|
||||
threads = multi:getThreads(),
|
||||
tasks = multi:getTasks()
|
||||
tasks = multi.Mainloop,
|
||||
name = "root",
|
||||
fullName = "root",
|
||||
connections = self.connection_count,
|
||||
subscriptions = self.connection_subscriptions
|
||||
}
|
||||
}
|
||||
local procs = multi:getProcessors()
|
||||
@@ -166,7 +173,11 @@ function multi:getStats()
|
||||
local proc = procs[i]
|
||||
stats[proc:getFullName()] = {
|
||||
threads = proc:getThreads(),
|
||||
tasks = proc:getTasks()
|
||||
tasks = proc.Mainloop,
|
||||
name = proc:getName(),
|
||||
fullName = proc:getFullName(),
|
||||
connections = proc.connection_count,
|
||||
subscriptions = proc.connection_subscriptions
|
||||
}
|
||||
end
|
||||
return stats
|
||||
@@ -202,7 +213,7 @@ end
|
||||
|
||||
function multi.forwardConnection(src, dest)
|
||||
if multi.isMulitObj(src) and multi.isMulitObj(dest) then
|
||||
src(function(...)
|
||||
return src(function(...)
|
||||
dest:Fire(...)
|
||||
end)
|
||||
else
|
||||
@@ -215,23 +226,32 @@ local ignoreconn = true
|
||||
local empty_func = function() end
|
||||
|
||||
function multi:newConnection(protect,func,kill)
|
||||
self.connection_count = self.connection_count + 1
|
||||
local processor = self
|
||||
local c={}
|
||||
local c = {}
|
||||
local lock = false
|
||||
local fast = {}
|
||||
c.__connectionAdded = function() end
|
||||
c.rawadd = false
|
||||
c.Parent = self
|
||||
c._child_conns = {} -- tracks connections spawned by operators
|
||||
c.destroyed = false
|
||||
|
||||
setmetatable(c,{
|
||||
__call=function(self,...)
|
||||
-- Helper: register a child connection for cleanup
|
||||
local function trackChild(cn)
|
||||
c._child_conns[#c._child_conns + 1] = cn
|
||||
return cn
|
||||
end
|
||||
|
||||
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,...))
|
||||
if type(t) == "table" then
|
||||
for i, v in pairs(t) do
|
||||
if v == self then
|
||||
local ref = self:Connect(select(2, ...))
|
||||
if ref then
|
||||
ref.root_link = select(1,...)
|
||||
ref.root_link = select(1, ...)
|
||||
return ref
|
||||
end
|
||||
return self
|
||||
@@ -242,7 +262,8 @@ function multi:newConnection(protect,func,kill)
|
||||
return self:Connect(...)
|
||||
end
|
||||
end,
|
||||
__unm = function(obj) -- -obj Reverses the order of connected events
|
||||
|
||||
__unm = function(obj)
|
||||
local conns = obj:Bind({})
|
||||
for i = #conns, 1, -1 do
|
||||
obj.rawadd = true
|
||||
@@ -251,15 +272,16 @@ function multi:newConnection(protect,func,kill)
|
||||
end
|
||||
return obj
|
||||
end,
|
||||
__mod = function(obj1, obj2) -- %
|
||||
local cn = self:newConnection()
|
||||
if type(obj1) == "function" and type(obj2) == "table" then
|
||||
|
||||
__mod = function(obj1, obj2)
|
||||
local cn = trackChild(self:newConnection())
|
||||
if (type(obj1) == "function" or type(obj1) == "table" and obj1.Type == multi.registerType("function", "functions")) and type(obj2) == "table" then
|
||||
obj2(function(...)
|
||||
cn:Fire(obj1(...))
|
||||
end)
|
||||
elseif type(obj1) == "table" and type(obj2) == "function" then
|
||||
elseif type(obj1) == "table" and (type(obj2) == "function" or type(obj2) == "table" and obj2.Type == multi.registerType("function", "functions")) then
|
||||
local conns = obj1:Bind({})
|
||||
for i = 1,#conns do
|
||||
for i = 1, #conns do
|
||||
obj1(function(...)
|
||||
conns[i](obj2(...))
|
||||
end)
|
||||
@@ -274,31 +296,33 @@ function multi:newConnection(protect,func,kill)
|
||||
end
|
||||
return obj1
|
||||
else
|
||||
error("Invalid mod!", type(obj1), type(obj2),"Expected function, connection(table)")
|
||||
multi.error("Invalid mod!", type(obj1), type(obj2), "Expected function, connection(table)")
|
||||
end
|
||||
return cn
|
||||
end,
|
||||
__div = function(obj1, obj2) -- /
|
||||
local cn = self:newConnection()
|
||||
local ref
|
||||
|
||||
__div = function(obj1, obj2)
|
||||
local cn = trackChild(self:newConnection())
|
||||
if type(obj1) == "function" and type(obj2) == "table" then
|
||||
obj2(function(...)
|
||||
local args = {obj1(...)}
|
||||
local args = { obj1(...) }
|
||||
if args[1] then
|
||||
table.remove(args, 1)
|
||||
cn:Fire(multi.unpack(args))
|
||||
end
|
||||
end)
|
||||
else
|
||||
multi.error("Invalid divide!", type(obj1), type(obj2),"Expected function/connection(table)")
|
||||
multi.error("Invalid divide!", type(obj1), type(obj2), "Expected function/connection(table)")
|
||||
end
|
||||
return cn
|
||||
end,
|
||||
__concat = function(obj1, obj2) -- ..
|
||||
local cn = self:newConnection()
|
||||
|
||||
__concat = function(obj1, obj2)
|
||||
local cn = trackChild(self:newConnection())
|
||||
local ref
|
||||
if type(obj1) == "function" and type(obj2) == "table" then
|
||||
cn(function(...)
|
||||
if obj1(...) then
|
||||
if obj1(...) == true then
|
||||
obj2:Fire(...)
|
||||
end
|
||||
end)
|
||||
@@ -320,26 +344,24 @@ function multi:newConnection(protect,func,kill)
|
||||
end
|
||||
end)
|
||||
end
|
||||
return obj1
|
||||
return cn
|
||||
elseif type(obj1) == "table" and type(obj2) == "table" then
|
||||
--
|
||||
-- reserved
|
||||
else
|
||||
error("Invalid concat!", type(obj1), type(obj2),"Expected function/connection(table), connection(table)/function")
|
||||
error("Invalid concat!", type(obj1), type(obj2), "Expected function/connection(table), connection(table)/function")
|
||||
end
|
||||
return cn
|
||||
end,
|
||||
__add = function(c1,c2) -- Or
|
||||
local cn = self:newConnection()
|
||||
c1(function(...)
|
||||
cn:Fire(...)
|
||||
end)
|
||||
c2(function(...)
|
||||
cn:Fire(...)
|
||||
end)
|
||||
|
||||
__add = function(c1, c2) -- Or
|
||||
local cn = trackChild(self:newConnection())
|
||||
c1(function(...) cn:Fire(...) end)
|
||||
c2(function(...) cn:Fire(...) end)
|
||||
return cn
|
||||
end,
|
||||
__mul = function(c1,c2) -- And
|
||||
local cn = self:newConnection()
|
||||
|
||||
__mul = function(c1, c2) -- And
|
||||
local cn = trackChild(self:newConnection())
|
||||
local ref1, ref2
|
||||
if c1.__hasInstances == nil then
|
||||
cn.__hasInstances = {2}
|
||||
@@ -367,12 +389,80 @@ function multi:newConnection(protect,func,kill)
|
||||
if cn.__count[1] == cn.__hasInstances[1] then
|
||||
cn:Fire(...)
|
||||
cn.__count[1] = 0
|
||||
c1:Unlock(ref1)
|
||||
c2:Unlock(ref2)
|
||||
end
|
||||
end)
|
||||
return cn
|
||||
end})
|
||||
end,
|
||||
})
|
||||
|
||||
-- ... (Type, ID, FC setup, Lock/Unlock, Fire, Connect, Bind, etc. unchanged) ...
|
||||
|
||||
--- Destroy this connection and all resources it owns.
|
||||
-- Recursively destroys any connections created by operator overloads (+, *, %, /, ..).
|
||||
-- Safe to call multiple times.
|
||||
function c:Destroy()
|
||||
if self.destroyed then return end
|
||||
self.destroyed = true
|
||||
|
||||
-- Unlock first so any in-progress Fire() calls drain cleanly
|
||||
lock = false
|
||||
|
||||
-- Destroy operator-spawned child connections recursively
|
||||
for i = 1, #self._child_conns do
|
||||
local child = self._child_conns[i]
|
||||
if child and type(child.Destroy) == "function" and not child.destroyed then
|
||||
child:Destroy()
|
||||
end
|
||||
end
|
||||
self._child_conns = {}
|
||||
|
||||
-- Null out root_link back-references on all connection handles
|
||||
for key, _ in pairs(fast) do
|
||||
if type(key) == "string" then
|
||||
local handle = fast[key]
|
||||
if type(handle) == "table" and rawget(handle, "root_link") then
|
||||
handle.root_link = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Adjust subscription count on parent before wiping fast[]
|
||||
if self.Parent and self.Parent.connection_subscriptions then
|
||||
self.Parent.connection_subscriptions =
|
||||
math.max(0, self.Parent.connection_subscriptions - #fast)
|
||||
end
|
||||
|
||||
-- Clear all stored callbacks
|
||||
fast = {}
|
||||
|
||||
-- Remove from parent's object list
|
||||
if self.Parent then
|
||||
for i = 1, #self.Parent do
|
||||
if self.Parent[i] == self then
|
||||
table.remove(self.Parent, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Detach hooks so stale references can't re-fire into dead state
|
||||
self.__connectionAdded = function() end
|
||||
self.Connect = function() multi:warning("Connect called on destroyed connection") end
|
||||
self.Fire = function() end
|
||||
self.Bind = function() return {} end
|
||||
self.Unconnect = function() end
|
||||
self.Lock = function() return self end
|
||||
self.Unlock = function() return self end
|
||||
|
||||
if self.Parent and self.Parent.connection_count then
|
||||
self.Parent.connection_count = math.max(0, self.Parent.connection_count - 1)
|
||||
end
|
||||
|
||||
self.Parent = nil
|
||||
end
|
||||
|
||||
-- Alias
|
||||
c.destroy = c.Destroy
|
||||
|
||||
c.Type=multi.registerType("connector", "connections")
|
||||
c.func={}
|
||||
@@ -402,7 +492,8 @@ function multi:newConnection(protect,func,kill)
|
||||
|
||||
function c:Unlock(conn)
|
||||
if conn and conn.lock then
|
||||
for i = 1, #fast do
|
||||
local n = #fast
|
||||
for i = 1, n do
|
||||
if conn.lock == fast[i] then
|
||||
fast[i] = fast[conn.ref]
|
||||
return self
|
||||
@@ -418,12 +509,13 @@ function multi:newConnection(protect,func,kill)
|
||||
function c:Fire(...)
|
||||
if lock then return end
|
||||
local kills = {}
|
||||
for i=1,#fast do
|
||||
local n = #fast
|
||||
for i=1, n do
|
||||
local suc, err = pcall(fast[i], ...)
|
||||
if not suc then
|
||||
multi.error(err)
|
||||
end
|
||||
if kill then
|
||||
if kill and n > 0 then
|
||||
table.insert(kills,i)
|
||||
processor:newTask(function()
|
||||
for _, k in pairs(kills) do
|
||||
@@ -448,6 +540,7 @@ function multi:newConnection(protect,func,kill)
|
||||
for i = 1, #fast do
|
||||
if fast[conn.ref] == fast[i] then
|
||||
table.remove(self)
|
||||
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions - 1
|
||||
return table.remove(fast, i), i
|
||||
end
|
||||
end
|
||||
@@ -476,12 +569,18 @@ function multi:newConnection(protect,func,kill)
|
||||
function c:Fire(...)
|
||||
if lock then return end
|
||||
for i=1,#fast do
|
||||
if fast[i] then
|
||||
fast[i](...)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function c:Connect(func, name)
|
||||
if func == nil then
|
||||
multi.error("You must provide a valid function when calling a connection")
|
||||
end
|
||||
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions + 1
|
||||
local th
|
||||
if thread.getRunningThread then
|
||||
th = thread.getRunningThread()
|
||||
@@ -494,11 +593,12 @@ function multi:newConnection(protect,func,kill)
|
||||
__CurrentConnectionThread = nil
|
||||
end
|
||||
end
|
||||
local ref = multi.randomString(24)
|
||||
table.insert(fast, func)
|
||||
if name then
|
||||
fast[name] = func
|
||||
else
|
||||
fast["Conn_"..multi.randomString(12)] = func
|
||||
fast["Conn_"..ref:sub(1, 12)] = func
|
||||
end
|
||||
local temp = {fast = true}
|
||||
setmetatable(temp,{
|
||||
@@ -518,9 +618,13 @@ function multi:newConnection(protect,func,kill)
|
||||
rawset(t,k,v)
|
||||
end,
|
||||
})
|
||||
temp.ref = multi.randomString(24)
|
||||
temp.ref = ref
|
||||
fast[temp.ref] = func
|
||||
temp.name = name
|
||||
temp.link = self
|
||||
function temp:Unconnect()
|
||||
self.link:Unconnect(self)
|
||||
end
|
||||
if self.rawadd then
|
||||
self.rawadd = false
|
||||
else
|
||||
@@ -532,7 +636,9 @@ function multi:newConnection(protect,func,kill)
|
||||
|
||||
function c:Bind(t)
|
||||
local temp = fast
|
||||
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions - #fast
|
||||
fast=t
|
||||
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions + #t
|
||||
return temp
|
||||
end
|
||||
|
||||
@@ -542,6 +648,7 @@ function multi:newConnection(protect,func,kill)
|
||||
|
||||
function c:Remove()
|
||||
local temp = fast
|
||||
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions - #fast
|
||||
fast={}
|
||||
return temp
|
||||
end
|
||||
@@ -610,10 +717,10 @@ function multi:SetTime(n)
|
||||
c.OnTimedOut = self:newConnection()
|
||||
c.OnTimerResolved = self:newConnection()
|
||||
self._timer=c.timer
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
if self.timer:Get()>=self.set then
|
||||
self.link:Pause()
|
||||
self.OnTimedOut:Fire(self.link)
|
||||
self.OnTimedOut:Fire(self.link,dt)
|
||||
self:Destroy()
|
||||
return true
|
||||
end
|
||||
@@ -698,17 +805,12 @@ function multi:isDone()
|
||||
return self.Active~=true
|
||||
end
|
||||
|
||||
local time = os.time
|
||||
local ok, chronos = pcall(require, "chronos") -- hpc
|
||||
|
||||
if ok then
|
||||
math.randomseed(chronos.nanotime()*100000000)
|
||||
else
|
||||
math.randomseed(time())
|
||||
end
|
||||
|
||||
function multi:create(ref)
|
||||
ref.UID = multi.generate_uuid7()
|
||||
ref.UPTIME = clock()
|
||||
if ref.setPriority then
|
||||
ref:setPriority("normal")
|
||||
end
|
||||
self.OnObjectCreated:Fire(ref, self)
|
||||
return self
|
||||
end
|
||||
@@ -813,12 +915,12 @@ end
|
||||
function multi:newEvent(task, func)
|
||||
local c,err=self:newBase(multi.registerType("event", "events"))
|
||||
local task = task or function() end
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
local t = task(self)
|
||||
if t then
|
||||
self:Pause()
|
||||
self.returns = t
|
||||
self.OnEvent:Fire(self)
|
||||
self.OnEvent:Fire(self,dt)
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -840,10 +942,10 @@ function multi:newUpdater(skip, func)
|
||||
local c,err=self:newBase(multi.registerType("updater", "updaters"))
|
||||
local pos = 1
|
||||
local skip = skip or 1
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
if pos >= skip then
|
||||
pos = 0
|
||||
self.OnUpdate:Fire(self)
|
||||
self.OnUpdate:Fire(self,dt)
|
||||
return true
|
||||
end
|
||||
pos = pos+1
|
||||
@@ -867,11 +969,11 @@ function multi:newAlarm(set, func)
|
||||
c.set=set or 0
|
||||
local count = 0
|
||||
local t = clock()
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
if clock()-t>=self.set then
|
||||
self:Pause()
|
||||
self.Active=false
|
||||
self.OnRing:Fire(self)
|
||||
self.OnRing:Fire(self,dt)
|
||||
t = clock()
|
||||
return true
|
||||
end
|
||||
@@ -905,13 +1007,13 @@ function multi:newLoop(func, notime)
|
||||
local c,err=self:newBase(multi.registerType("loop", "loops"))
|
||||
local start=clock()
|
||||
if notime then
|
||||
function c:Act()
|
||||
self.OnLoop:Fire(self)
|
||||
function c:Act(dt)
|
||||
self.OnLoop:Fire(self,nil,dt)
|
||||
return true
|
||||
end
|
||||
else
|
||||
function c:Act()
|
||||
self.OnLoop:Fire(self,clock()-start)
|
||||
function c:Act(dt)
|
||||
self.OnLoop:Fire(self,clock()-start,dt)
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -941,13 +1043,13 @@ function multi:newStep(start,reset,count,skip)
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.pos==self.start then
|
||||
self.OnStart:Fire(self)
|
||||
end
|
||||
self.OnStep:Fire(self,self.pos)
|
||||
self.OnStep:Fire(self,self.pos,dt)
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
@@ -993,11 +1095,11 @@ function multi:newTLoop(func, set)
|
||||
c.life=0
|
||||
c:setPriority("Low")
|
||||
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
if self.timer:Get() >= self.set then
|
||||
self.life=self.life+1
|
||||
self.timer:Reset()
|
||||
self.OnLoop:Fire(self, self.life)
|
||||
self.OnLoop:Fire(self, self.life,dt)
|
||||
return true
|
||||
end
|
||||
end
|
||||
@@ -1052,13 +1154,13 @@ function multi:newTStep(start,reset,count,set)
|
||||
self:Resume()
|
||||
return self
|
||||
end
|
||||
function c:Act()
|
||||
function c:Act(dt)
|
||||
if clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
self.OnStart:Fire(self)
|
||||
end
|
||||
self.OnStep:Fire(self,self.pos)
|
||||
self.OnStep:Fire(self,self.pos,dt)
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
@@ -1181,6 +1283,8 @@ function multi:newProcessor(name, opts, priority)
|
||||
end
|
||||
|
||||
sandcount = sandcount + 1
|
||||
c.connection_count = 0
|
||||
c.connection_subscriptions = 0
|
||||
c.Mainloop = {}
|
||||
c.Type = multi.registerType("process", "processes")
|
||||
local Active = nothread or false
|
||||
@@ -1196,7 +1300,7 @@ function multi:newProcessor(name, opts, priority)
|
||||
local handler
|
||||
|
||||
if type(opts) == "table" then
|
||||
priority = opts.Priority or false
|
||||
priority = opts.Priority
|
||||
Active = opts.Start or false
|
||||
maxThreads = opts.MaxThreads or -1
|
||||
maxObjects = opts.MaxObjects or -1
|
||||
@@ -1207,7 +1311,7 @@ function multi:newProcessor(name, opts, priority)
|
||||
end
|
||||
end
|
||||
|
||||
if priority then
|
||||
if priority or multi.defaultSettings.priority then
|
||||
handler = c:createPriorityHandler(c)
|
||||
else
|
||||
handler = c:createHandler(c)
|
||||
@@ -1280,27 +1384,27 @@ function multi:newProcessor(name, opts, priority)
|
||||
function c:boost(count)
|
||||
boost = count or 1
|
||||
if boost > 1 then
|
||||
self.run = function()
|
||||
self.run = function(dt)
|
||||
if not Active then return end
|
||||
for i=1,boost do
|
||||
c:uManager(true)
|
||||
c:uManager(dt)
|
||||
handler()
|
||||
end
|
||||
return c
|
||||
end
|
||||
else
|
||||
self.run = function()
|
||||
self.run = function(dt)
|
||||
if not Active then return end
|
||||
c:uManager(true)
|
||||
c:uManager(dt)
|
||||
handler()
|
||||
return c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function c.run()
|
||||
function c.run(dt)
|
||||
if not Active then return end
|
||||
c:uManager(true)
|
||||
c:uManager(dt)
|
||||
handler()
|
||||
return c
|
||||
end
|
||||
@@ -1309,6 +1413,10 @@ function multi:newProcessor(name, opts, priority)
|
||||
return Active
|
||||
end
|
||||
|
||||
function c:isPaused()
|
||||
return not(Active)
|
||||
end
|
||||
|
||||
function c.Start()
|
||||
Active = true
|
||||
return c
|
||||
@@ -1321,8 +1429,10 @@ function multi:newProcessor(name, opts, priority)
|
||||
|
||||
function c:Destroy()
|
||||
Active = false
|
||||
if c.process then
|
||||
c.process:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
function c:setTaskDelay(delay)
|
||||
if type(delay) == "function" then
|
||||
@@ -1517,8 +1627,9 @@ function thread.skip(n)
|
||||
return yield(CMD, t_skip, n or 1)
|
||||
end
|
||||
|
||||
function thread.kill()
|
||||
multi.error("thread killed!")
|
||||
function thread.kill(msg)
|
||||
msg = msg or "thread killed!"
|
||||
multi.error(msg)
|
||||
end
|
||||
|
||||
function thread.yield()
|
||||
@@ -1550,7 +1661,8 @@ function thread.get(name)
|
||||
end
|
||||
|
||||
function thread.waitFor(name)
|
||||
thread.hold(function() return thread.get(name)~=nil end)
|
||||
local check = function() return thread.get(name)~=nil end
|
||||
thread.hold(check)
|
||||
return thread.get(name)
|
||||
end
|
||||
|
||||
@@ -1578,6 +1690,7 @@ function thread:newFunctionBase(generator, holdme, TYPE)
|
||||
local tfunc = {
|
||||
GetCreationTimestamp = function() return multi.extract_uuid7_timestamp(UID).iso8601 end,
|
||||
}
|
||||
tfunc.UPTIME = clock()
|
||||
tfunc.Active = true
|
||||
function tfunc:Pause()
|
||||
self.Active = false
|
||||
@@ -1592,9 +1705,7 @@ function thread:newFunctionBase(generator, holdme, TYPE)
|
||||
return nil, "Function is paused"
|
||||
end
|
||||
local rets, err
|
||||
local function wait()
|
||||
if thread.isThread() then
|
||||
return thread.hold(function()
|
||||
local check = function()
|
||||
if err then
|
||||
return multi.NIL, err
|
||||
elseif rets then
|
||||
@@ -1602,7 +1713,10 @@ function thread:newFunctionBase(generator, holdme, TYPE)
|
||||
rets = nil
|
||||
return cleanReturns((g[1] or multi.NIL),g[2],g[3],g[4],g[5],g[6],g[7],g[8],g[9],g[10],g[11],g[12],g[13],g[14],g[15],g[16])
|
||||
end
|
||||
end)
|
||||
end
|
||||
local function wait()
|
||||
if thread.isThread() then
|
||||
return thread.hold(check)
|
||||
else
|
||||
while not rets and not err do
|
||||
multi:uManager()
|
||||
@@ -1751,7 +1865,7 @@ function thread:newThread(name, func, ...)
|
||||
multi.OnLoad:Fire() -- This was done incase a threaded function was called before mainloop/uManager was called
|
||||
if type(name) == "function" then
|
||||
func = name
|
||||
name = "UnnamedThread_"..multi.randomString(16)
|
||||
name = "UnnamedThread_"..multi.randomString(4)
|
||||
end
|
||||
local c={nil,nil,nil,nil,nil,nil,nil}
|
||||
c.TempRets = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
|
||||
@@ -2326,7 +2440,7 @@ function multi.init(settings, realsettings)
|
||||
return _G["$multi"].multi,_G["$multi"].thread
|
||||
end
|
||||
|
||||
function multi:uManager()
|
||||
function multi:uManager(dt)
|
||||
if self.Active then
|
||||
__CurrentProcess = self
|
||||
multi.OnPreLoad:Fire()
|
||||
@@ -2335,7 +2449,7 @@ function multi:uManager()
|
||||
end
|
||||
end
|
||||
|
||||
function multi:uManagerRefP1()
|
||||
function multi:uManagerRefP1(dt)
|
||||
if self.Active then
|
||||
__CurrentProcess = self
|
||||
local Loop=self.Mainloop
|
||||
@@ -2343,7 +2457,7 @@ function multi:uManagerRefP1()
|
||||
__CurrentTask = Loop[_D]
|
||||
for P=1,9 do
|
||||
if PList[P]%__CurrentTask.Priority==0 then
|
||||
__CurrentTask:Act()
|
||||
__CurrentTask:Act(dt)
|
||||
__CurrentProcess = self
|
||||
end
|
||||
end
|
||||
@@ -2351,13 +2465,13 @@ function multi:uManagerRefP1()
|
||||
end
|
||||
end
|
||||
|
||||
function multi:uManagerRef()
|
||||
function multi:uManagerRef(dt)
|
||||
if self.Active then
|
||||
__CurrentProcess = self
|
||||
local Loop=self.Mainloop
|
||||
for _D=#Loop,1,-1 do
|
||||
__CurrentTask = Loop[_D]
|
||||
__CurrentTask:Act()
|
||||
__CurrentTask:Act(dt)
|
||||
__CurrentProcess = self
|
||||
end
|
||||
end
|
||||
@@ -2389,7 +2503,7 @@ function table.merge(t1, t2)
|
||||
return t1
|
||||
end
|
||||
|
||||
math.randomseed(os.time())
|
||||
math.randomseed(math.floor(os.time()))
|
||||
|
||||
function multi:enableLoadDetection()
|
||||
if multi.maxSpd then return end
|
||||
@@ -2397,7 +2511,7 @@ function multi:enableLoadDetection()
|
||||
local temp = self:newProcessor()
|
||||
local t = clock()
|
||||
local stop = false
|
||||
temp:benchMark(.01):OnBench(function(time,steps)
|
||||
temp:benchMark(.1):OnBench(function(time,steps)
|
||||
stop = steps
|
||||
end)
|
||||
while not stop do
|
||||
@@ -2410,28 +2524,39 @@ end
|
||||
local lastVal = 0
|
||||
local last_step = 0
|
||||
|
||||
function multi:getLoad()
|
||||
if not multi.maxSpd then multi:enableLoadDetection() end
|
||||
function multi:getLoad(loops)
|
||||
local proc = proc or multi
|
||||
if not proc.maxSpd then proc:enableLoadDetection() end
|
||||
local val = nil
|
||||
local bench
|
||||
local bb
|
||||
self:benchMark(.01).OnBench(function(time,steps)
|
||||
local avg = 0
|
||||
local loops = loops or 5
|
||||
for i=1,loops do
|
||||
self:benchMark(1).OnBench(function(time,steps)
|
||||
bench = steps
|
||||
bb = steps
|
||||
avg = avg + steps
|
||||
if steps > proc.maxSpd then
|
||||
proc.maxSpd = steps
|
||||
end
|
||||
end)
|
||||
_,timeout = multi.hold(function()
|
||||
return bench
|
||||
end,{sleep=.012})
|
||||
end,{sleep=1.1})
|
||||
end
|
||||
avg = avg/loops
|
||||
if timeout or not bench then
|
||||
bench = 0
|
||||
bb = 0
|
||||
end
|
||||
bench = bench^1.5
|
||||
val = math.ceil((1-(bench/(multi.maxSpd/2.2)))*100)
|
||||
val = math.ceil((1-(bench/(proc.maxSpd/2.2)))*100)
|
||||
if val<0 then val = 0 end
|
||||
if val > 100 then val = 100 end
|
||||
lastVal = val
|
||||
last_step = bb*100
|
||||
print(proc.maxSpd, bench, bench/proc.maxSpd/2.2,val)
|
||||
return val,last_step
|
||||
end
|
||||
|
||||
@@ -2532,8 +2657,8 @@ function multi:benchMark(sec,p,pt)
|
||||
end
|
||||
|
||||
function multi.Round(num, numDecimalPlaces)
|
||||
local mult = 10^(numDecimalPlaces or 0)
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
local mult = 10 ^ (numDecimalPlaces or 0)
|
||||
return math.floor((num * mult) + 0.5 + 1e-10) / mult
|
||||
end
|
||||
|
||||
function multi.AlignTable(tab)
|
||||
@@ -2582,6 +2707,12 @@ end
|
||||
function multi:reallocate(processor, index)
|
||||
index=index or #processor.Mainloop+1
|
||||
local int=self.Parent
|
||||
for i = #int.Mainloop, 1, -1 do
|
||||
if int.Mainloop[i] == self then
|
||||
table.remove(int.Mainloop,i)
|
||||
break
|
||||
end
|
||||
end
|
||||
self.Parent=processor
|
||||
if index then
|
||||
table.insert(processor.Mainloop, index, self)
|
||||
@@ -2679,11 +2810,8 @@ local function random_hex(len)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
math.randomseed(math.floor(os.time()))
|
||||
multi.generate_uuid7 = function()
|
||||
-- Seed random number generator with current time
|
||||
math.randomseed(os.time() * os.clock() * 1000000)
|
||||
|
||||
-- Get timestamp in milliseconds
|
||||
local timestamp_ms = get_timestamp_ms()
|
||||
|
||||
|
||||
@@ -58,16 +58,20 @@ function multi:newSystemThreadedQueue(name)
|
||||
GLOBAL[name] = c
|
||||
end
|
||||
|
||||
local peek = function()
|
||||
return c:peek()
|
||||
end
|
||||
|
||||
local pop = function()
|
||||
return c:pop()
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
local multi, thread = require("multi"):init()
|
||||
if opt.peek then
|
||||
return thread.hold(function()
|
||||
return self:peek()
|
||||
end)
|
||||
return thread.hold(peek)
|
||||
else
|
||||
return thread.hold(function()
|
||||
return self:pop()
|
||||
end)
|
||||
return thread.hold(pop)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ SOFTWARE.
|
||||
]]
|
||||
package.path = "?/init.lua;?.lua;" .. package.path
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
local pseudoProcessor = multi:newProcessor()
|
||||
|
||||
if multi.integration then
|
||||
return {
|
||||
init = function()
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
function love.conf(t)
|
||||
t.identity = nil -- The name of the save directory (string)
|
||||
t.version = "12.0" -- The LOVE version this game was made for (string)
|
||||
t.console = true -- Attach a console (boolean, Windows only)
|
||||
|
||||
t.window.title = "MultiThreadTest" -- The window title (string)
|
||||
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
|
||||
t.window.width = 1280 -- The window width (number)
|
||||
t.window.height = 720 -- The window height (number)
|
||||
|
||||
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
||||
t.window.resizable = true -- Let the window be user-resizable (boolean)
|
||||
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
|
||||
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
|
||||
t.window.fullscreen = false -- Enable fullscreen (boolean)
|
||||
t.window.fullscreentype = "desktop" -- Standard fullscreen or desktop fullscreen mode (string)
|
||||
t.window.vsync = false -- Enable vertical sync (boolean)
|
||||
t.window.fsaa = 2 -- The number of samples to use with multi-sampled antialiasing (number)
|
||||
t.window.display = 1 -- Index of the monitor to show the window in (number)
|
||||
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
|
||||
t.window.srgb = false -- Enable sRGB gamma correction when drawing to the screen (boolean)
|
||||
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
|
||||
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
|
||||
|
||||
t.modules.audio = false -- Enable the audio module (boolean)
|
||||
t.modules.event = false -- Enable the event module (boolean)
|
||||
t.modules.graphics = false -- Enable the graphics module (boolean)
|
||||
t.modules.image = false -- Enable the image module (boolean)
|
||||
t.modules.joystick = false -- Enable the joystick module (boolean)
|
||||
t.modules.keyboard = false -- Enable the keyboard module (boolean)
|
||||
t.modules.math = false -- Enable the math module (boolean)
|
||||
t.modules.mouse = false -- Enable the mouse module (boolean)
|
||||
t.modules.physics = false -- Enable the physics module (boolean)
|
||||
t.modules.sound = false -- Enable the sound module (boolean)
|
||||
t.modules.system = false -- Enable the system module (boolean)
|
||||
t.modules.timer = false -- Enable the timer module (boolean)
|
||||
t.modules.window = false -- Enable the window module (boolean)
|
||||
t.modules.thread = true -- Enable the thread module (boolean)
|
||||
end
|
||||
@@ -0,0 +1,244 @@
|
||||
--[[
|
||||
Test suite for multi.lua
|
||||
Run with: lua multi_test.lua
|
||||
|
||||
Requires multi.lua to be in the same directory or on the Lua path.
|
||||
Compatible with Lua 5.1, 5.2, 5.3, 5.4, and LuaJIT.
|
||||
]]
|
||||
package.path = "../?/init.lua;../?.lua;../init.lua;../?.lua;" .. package.path
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Minimal test runner
|
||||
-- ─────────────────────────────────────────────
|
||||
local passed, failed, skipped = 0, 0, 0
|
||||
local failures = {}
|
||||
|
||||
local function test(name, fn)
|
||||
local ok, err = pcall(fn)
|
||||
if ok then
|
||||
passed = passed + 1
|
||||
io.write("\x1b[92m ✓\x1b[0m " .. name .. "\n")
|
||||
else
|
||||
failed = failed + 1
|
||||
table.insert(failures, {name = name, err = tostring(err)})
|
||||
io.write("\x1b[91m ✗\x1b[0m " .. name .. "\n")
|
||||
io.write(" " .. tostring(err) .. "\n")
|
||||
end
|
||||
end
|
||||
|
||||
local function skip(name, reason)
|
||||
skipped = skipped + 1
|
||||
io.write("\x1b[93m -\x1b[0m " .. name .. " [SKIPPED: " .. (reason or "") .. "]\n")
|
||||
end
|
||||
|
||||
local function section(name)
|
||||
io.write("\n\x1b[97m── " .. name .. " ──\x1b[0m\n")
|
||||
end
|
||||
|
||||
local function assert_eq(a, b, msg)
|
||||
if a ~= b then
|
||||
error((msg or "assert_eq failed") .. ": expected " .. tostring(b) .. ", got " .. tostring(a), 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_truthy(v, msg)
|
||||
if not v then
|
||||
error((msg or "expected truthy value, got falsy") .. ": " .. tostring(v), 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_falsy(v, msg)
|
||||
if v then
|
||||
error((msg or "expected falsy value, got truthy") .. ": " .. tostring(v), 2)
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_type(v, t, msg)
|
||||
if type(v) ~= t then
|
||||
error((msg or "type mismatch") .. ": expected " .. t .. ", got " .. type(v), 2)
|
||||
end
|
||||
end
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Load the library
|
||||
-- ─────────────────────────────────────────────
|
||||
local multi, thread
|
||||
local ok, err = pcall(function()
|
||||
multi, thread = require("multi"):init()
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
io.write("\x1b[91mFATAL: Could not load multi.lua: " .. tostring(err) .. "\x1b[0m\n")
|
||||
io.write("Make sure multi.lua is in the same directory or on package.path.\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
-- Helper: run the scheduler for up to `max_ticks` ticks or until `done()` returns true.
|
||||
local function run_until(done, max_ticks)
|
||||
max_ticks = max_ticks or 10000
|
||||
for _ = 1, max_ticks do
|
||||
multi:uManager()
|
||||
if done and done() then return true end
|
||||
end
|
||||
return done == nil
|
||||
end
|
||||
|
||||
section("Initialization")
|
||||
|
||||
test("lanesManager init returns GLOBAL and THREAD", function()
|
||||
local GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
||||
assert_type(GLOBAL, "table")
|
||||
assert_type(THREAD, "table")
|
||||
end)
|
||||
|
||||
test("lanesManager sets integration table", function()
|
||||
assert_type(multi.integration.GLOBAL, "table")
|
||||
assert_type(multi.integration.THREAD, "table")
|
||||
end)
|
||||
|
||||
-- initialize once for rest of tests
|
||||
local GLOBAL, THREAD = require("multi.integration.lanesManager"):init()
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
section("SystemThreadedQueue")
|
||||
|
||||
test("queue push/pop", function()
|
||||
local q = multi:newSystemThreadedQueue("testQ1")
|
||||
|
||||
q:push(42)
|
||||
local val = q:pop()
|
||||
|
||||
assert_eq(val, 42)
|
||||
end)
|
||||
|
||||
test("queue peek does not remove", function()
|
||||
local q = multi:newSystemThreadedQueue("testQ2")
|
||||
|
||||
q:push("hello")
|
||||
|
||||
assert_eq(q:peek(), "hello")
|
||||
assert_eq(q:pop(), "hello")
|
||||
end)
|
||||
|
||||
test("queue empty returns nil", function()
|
||||
local q = multi:newSystemThreadedQueue("testQ3")
|
||||
|
||||
assert_eq(q:pop(), nil)
|
||||
end)
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
section("SystemThreadedTable")
|
||||
|
||||
test("table set/get", function()
|
||||
local t = multi:newSystemThreadedTable("testT1")
|
||||
|
||||
t.foo = "bar"
|
||||
assert_eq(t.foo, "bar")
|
||||
end)
|
||||
|
||||
test("table overwrite", function()
|
||||
local t = multi:newSystemThreadedTable("testT2")
|
||||
|
||||
t.x = 1
|
||||
t.x = 2
|
||||
|
||||
assert_eq(t.x, 2)
|
||||
end)
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
section("THREAD API")
|
||||
|
||||
test("THREAD set/get", function()
|
||||
THREAD.set("abc", 123)
|
||||
assert_eq(THREAD.get("abc"), 123)
|
||||
end)
|
||||
|
||||
test("THREAD getCores > 0", function()
|
||||
assert_truthy(THREAD.getCores() > 0)
|
||||
end)
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
section("SystemThreadedConnection")
|
||||
|
||||
test("connection fires event", function()
|
||||
local conn = multi:newSystemThreadedConnection("conn1"):init()
|
||||
|
||||
local received = nil
|
||||
|
||||
conn(function(v)
|
||||
received = v
|
||||
end)
|
||||
|
||||
conn:Fire("ping")
|
||||
|
||||
run_until(function()
|
||||
return received ~= nil
|
||||
end, 5000)
|
||||
|
||||
assert_eq(received, "ping")
|
||||
end)
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
section("SystemThreadedJobQueue")
|
||||
|
||||
test("job queue executes function", function()
|
||||
local jq = multi:newSystemThreadedJobQueue(1)
|
||||
|
||||
jq:registerFunction("add", function(a, b)
|
||||
return a + b
|
||||
end)
|
||||
|
||||
local result = nil
|
||||
|
||||
jq.OnJobCompleted(function(_, val)
|
||||
result = val
|
||||
end)
|
||||
|
||||
jq:pushJob("add", 2, 3)
|
||||
|
||||
run_until(function()
|
||||
return result ~= nil
|
||||
end, 10000)
|
||||
|
||||
assert_eq(result, 5)
|
||||
end)
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
section("JobQueue newFunction")
|
||||
|
||||
test("newFunction returns correct result", function()
|
||||
local jq = multi:newSystemThreadedJobQueue(1)
|
||||
|
||||
local fn = jq:newFunction(function(a, b)
|
||||
return a * b
|
||||
end,true)
|
||||
|
||||
local result = nil
|
||||
|
||||
result = fn(3, 4)
|
||||
|
||||
run_until(function()
|
||||
return result ~= nil
|
||||
end, 10000)
|
||||
|
||||
assert_eq(result, 12)
|
||||
end)
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Summary
|
||||
-- ─────────────────────────────────────────────
|
||||
section("Results")
|
||||
|
||||
io.write("\n")
|
||||
io.write("Passed: " .. passed .. "\n")
|
||||
io.write("Failed: " .. failed .. "\n")
|
||||
io.write("Skipped: " .. skipped .. "\n")
|
||||
|
||||
if failed > 0 then
|
||||
io.write("\nFailures:\n")
|
||||
for _, f in ipairs(failures) do
|
||||
io.write(" - " .. f.name .. "\n")
|
||||
io.write(" " .. f.err .. "\n")
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
@@ -1,10 +0,0 @@
|
||||
package.path = "../?/init.lua;../?.lua;"..package.path
|
||||
|
||||
if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE") == "1" then
|
||||
require("lldebugger").start()
|
||||
end
|
||||
|
||||
GLOBAL, THREAD = require("multi.integration.loveManager"):init()
|
||||
|
||||
require("runtests")
|
||||
require("threadtests")
|
||||
@@ -1 +0,0 @@
|
||||
../
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,195 +0,0 @@
|
||||
package.path = "../?/init.lua;../?.lua;./init.lua;./?.lua;"..package.path
|
||||
|
||||
local multi, thread = require("multi"):init{print=true,warn=true,error=true}--{priority=true}
|
||||
local good = false
|
||||
local proc = multi:newProcessor("Test")
|
||||
|
||||
print("Version: "..multi.Version)
|
||||
|
||||
proc.Start()
|
||||
|
||||
proc:newAlarm(3):OnRing(function()
|
||||
good = true
|
||||
end)
|
||||
|
||||
runTest = thread:newFunction(function()
|
||||
local alarms,tsteps,steps,loops,tloops,updaters,events=false,0,0,0,0,0,false
|
||||
multi.print("Testing Basic Features. If this fails most other features will probably not work!")
|
||||
proc:newAlarm(2):OnRing(function(a)
|
||||
alarms = true
|
||||
a:Destroy()
|
||||
end)
|
||||
proc:newTStep(1,10,1,.1):OnStep(function(t)
|
||||
tsteps = tsteps + 1
|
||||
end):OnEnd(function(step)
|
||||
step:Destroy()
|
||||
end)
|
||||
proc:newStep(1,10):OnStep(function(s)
|
||||
steps = steps + 1
|
||||
end):OnEnd(function(step)
|
||||
step:Destroy()
|
||||
end)
|
||||
local loop = proc:newLoop(function(l)
|
||||
loops = loops + 1
|
||||
end)
|
||||
proc:newTLoop(function(t)
|
||||
tloops = tloops + 1
|
||||
end,.1)
|
||||
local updater = proc:newUpdater(1):OnUpdate(function()
|
||||
updaters = updaters + 1
|
||||
end)
|
||||
local event = proc:newEvent(function()
|
||||
return alarms
|
||||
end)
|
||||
event.OnEvent(function(evnt)
|
||||
evnt:Destroy()
|
||||
events = true
|
||||
multi.success("Alarms: Ok")
|
||||
multi.success("Events: Ok")
|
||||
if tsteps == 10 then multi.success("TSteps: Ok") else multi.error("TSteps: Bad!") end
|
||||
if steps == 10 then multi.success("Steps: Ok") else multi.error("Steps: Bad!") end
|
||||
if loops > 100 then multi.success("Loops: Ok") else multi.error("Loops: Bad!") end
|
||||
if tloops > 10 then multi.success("TLoops: Ok") else multi.error("TLoops: Bad!") end
|
||||
if updaters > 100 then multi.success("Updaters: Ok") else multi.error("Updaters: Bad!") end
|
||||
end)
|
||||
thread.hold(event.OnEvent)
|
||||
multi.print("Starting Connection and Thread tests!")
|
||||
func = thread:newFunction(function(count)
|
||||
multi.print("Starting Status test: ",count)
|
||||
local a = 0
|
||||
while true do
|
||||
a = a + 1
|
||||
thread.sleep(.1)
|
||||
thread.pushStatus(a,count)
|
||||
if a == count then break end
|
||||
end
|
||||
return "Done", true, math.random(1,10000)
|
||||
end)
|
||||
local ret = func(10)
|
||||
local ret2 = func(15)
|
||||
local ret3 = func(20)
|
||||
local s1,s2,s3 = 0,0,0
|
||||
ret.OnError(function(...)
|
||||
multi.error("Func 1:",...)
|
||||
end)
|
||||
ret2.OnError(function(...)
|
||||
multi.error("Func 2:",...)
|
||||
end)
|
||||
ret3.OnError(function(...)
|
||||
multi.error("Func 3:",...)
|
||||
end)
|
||||
ret.OnStatus(function(part,whole)
|
||||
s1 = math.ceil((part/whole)*1000)/10
|
||||
end)
|
||||
ret2.OnStatus(function(part,whole)
|
||||
s2 = math.ceil((part/whole)*1000)/10
|
||||
end)
|
||||
ret3.OnStatus(function(part,whole)
|
||||
s3 = math.ceil((part/whole)*1000)/10
|
||||
end)
|
||||
|
||||
ret.OnReturn(function(...)
|
||||
multi.success("Done 1",...)
|
||||
end)
|
||||
ret2.OnReturn(function(...)
|
||||
multi.success("Done 2",...)
|
||||
end)
|
||||
ret3.OnReturn(function(...)
|
||||
multi.success("Done 3",...)
|
||||
end)
|
||||
|
||||
local err, timeout = thread.hold(ret.OnReturn * ret2.OnReturn * ret3.OnReturn)
|
||||
|
||||
if s1 == 100 and s2 == 100 and s3 == 100 then
|
||||
multi.success("Threads: All tests Ok")
|
||||
else
|
||||
if s1>0 and s2>0 and s3 > 0 then
|
||||
multi.success("Thread OnStatus: Ok")
|
||||
else
|
||||
multi.error("Threads OnStatus or thread.hold(conn) Error!")
|
||||
end
|
||||
if timeout then
|
||||
multi.error("Connection Error!")
|
||||
else
|
||||
multi.success("Connection Test 1: Ok")
|
||||
end
|
||||
multi.error("Connection holding Error!")
|
||||
end
|
||||
|
||||
conn1 = proc:newConnection()
|
||||
conn2 = proc:newConnection()
|
||||
conn3 = proc:newConnection()
|
||||
local c1,c2,c3,c4 = false,false,false,false
|
||||
|
||||
local a = conn1(function()
|
||||
c1 = true
|
||||
end)
|
||||
|
||||
local b = conn2(function()
|
||||
c2 = true
|
||||
end)
|
||||
|
||||
local c = conn3(function()
|
||||
c3 = true
|
||||
end)
|
||||
|
||||
local d = conn3(function()
|
||||
c4 = true
|
||||
end)
|
||||
|
||||
conn1:Fire()
|
||||
conn2:Fire()
|
||||
conn3:Fire()
|
||||
|
||||
if c1 and c2 and c3 and c4 then
|
||||
multi.success("Connection Test 2: Ok")
|
||||
else
|
||||
multi.error("Connection Test 2: Error")
|
||||
end
|
||||
c3 = false
|
||||
c4 = false
|
||||
conn3:Unconnect(d)
|
||||
conn3:Fire()
|
||||
if c3 and not(c4) then
|
||||
multi.success("Connection Test 3: Ok")
|
||||
else
|
||||
multi.error("Connection Test 3: Error removing connection")
|
||||
end
|
||||
if not love then
|
||||
local ec = 0
|
||||
multi.print("Testing pseudo threading")
|
||||
capture = io.popen("lua tests/threadtests.lua p"):read("*a")
|
||||
if capture:lower():match("error") then
|
||||
ec = ec + 1
|
||||
os.exit(1)
|
||||
else
|
||||
io.write(capture)
|
||||
end
|
||||
multi.print("Testing lanes threading")
|
||||
capture = io.popen("lua tests/threadtests.lua l"):read("*a")
|
||||
if capture:lower():match("error") then
|
||||
ec = ec + 1
|
||||
os.exit(1)
|
||||
else
|
||||
io.write(capture)
|
||||
end
|
||||
os.exit(0)
|
||||
end
|
||||
end)
|
||||
|
||||
local handle = runTest()
|
||||
|
||||
handle.OnError(function(...)
|
||||
multi.error("Something went wrong with the test!")
|
||||
print(...)
|
||||
end)
|
||||
|
||||
if not love then
|
||||
multi:mainloop()
|
||||
else
|
||||
local hold = thread:newFunction(function()
|
||||
thread.hold(handle.OnError + handle.OnReturn)
|
||||
end, true)
|
||||
hold()
|
||||
multi.print("Starting Threading tests!")
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
package.path = "D:/VSCWorkspace/?/init.lua;D:/VSCWorkspace/?.lua;"..package.path
|
||||
package.path = "../?/init.lua;../?.lua;../init.lua;../?.lua;" .. package.path
|
||||
package.cpath = "C:/luaInstalls/lua5.4/lib/lua/5.4/?/core.dll;" .. package.cpath
|
||||
multi, thread = require("multi"):init{error=true,warning=true,print=true, priority=true}
|
||||
proc = multi:newProcessor("Thread Test",true)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user