19 Commits
Author SHA1 Message Date
server 8b5d10cbb6 updated tests and added check in connections 2026-06-09 23:04:02 -07:00
server 0796788fcb removed dup func 2026-06-08 11:04:25 -07:00
server ee5e3bde7f fixed int issue with lua 5.5 2026-05-30 21:21:32 -07:00
server c86413351c updated opt for connections 2026-05-14 22:29:34 -07:00
server 4a52cd5e14 working on new documentation 2026-04-04 01:00:50 -07:00
server 14beeb686a making some changes 2026-04-01 22:22:05 -07:00
server 60783fdb16 remove luajit and 5.5 not supported with current actions 2026-03-23 22:07:57 -07:00
server b1a7e61c03 added lua 5.5 and using luajit 2026-03-23 22:06:18 -07:00
server 9fa0e7c2cb changed branch to master 2026-03-23 22:01:52 -07:00
server ef243f87d4 removed love2d test 2026-03-23 22:01:12 -07:00
server 178283e0b4 Merge branch 'master' into v16.3.0 2026-03-23 22:00:29 -07:00
server 7f35265a8e new actions (#78) 2026-03-23 21:59:47 -07:00
server 0501f068e4 testing new action 2026-03-23 21:55:35 -07:00
server ffd7ac15f6 added new unit tests 2026-03-23 21:52:56 -07:00
server 869368af56 updated docs 2026-03-23 21:26:40 -07:00
server eeb75490dc default priority is normal 2026-03-23 21:18:27 -07:00
server 2fec5b39b8 fixing bugs 2026-03-23 20:40:58 -07:00
server 0a92890290 working on task management 2026-03-15 01:13:00 -07:00
server 40de9a8d22 Improving tests 2026-01-12 23:15:31 -08:00
98 changed files with 30509 additions and 28904 deletions
-25
View File
@@ -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
+17 -26
View File
@@ -1,4 +1,4 @@
name: Build & Run tests Ubuntu name: Lua Unit Tests (Matrix)
on: on:
push: push:
@@ -7,35 +7,26 @@ on:
branches: [ master ] branches: [ master ]
jobs: jobs:
build: test:
runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
build-type: [Release] # Debug luaVersion: ["5.1", "5.2", "5.3", "5.4"]
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 }}
steps: steps:
- uses: actions/checkout@v2 - name: Checkout repository
uses: actions/checkout@v4
- name: Setup Lua
uses: leafo/gh-actions-lua@v10
with: with:
submodules: recursive luaVersion: ${{ matrix.luaVersion }}
- uses: actions/setup-python@v2 - name: Verify Lua version
with: run: lua -v
python-version: '3.10'
- name: Setup env - name: Run unit tests
run: | run: |
pip install hererocks # Replace with your actual test file
hererocks lua-pkg --${{ matrix.lua }} -rlatest lua tests/multi_test.lua
- 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
+5 -5
View File
@@ -1,8 +1,8 @@
# Multi Version: 16.1.0 - The Flow State # Multi Version: 16.3.0 -
**Key Changes** **Key Changes**
- Updated Processors to have more controlled over scheduled processes - Fixed connection multiplying
- Forwarding connections - Optimizing the library
- New Timeout handling - Updated task management support
Refer to the [Change Log](https://github.com/rayaman/multi/blob/master/docs/changes.md) for more infromation 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> </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> </br>
+28 -5
View File
@@ -1,7 +1,8 @@
# Changelog # Changelog
Table of contents Table of contents
--- ---
[Update 16.2.0 - TimeStamps and UUIDs](#update-1620---timestamps-and-uuids-and-bugfixes)</br> [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.1.0 - The Flow State](#update-1610---the-flow-state)</br>
[Update 16.0.1 - Bug fix](#update-1601---bug-fix)</br> [Update 16.0.1 - Bug fix](#update-1601---bug-fix)</br>
[Update 16.0.0 - Connecting the dots](#update-1600---getting-the-priorities-straight)</br> [Update 16.0.0 - Connecting the dots](#update-1600---getting-the-priorities-straight)</br>
@@ -61,6 +62,30 @@ Table of contents
[Update: EventManager 1.0.0 - Error checking](#update-eventmanager-100---error-checking)</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) [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 # Update 16.2.0 - TimeStamps and UUIDs and bugfixes
Added Added
--- ---
@@ -629,15 +654,13 @@ Added
Output: Output:
``` ```
I run before all and control if execution should continue! I run before all and control if things go!
Hi 3 Hi 3
Hi 1 Hi 1
I run before all and control if execution should continue!
Hi 3
Hi 2
Test 1 Test 1
Test 2 Test 2
Test 3 Test 3
I run after it all!
``` ```
**Note:** Concat of connections does modify internal events on both connections depending on the direction func .. conn or conn .. func See implemention below: **Note:** Concat of connections does modify internal events on both connections depending on the direction func .. conn or conn .. func See implemention below:
File diff suppressed because it is too large Load Diff
+222 -94
View File
@@ -92,7 +92,7 @@ function multi.getTypes()
return types return types
end end
multi.Version = "16.2.0" multi.Version = "16.3.0-testing"
multi.Name = "root" multi.Name = "root"
multi.NIL = {Type="NIL"} multi.NIL = {Type="NIL"}
local NIL = multi.NIL local NIL = multi.NIL
@@ -154,11 +154,18 @@ function multi:isType(type)
return self.Type == type return self.Type == type
end end
multi.connection_count = 0
multi.connection_subscriptions = 0
function multi:getStats() function multi:getStats()
local stats = { local stats = {
[multi.Name] = { [multi.Name] = {
threads = multi:getThreads(), 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() local procs = multi:getProcessors()
@@ -166,7 +173,11 @@ function multi:getStats()
local proc = procs[i] local proc = procs[i]
stats[proc:getFullName()] = { stats[proc:getFullName()] = {
threads = proc:getThreads(), threads = proc:getThreads(),
tasks = proc:getTasks() tasks = proc.Mainloop,
name = proc:getName(),
fullName = proc:getFullName(),
connections = proc.connection_count,
subscriptions = proc.connection_subscriptions
} }
end end
return stats return stats
@@ -202,7 +213,7 @@ end
function multi.forwardConnection(src, dest) function multi.forwardConnection(src, dest)
if multi.isMulitObj(src) and multi.isMulitObj(dest) then if multi.isMulitObj(src) and multi.isMulitObj(dest) then
src(function(...) return src(function(...)
dest:Fire(...) dest:Fire(...)
end) end)
else else
@@ -215,6 +226,7 @@ local ignoreconn = true
local empty_func = function() end local empty_func = function() end
function multi:newConnection(protect,func,kill) function multi:newConnection(protect,func,kill)
self.connection_count = self.connection_count + 1
local processor = self local processor = self
local c = {} local c = {}
local lock = false local lock = false
@@ -222,6 +234,14 @@ function multi:newConnection(protect,func,kill)
c.__connectionAdded = function() end c.__connectionAdded = function() end
c.rawadd = false c.rawadd = false
c.Parent = self c.Parent = self
c._child_conns = {} -- tracks connections spawned by operators
c.destroyed = false
-- Helper: register a child connection for cleanup
local function trackChild(cn)
c._child_conns[#c._child_conns + 1] = cn
return cn
end
setmetatable(c, { setmetatable(c, {
__call = function(self, ...) __call = function(self, ...)
@@ -242,7 +262,8 @@ function multi:newConnection(protect,func,kill)
return self:Connect(...) return self:Connect(...)
end end
end, end,
__unm = function(obj) -- -obj Reverses the order of connected events
__unm = function(obj)
local conns = obj:Bind({}) local conns = obj:Bind({})
for i = #conns, 1, -1 do for i = #conns, 1, -1 do
obj.rawadd = true obj.rawadd = true
@@ -251,13 +272,14 @@ function multi:newConnection(protect,func,kill)
end end
return obj return obj
end, end,
__mod = function(obj1, obj2) -- %
local cn = self:newConnection() __mod = function(obj1, obj2)
if type(obj1) == "function" and type(obj2) == "table" then 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(...) obj2(function(...)
cn:Fire(obj1(...)) cn:Fire(obj1(...))
end) 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({}) local conns = obj1:Bind({})
for i = 1, #conns do for i = 1, #conns do
obj1(function(...) obj1(function(...)
@@ -274,17 +296,18 @@ function multi:newConnection(protect,func,kill)
end end
return obj1 return obj1
else 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 end
return cn return cn
end, end,
__div = function(obj1, obj2) -- /
local cn = self:newConnection() __div = function(obj1, obj2)
local ref local cn = trackChild(self:newConnection())
if type(obj1) == "function" and type(obj2) == "table" then if type(obj1) == "function" and type(obj2) == "table" then
obj2(function(...) obj2(function(...)
local args = { obj1(...) } local args = { obj1(...) }
if args[1] then if args[1] then
table.remove(args, 1)
cn:Fire(multi.unpack(args)) cn:Fire(multi.unpack(args))
end end
end) end)
@@ -293,12 +316,13 @@ function multi:newConnection(protect,func,kill)
end end
return cn return cn
end, end,
__concat = function(obj1, obj2) -- ..
local cn = self:newConnection() __concat = function(obj1, obj2)
local cn = trackChild(self:newConnection())
local ref local ref
if type(obj1) == "function" and type(obj2) == "table" then if type(obj1) == "function" and type(obj2) == "table" then
cn(function(...) cn(function(...)
if obj1(...) then if obj1(...) == true then
obj2:Fire(...) obj2:Fire(...)
end end
end) end)
@@ -320,26 +344,24 @@ function multi:newConnection(protect,func,kill)
end end
end) end)
end end
return obj1 return cn
elseif type(obj1) == "table" and type(obj2) == "table" then elseif type(obj1) == "table" and type(obj2) == "table" then
-- -- reserved
else 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 end
return cn return cn
end, end,
__add = function(c1, c2) -- Or __add = function(c1, c2) -- Or
local cn = self:newConnection() local cn = trackChild(self:newConnection())
c1(function(...) c1(function(...) cn:Fire(...) end)
cn:Fire(...) c2(function(...) cn:Fire(...) end)
end)
c2(function(...)
cn:Fire(...)
end)
return cn return cn
end, end,
__mul = function(c1, c2) -- And __mul = function(c1, c2) -- And
local cn = self:newConnection() local cn = trackChild(self:newConnection())
local ref1, ref2 local ref1, ref2
if c1.__hasInstances == nil then if c1.__hasInstances == nil then
cn.__hasInstances = {2} cn.__hasInstances = {2}
@@ -367,12 +389,80 @@ function multi:newConnection(protect,func,kill)
if cn.__count[1] == cn.__hasInstances[1] then if cn.__count[1] == cn.__hasInstances[1] then
cn:Fire(...) cn:Fire(...)
cn.__count[1] = 0 cn.__count[1] = 0
c1:Unlock(ref1)
c2:Unlock(ref2)
end end
end) end)
return cn 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.Type=multi.registerType("connector", "connections")
c.func={} c.func={}
@@ -402,7 +492,8 @@ function multi:newConnection(protect,func,kill)
function c:Unlock(conn) function c:Unlock(conn)
if conn and conn.lock then 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 if conn.lock == fast[i] then
fast[i] = fast[conn.ref] fast[i] = fast[conn.ref]
return self return self
@@ -418,12 +509,13 @@ function multi:newConnection(protect,func,kill)
function c:Fire(...) function c:Fire(...)
if lock then return end if lock then return end
local kills = {} local kills = {}
for i=1,#fast do local n = #fast
for i=1, n do
local suc, err = pcall(fast[i], ...) local suc, err = pcall(fast[i], ...)
if not suc then if not suc then
multi.error(err) multi.error(err)
end end
if kill then if kill and n > 0 then
table.insert(kills,i) table.insert(kills,i)
processor:newTask(function() processor:newTask(function()
for _, k in pairs(kills) do for _, k in pairs(kills) do
@@ -448,6 +540,7 @@ function multi:newConnection(protect,func,kill)
for i = 1, #fast do for i = 1, #fast do
if fast[conn.ref] == fast[i] then if fast[conn.ref] == fast[i] then
table.remove(self) table.remove(self)
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions - 1
return table.remove(fast, i), i return table.remove(fast, i), i
end end
end end
@@ -476,12 +569,18 @@ function multi:newConnection(protect,func,kill)
function c:Fire(...) function c:Fire(...)
if lock then return end if lock then return end
for i=1,#fast do for i=1,#fast do
if fast[i] then
fast[i](...) fast[i](...)
end end
end end
end end
end
function c:Connect(func, name) 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 local th
if thread.getRunningThread then if thread.getRunningThread then
th = thread.getRunningThread() th = thread.getRunningThread()
@@ -494,11 +593,12 @@ function multi:newConnection(protect,func,kill)
__CurrentConnectionThread = nil __CurrentConnectionThread = nil
end end
end end
local ref = multi.randomString(24)
table.insert(fast, func) table.insert(fast, func)
if name then if name then
fast[name] = func fast[name] = func
else else
fast["Conn_"..multi.randomString(12)] = func fast["Conn_"..ref:sub(1, 12)] = func
end end
local temp = {fast = true} local temp = {fast = true}
setmetatable(temp,{ setmetatable(temp,{
@@ -518,9 +618,13 @@ function multi:newConnection(protect,func,kill)
rawset(t,k,v) rawset(t,k,v)
end, end,
}) })
temp.ref = multi.randomString(24) temp.ref = ref
fast[temp.ref] = func fast[temp.ref] = func
temp.name = name temp.name = name
temp.link = self
function temp:Unconnect()
self.link:Unconnect(self)
end
if self.rawadd then if self.rawadd then
self.rawadd = false self.rawadd = false
else else
@@ -532,7 +636,9 @@ function multi:newConnection(protect,func,kill)
function c:Bind(t) function c:Bind(t)
local temp = fast local temp = fast
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions - #fast
fast=t fast=t
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions + #t
return temp return temp
end end
@@ -542,6 +648,7 @@ function multi:newConnection(protect,func,kill)
function c:Remove() function c:Remove()
local temp = fast local temp = fast
self.Parent.connection_subscriptions = self.Parent.connection_subscriptions - #fast
fast={} fast={}
return temp return temp
end end
@@ -610,10 +717,10 @@ function multi:SetTime(n)
c.OnTimedOut = self:newConnection() c.OnTimedOut = self:newConnection()
c.OnTimerResolved = self:newConnection() c.OnTimerResolved = self:newConnection()
self._timer=c.timer self._timer=c.timer
function c:Act() function c:Act(dt)
if self.timer:Get()>=self.set then if self.timer:Get()>=self.set then
self.link:Pause() self.link:Pause()
self.OnTimedOut:Fire(self.link) self.OnTimedOut:Fire(self.link,dt)
self:Destroy() self:Destroy()
return true return true
end end
@@ -698,17 +805,12 @@ function multi:isDone()
return self.Active~=true return self.Active~=true
end 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) function multi:create(ref)
ref.UID = multi.generate_uuid7() ref.UID = multi.generate_uuid7()
ref.UPTIME = clock()
if ref.setPriority then
ref:setPriority("normal")
end
self.OnObjectCreated:Fire(ref, self) self.OnObjectCreated:Fire(ref, self)
return self return self
end end
@@ -813,12 +915,12 @@ end
function multi:newEvent(task, func) function multi:newEvent(task, func)
local c,err=self:newBase(multi.registerType("event", "events")) local c,err=self:newBase(multi.registerType("event", "events"))
local task = task or function() end local task = task or function() end
function c:Act() function c:Act(dt)
local t = task(self) local t = task(self)
if t then if t then
self:Pause() self:Pause()
self.returns = t self.returns = t
self.OnEvent:Fire(self) self.OnEvent:Fire(self,dt)
return true return true
end end
end end
@@ -840,10 +942,10 @@ function multi:newUpdater(skip, func)
local c,err=self:newBase(multi.registerType("updater", "updaters")) local c,err=self:newBase(multi.registerType("updater", "updaters"))
local pos = 1 local pos = 1
local skip = skip or 1 local skip = skip or 1
function c:Act() function c:Act(dt)
if pos >= skip then if pos >= skip then
pos = 0 pos = 0
self.OnUpdate:Fire(self) self.OnUpdate:Fire(self,dt)
return true return true
end end
pos = pos+1 pos = pos+1
@@ -867,11 +969,11 @@ function multi:newAlarm(set, func)
c.set=set or 0 c.set=set or 0
local count = 0 local count = 0
local t = clock() local t = clock()
function c:Act() function c:Act(dt)
if clock()-t>=self.set then if clock()-t>=self.set then
self:Pause() self:Pause()
self.Active=false self.Active=false
self.OnRing:Fire(self) self.OnRing:Fire(self,dt)
t = clock() t = clock()
return true return true
end end
@@ -905,13 +1007,13 @@ function multi:newLoop(func, notime)
local c,err=self:newBase(multi.registerType("loop", "loops")) local c,err=self:newBase(multi.registerType("loop", "loops"))
local start=clock() local start=clock()
if notime then if notime then
function c:Act() function c:Act(dt)
self.OnLoop:Fire(self) self.OnLoop:Fire(self,nil,dt)
return true return true
end end
else else
function c:Act() function c:Act(dt)
self.OnLoop:Fire(self,clock()-start) self.OnLoop:Fire(self,clock()-start,dt)
return true return true
end end
end end
@@ -941,13 +1043,13 @@ function multi:newStep(start,reset,count,skip)
think=-1 think=-1
end end
end end
function c:Act() function c:Act(dt)
if self~=nil then if self~=nil then
if self.spos==0 then if self.spos==0 then
if self.pos==self.start then if self.pos==self.start then
self.OnStart:Fire(self) self.OnStart:Fire(self)
end end
self.OnStep:Fire(self,self.pos) self.OnStep:Fire(self,self.pos,dt)
self.pos=self.pos+self.count self.pos=self.pos+self.count
if self.pos-self.count==self.endAt then if self.pos-self.count==self.endAt then
self:Pause() self:Pause()
@@ -993,11 +1095,11 @@ function multi:newTLoop(func, set)
c.life=0 c.life=0
c:setPriority("Low") c:setPriority("Low")
function c:Act() function c:Act(dt)
if self.timer:Get() >= self.set then if self.timer:Get() >= self.set then
self.life=self.life+1 self.life=self.life+1
self.timer:Reset() self.timer:Reset()
self.OnLoop:Fire(self, self.life) self.OnLoop:Fire(self, self.life,dt)
return true return true
end end
end end
@@ -1052,13 +1154,13 @@ function multi:newTStep(start,reset,count,set)
self:Resume() self:Resume()
return self return self
end end
function c:Act() function c:Act(dt)
if clock()-self.timer>=self.set then if clock()-self.timer>=self.set then
self:Reset() self:Reset()
if self.pos==self.start then if self.pos==self.start then
self.OnStart:Fire(self) self.OnStart:Fire(self)
end end
self.OnStep:Fire(self,self.pos) self.OnStep:Fire(self,self.pos,dt)
self.pos=self.pos+self.count self.pos=self.pos+self.count
if self.pos-self.count==self.endAt then if self.pos-self.count==self.endAt then
self:Pause() self:Pause()
@@ -1181,6 +1283,8 @@ function multi:newProcessor(name, opts, priority)
end end
sandcount = sandcount + 1 sandcount = sandcount + 1
c.connection_count = 0
c.connection_subscriptions = 0
c.Mainloop = {} c.Mainloop = {}
c.Type = multi.registerType("process", "processes") c.Type = multi.registerType("process", "processes")
local Active = nothread or false local Active = nothread or false
@@ -1196,7 +1300,7 @@ function multi:newProcessor(name, opts, priority)
local handler local handler
if type(opts) == "table" then if type(opts) == "table" then
priority = opts.Priority or false priority = opts.Priority
Active = opts.Start or false Active = opts.Start or false
maxThreads = opts.MaxThreads or -1 maxThreads = opts.MaxThreads or -1
maxObjects = opts.MaxObjects or -1 maxObjects = opts.MaxObjects or -1
@@ -1207,7 +1311,7 @@ function multi:newProcessor(name, opts, priority)
end end
end end
if priority then if priority or multi.defaultSettings.priority then
handler = c:createPriorityHandler(c) handler = c:createPriorityHandler(c)
else else
handler = c:createHandler(c) handler = c:createHandler(c)
@@ -1280,27 +1384,27 @@ function multi:newProcessor(name, opts, priority)
function c:boost(count) function c:boost(count)
boost = count or 1 boost = count or 1
if boost > 1 then if boost > 1 then
self.run = function() self.run = function(dt)
if not Active then return end if not Active then return end
for i=1,boost do for i=1,boost do
c:uManager(true) c:uManager(dt)
handler() handler()
end end
return c return c
end end
else else
self.run = function() self.run = function(dt)
if not Active then return end if not Active then return end
c:uManager(true) c:uManager(dt)
handler() handler()
return c return c
end end
end end
end end
function c.run() function c.run(dt)
if not Active then return end if not Active then return end
c:uManager(true) c:uManager(dt)
handler() handler()
return c return c
end end
@@ -1309,6 +1413,10 @@ function multi:newProcessor(name, opts, priority)
return Active return Active
end end
function c:isPaused()
return not(Active)
end
function c.Start() function c.Start()
Active = true Active = true
return c return c
@@ -1321,8 +1429,10 @@ function multi:newProcessor(name, opts, priority)
function c:Destroy() function c:Destroy()
Active = false Active = false
if c.process then
c.process:Destroy() c.process:Destroy()
end end
end
function c:setTaskDelay(delay) function c:setTaskDelay(delay)
if type(delay) == "function" then if type(delay) == "function" then
@@ -1517,8 +1627,9 @@ function thread.skip(n)
return yield(CMD, t_skip, n or 1) return yield(CMD, t_skip, n or 1)
end end
function thread.kill() function thread.kill(msg)
multi.error("thread killed!") msg = msg or "thread killed!"
multi.error(msg)
end end
function thread.yield() function thread.yield()
@@ -1550,7 +1661,8 @@ function thread.get(name)
end end
function thread.waitFor(name) 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) return thread.get(name)
end end
@@ -1578,6 +1690,7 @@ function thread:newFunctionBase(generator, holdme, TYPE)
local tfunc = { local tfunc = {
GetCreationTimestamp = function() return multi.extract_uuid7_timestamp(UID).iso8601 end, GetCreationTimestamp = function() return multi.extract_uuid7_timestamp(UID).iso8601 end,
} }
tfunc.UPTIME = clock()
tfunc.Active = true tfunc.Active = true
function tfunc:Pause() function tfunc:Pause()
self.Active = false self.Active = false
@@ -1592,9 +1705,7 @@ function thread:newFunctionBase(generator, holdme, TYPE)
return nil, "Function is paused" return nil, "Function is paused"
end end
local rets, err local rets, err
local function wait() local check = function()
if thread.isThread() then
return thread.hold(function()
if err then if err then
return multi.NIL, err return multi.NIL, err
elseif rets then elseif rets then
@@ -1602,7 +1713,10 @@ function thread:newFunctionBase(generator, holdme, TYPE)
rets = nil 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]) 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) end
local function wait()
if thread.isThread() then
return thread.hold(check)
else else
while not rets and not err do while not rets and not err do
multi:uManager() 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 multi.OnLoad:Fire() -- This was done incase a threaded function was called before mainloop/uManager was called
if type(name) == "function" then if type(name) == "function" then
func = name func = name
name = "UnnamedThread_"..multi.randomString(16) name = "UnnamedThread_"..multi.randomString(4)
end end
local c={nil,nil,nil,nil,nil,nil,nil} local c={nil,nil,nil,nil,nil,nil,nil}
c.TempRets = {nil,nil,nil,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 return _G["$multi"].multi,_G["$multi"].thread
end end
function multi:uManager() function multi:uManager(dt)
if self.Active then if self.Active then
__CurrentProcess = self __CurrentProcess = self
multi.OnPreLoad:Fire() multi.OnPreLoad:Fire()
@@ -2335,7 +2449,7 @@ function multi:uManager()
end end
end end
function multi:uManagerRefP1() function multi:uManagerRefP1(dt)
if self.Active then if self.Active then
__CurrentProcess = self __CurrentProcess = self
local Loop=self.Mainloop local Loop=self.Mainloop
@@ -2343,7 +2457,7 @@ function multi:uManagerRefP1()
__CurrentTask = Loop[_D] __CurrentTask = Loop[_D]
for P=1,9 do for P=1,9 do
if PList[P]%__CurrentTask.Priority==0 then if PList[P]%__CurrentTask.Priority==0 then
__CurrentTask:Act() __CurrentTask:Act(dt)
__CurrentProcess = self __CurrentProcess = self
end end
end end
@@ -2351,13 +2465,13 @@ function multi:uManagerRefP1()
end end
end end
function multi:uManagerRef() function multi:uManagerRef(dt)
if self.Active then if self.Active then
__CurrentProcess = self __CurrentProcess = self
local Loop=self.Mainloop local Loop=self.Mainloop
for _D=#Loop,1,-1 do for _D=#Loop,1,-1 do
__CurrentTask = Loop[_D] __CurrentTask = Loop[_D]
__CurrentTask:Act() __CurrentTask:Act(dt)
__CurrentProcess = self __CurrentProcess = self
end end
end end
@@ -2389,7 +2503,7 @@ function table.merge(t1, t2)
return t1 return t1
end end
math.randomseed(os.time()) math.randomseed(math.floor(os.time()))
function multi:enableLoadDetection() function multi:enableLoadDetection()
if multi.maxSpd then return end if multi.maxSpd then return end
@@ -2397,7 +2511,7 @@ function multi:enableLoadDetection()
local temp = self:newProcessor() local temp = self:newProcessor()
local t = clock() local t = clock()
local stop = false local stop = false
temp:benchMark(.01):OnBench(function(time,steps) temp:benchMark(.1):OnBench(function(time,steps)
stop = steps stop = steps
end) end)
while not stop do while not stop do
@@ -2410,28 +2524,39 @@ end
local lastVal = 0 local lastVal = 0
local last_step = 0 local last_step = 0
function multi:getLoad() function multi:getLoad(loops)
if not multi.maxSpd then multi:enableLoadDetection() end local proc = proc or multi
if not proc.maxSpd then proc:enableLoadDetection() end
local val = nil local val = nil
local bench local bench
local bb 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 bench = steps
bb = steps bb = steps
avg = avg + steps
if steps > proc.maxSpd then
proc.maxSpd = steps
end
end) end)
_,timeout = multi.hold(function() _,timeout = multi.hold(function()
return bench return bench
end,{sleep=.012}) end,{sleep=1.1})
end
avg = avg/loops
if timeout or not bench then if timeout or not bench then
bench = 0 bench = 0
bb = 0 bb = 0
end end
bench = bench^1.5 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<0 then val = 0 end
if val > 100 then val = 100 end if val > 100 then val = 100 end
lastVal = val lastVal = val
last_step = bb*100 last_step = bb*100
print(proc.maxSpd, bench, bench/proc.maxSpd/2.2,val)
return val,last_step return val,last_step
end end
@@ -2533,7 +2658,7 @@ end
function multi.Round(num, numDecimalPlaces) function multi.Round(num, numDecimalPlaces)
local mult = 10 ^ (numDecimalPlaces or 0) local mult = 10 ^ (numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult return math.floor((num * mult) + 0.5 + 1e-10) / mult
end end
function multi.AlignTable(tab) function multi.AlignTable(tab)
@@ -2582,6 +2707,12 @@ end
function multi:reallocate(processor, index) function multi:reallocate(processor, index)
index=index or #processor.Mainloop+1 index=index or #processor.Mainloop+1
local int=self.Parent 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 self.Parent=processor
if index then if index then
table.insert(processor.Mainloop, index, self) table.insert(processor.Mainloop, index, self)
@@ -2679,11 +2810,8 @@ local function random_hex(len)
end end
return result return result
end end
math.randomseed(math.floor(os.time()))
multi.generate_uuid7 = function() multi.generate_uuid7 = function()
-- Seed random number generator with current time
math.randomseed(os.time() * os.clock() * 1000000)
-- Get timestamp in milliseconds -- Get timestamp in milliseconds
local timestamp_ms = get_timestamp_ms() local timestamp_ms = get_timestamp_ms()
+10 -6
View File
@@ -58,16 +58,20 @@ function multi:newSystemThreadedQueue(name)
GLOBAL[name] = c GLOBAL[name] = c
end end
local peek = function()
return c:peek()
end
local pop = function()
return c:pop()
end
function c:Hold(opt) function c:Hold(opt)
local multi, thread = require("multi"):init() local multi, thread = require("multi"):init()
if opt.peek then if opt.peek then
return thread.hold(function() return thread.hold(peek)
return self:peek()
end)
else else
return thread.hold(function() return thread.hold(pop)
return self:pop()
end)
end end
end end
-2
View File
@@ -23,9 +23,7 @@ SOFTWARE.
]] ]]
package.path = "?/init.lua;?.lua;" .. package.path package.path = "?/init.lua;?.lua;" .. package.path
local multi, thread = require("multi"):init() local multi, thread = require("multi"):init()
local pseudoProcessor = multi:newProcessor() local pseudoProcessor = multi:newProcessor()
if multi.integration then if multi.integration then
return { return {
init = function() init = function()
-39
View File
@@ -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
+244
View File
@@ -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
-10
View File
@@ -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
View File
@@ -1 +0,0 @@
../
+1095
View File
File diff suppressed because it is too large Load Diff
-195
View File
@@ -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 -1
View File
@@ -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 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} multi, thread = require("multi"):init{error=true,warning=true,print=true, priority=true}
proc = multi:newProcessor("Thread Test",true) proc = multi:newProcessor("Thread Test",true)
File diff suppressed because it is too large Load Diff