23 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
server f8265fbb3b Update changes.md (#75)
Fixed bad console output
2026-01-11 15:25:53 -08:00
server b995b1c0a7 Update changes.md
fixed hyperlink
2026-01-11 14:24:01 -08:00
server 66e922a971 fixing issue with timestamp using lua 5.1/luajit (#72)
* fixing issue with timestamp using lua 5.1/luajit

* fixed issues, testing

* bumped up version number

* updated docs
2026-01-11 14:18:43 -08:00
server 92659c7eaf Update README.md (#71) 2026-01-03 22:36:17 -08:00
98 changed files with 30509 additions and 28729 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
+18 -27
View File
@@ -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
with:
submodules: recursive
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-python@v2
with:
python-version: '3.10'
- name: Setup Lua
uses: leafo/gh-actions-lua@v10
with:
luaVersion: ${{ matrix.luaVersion }}
- name: Setup env
run: |
pip install hererocks
hererocks lua-pkg --${{ matrix.lua }} -rlatest
- name: Verify Lua version
run: lua -v
- 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
- name: Run unit tests
run: |
# Replace with your actual test file
lua tests/multi_test.lua
+5 -5
View File
@@ -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.1.0](https://github.com/rayaman/multi/tree/v16.1.0)
Progress is being made in [v16.3.0](https://github.com/rayaman/multi/tree/v16.3.0)
---
</br>
+39
View File
@@ -1,6 +1,8 @@
# 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>
[Update 16.0.0 - Connecting the dots](#update-1600---getting-the-priorities-straight)</br>
@@ -60,6 +62,43 @@ 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
---
- threadedfunctions, multiobjs and threads contain new method: `GetCreationTimestamp()` With will return a string formated date time using ISO 8601
Changed
---
- Internal uuid functions to not use bitwise operators
Fixed
---
- [Bitwise operations break lua 5.1/luajit](https://github.com/rayaman/multi/issues/73)
# Update 16.1.0 - The Flow State
Added
---
File diff suppressed because it is too large Load Diff
+513 -268
View File
File diff suppressed because it is too large Load Diff
+10 -6
View File
@@ -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
-2
View File
@@ -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()
+42
View File
@@ -0,0 +1,42 @@
package = "multi"
version = "16.2-0"
source = {
url = "git://github.com/rayaman/multi.git",
tag = "v16.2.0",
}
description = {
summary = "Lua Multi tasking library",
detailed = [[
This library contains many methods for multi tasking. Features non coroutine based multi-tasking, coroutine based multi-tasking, and system threading (Requires use of an integration).
Check github for documentation.
]],
homepage = "https://github.com/rayaman/multi",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
["multi"] = "init.lua",
["multi.integration.lanesManager"] = "integration/lanesManager/init.lua",
["multi.integration.lanesManager.extensions"] = "integration/lanesManager/extensions.lua",
["multi.integration.lanesManager.threads"] = "integration/lanesManager/threads.lua",
["multi.integration.loveManager"] = "integration/loveManager/init.lua",
["multi.integration.loveManager.extensions"] = "integration/loveManager/extensions.lua",
["multi.integration.loveManager.threads"] = "integration/loveManager/threads.lua",
["multi.integration.loveManager.utils"] = "integration/loveManager/threads.lua",
--["multi.integration.lovrManager"] = "integration/lovrManager/init.lua",
--["multi.integration.lovrManager.extensions"] = "integration/lovrManager/extensions.lua",
--["multi.integration.lovrManager.threads"] = "integration/lovrManager/threads.lua",
["multi.integration.pseudoManager"] = "integration/pseudoManager/init.lua",
["multi.integration.pseudoManager.extensions"] = "integration/pseudoManager/extensions.lua",
["multi.integration.pseudoManager.threads"] = "integration/pseudoManager/threads.lua",
["multi.integration.luvitManager"] = "integration/luvitManager.lua",
["multi.integration.threading"] = "integration/threading.lua",
["multi.integration.sharedExtensions"] = "integration/sharedExtensions/init.lua",
["multi.integration.priorityManager"] = "integration/priorityManager/init.lua",
--["multi.integration.networkManager"] = "integration/networkManager.lua",
}
}
-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
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