[!WARNING]
Project is deprecated in favor of chute.Chute is a continuation of this project, featuring truly lock-free MPMC writers that are superlinearly faster in highly concurrent scenarios.
Reader counted event queue
Fast, concurrent FIFO event queue (or message queue). Multiple consumers receive every message.
- mpmc (multi-producer multi-consumer) - lock-free read, locked write.
- spmc (single-producer multi-consumer) - lock-free read, lock-free write.
Write operations never block read operations. Performance consumer oriented. Mostly contiguous memory layout. Memory consumption does not grow with readers number.
Performance
Have VERY low CPU + memory overhead. Most of the time reader just do 1 atomic load per iter()
call. That's all!
Single-threaded.
Read - close to VecDeque
! Write:
mpmc
-push
2x slower thenVecDeque
.extend
with at least 4 items, close toVecDeque
.spmc
- equal toVecDeque
!
Multi-threaded.
Read - per thread performance degrades slowly, with each additional simultaneously reading thread.
(Also remember, since rc_event_queue
is message queue, and each reader read ALL queue -
adding more readers does not consume queue faster)
Write - per thread performance degrades almost linearly, with each additional simultaneously writing thread.
(Due to being locked). Not applicable to spmc
.
N.B. But if there is no heavy contention - performance very close to single-threaded case.
Principle of operation
See doc/principle-of-operation.md.
Short version - EventQueue
operates on the chunk basis. EventQueue
does not touch EventReader
s . EventReader
s always
"pull" from EventQueue
. The only way EventReader
interact with EventQueue
- by increasing read counter
when switching to next chunk during traverse.
Usage
use *;
use ;
let event = new;
let mut reader1 = new;
let mut reader2 = new;
event.push;
event.push;
event.push;
event.push;
assert!;
assert!;
assert!;
assert!;
event.extend;
assert!;
assert!;
clear:
event.push;
event.push;
event.clear;
event.push;
event.push;
assert!;
assert!;
clear
/truncate_front
have peculiarities - chunks occupied by readers, will not be freed immediately.
Emergency cut
If any of the readers did not read for a long time - it can retain queue from cleanup.
This means that queue capacity will grow. On long runs with unpredictable systems, you may want to periodically check total_capacity
,
and if it grows too much - you may want to force-cut/clear it.
if event.total_capacity > 100000
Even if some reader will stop read forever - you'll only lose/leak chunk directly occupied by reader.
Optimisation
CLEANUP
Set CLEANUP
to Never
in Settings
, in order to postpone chunks deallocations.
use ;
let event = new;
let mut reader = event.subscribe;
event.extend;
sum; // With CLEANUP != Never, this would cause chunk deallocation
...
event.cleanup; // Free used chunks
double_buffering
Use double_buffering
feature. This will reuse biggest freed chunk. When EventQueue
reach its optimal size - chunks will be just swapped,
without alloc/dealloc.
Soundness
EventQueue
covered with tests. Miri tests. Loom tests. See doc/tests.md