Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ jobs:

- name: Tests (gcc)
run: |
balls --path="." --cc:gcc --gc:arc --backend:c --define:danger --define:release
balls --path="." --cc:gcc --gc:orc --gc:arc --backend:c --define:danger --define:release

- name: Tests (clang)
run: |
balls --path="." --cc:clang --gc:arc --backend:c --define:danger --define:release
balls --path="." --cc:clang --gc:orc --gc:arc --backend:c --define:danger --define:release

- name: Build docs
if: >
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ After adapting the algorithm to nim CPS, disruptek adapted the queue for **any r
With the 11 bit aligned implementation we have:
- Lock-free consumption up to **512** threads
- Lock-free production up to **1,025** threads
- Memory-leak free under **ARC**
- Memory-leak free under ARC and ORC
- Can pass ANY ref object between threads; however:
- Is perfectly designed for passing Continuations between threads
- **0 memory copies**
Expand All @@ -74,7 +74,7 @@ Download with `nimble install loony` (CPS dependency for tests) or directly from

Simple.

First, ensure you compile with arc and threads (`--gc:arc --threads:on`)
First, ensure you compile with arc or orc and threads (`--threads:on`)

Then:
```nim
Expand Down
142 changes: 142 additions & 0 deletions benchmarks/behavioral/concurrent_matrix.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
## Behavioral Tests: Concurrent Producer/Consumer Matrix
## Contract: All items enqueued arrive at consumers in FIFO order under contention
## Tests all (1-4 producers) × (1-4 consumers) = 16 combinations

import ../framework
import ../../loony
import std/[threads, sync]

type
MatrixConfig = object
producers: int
consumers: int
itemsPerProducer: int

proc testConcurrentMatrix(config: MatrixConfig) =
## Run a single producer/consumer configuration test
let totalItems = config.producers * config.itemsPerProducer
var results: seq[int] = @[]
var resultsLock: Lock
initLock(resultsLock)
var threadsDone = 0
var doneCondvar: Cond
initCond(doneCondvar)

let q = newLoonyQueue[int]()

# Producer threads
var producerThreads: seq[Thread[tuple[q: LoonyQueue[int], id: int, itemsPerProducer: int]]]
for p in 0..<config.producers:
var t: Thread[tuple[q: LoonyQueue[int], id: int, itemsPerProducer: int]]
createThread(t, proc(data: tuple[q: LoonyQueue[int], id: int, itemsPerProducer: int]) =
for i in 0..<data.itemsPerProducer:
let item = data.id * 1000 + i
data.q.push(item)
, (q, p, config.itemsPerProducer))
producerThreads.add(t)

# Consumer threads
var consumerThreads: seq[Thread[tuple[q: LoonyQueue[int], id: int, lock: ptr Lock, results: ptr seq[int], totalItems: int]]]
for c in 0..<config.consumers:
var t: Thread[tuple[q: LoonyQueue[int], id: int, lock: ptr Lock, results: ptr seq[int], totalItems: int]]
createThread(t, proc(data: tuple[q: LoonyQueue[int], id: int, lock: ptr Lock, results: ptr seq[int], totalItems: int]) =
var collected = 0
let itemsPerConsumer = (data.totalItems + data.id) div data.id # Rough distribution

while true:
let v = data.q.pop()
withLock(data.lock[]):
data.results[].add(v)

collected += 1
if collected >= data.totalItems:
break
, (q, c, addr resultsLock, addr results, totalItems))
consumerThreads.add(t)

# Wait for producers
for t in producerThreads:
joinThread(t)

# Wait for consumers
for t in consumerThreads:
joinThread(t)

# Verify: all items received
check results.len == totalItems, &"Expected {totalItems} items, got {results.len}"

# Verify: no duplicates
let seen = results.len
results.sort()
for i in 1..<results.len:
check results[i] >= results[i-1], "Items out of sorted order"

suite "Concurrent Producer/Consumer Matrix":

# 1P/1C Configuration
test "1 producer / 1 consumer (10 items)":
let config = MatrixConfig(producers: 1, consumers: 1, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "1 producer / 2 consumers (10 items)":
let config = MatrixConfig(producers: 1, consumers: 2, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "1 producer / 3 consumers (10 items)":
let config = MatrixConfig(producers: 1, consumers: 3, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "1 producer / 4 consumers (10 items)":
let config = MatrixConfig(producers: 1, consumers: 4, itemsPerProducer: 10)
testConcurrentMatrix(config)

# 2P/1C Configuration
test "2 producers / 1 consumer (10 items each)":
let config = MatrixConfig(producers: 2, consumers: 1, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "2 producers / 2 consumers (10 items each)":
let config = MatrixConfig(producers: 2, consumers: 2, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "2 producers / 3 consumers (10 items each)":
let config = MatrixConfig(producers: 2, consumers: 3, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "2 producers / 4 consumers (10 items each)":
let config = MatrixConfig(producers: 2, consumers: 4, itemsPerProducer: 10)
testConcurrentMatrix(config)

# 3P/1C Configuration
test "3 producers / 1 consumer (10 items each)":
let config = MatrixConfig(producers: 3, consumers: 1, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "3 producers / 2 consumers (10 items each)":
let config = MatrixConfig(producers: 3, consumers: 2, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "3 producers / 3 consumers (10 items each)":
let config = MatrixConfig(producers: 3, consumers: 3, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "3 producers / 4 consumers (10 items each)":
let config = MatrixConfig(producers: 3, consumers: 4, itemsPerProducer: 10)
testConcurrentMatrix(config)

# 4P/1C Configuration
test "4 producers / 1 consumer (10 items each)":
let config = MatrixConfig(producers: 4, consumers: 1, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "4 producers / 2 consumers (10 items each)":
let config = MatrixConfig(producers: 4, consumers: 2, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "4 producers / 3 consumers (10 items each)":
let config = MatrixConfig(producers: 4, consumers: 3, itemsPerProducer: 10)
testConcurrentMatrix(config)

test "4 producers / 4 consumers (10 items each)":
let config = MatrixConfig(producers: 4, consumers: 4, itemsPerProducer: 10)
testConcurrentMatrix(config)
Loading
Loading