LinuxSharedTopic Design
For the basic API, see the "Shared-Memory Topic (Linux)" page in the basic message-system section.
This page focuses on the boundary between LinuxSharedTopic<T> and ordinary Topic, and on the
tradeoffs behind the current implementation.
1. Why it is not folded back into Topic
LinuxSharedTopic<T> solves Linux / Webots inter-process communication, large-payload sharing,
zero-copy reads, and per-subscriber queue policy. The original Topic is closer to in-process
publish-subscribe with MCU-oriented semantics: cache, callbacks, synchronous or asynchronous
subscribers, and lightweight queues. Those two paths operate under different constraints, so shared-
memory semantics were not pushed back into Topic. Keeping them separate lets Topic stay light
while Linux IPC evolves along its own model.
2. Separating data plane and control plane
LinuxSharedTopic<T> is not built around one simple queue. It uses two layers:
- payload slot
- the real data lives in shared-memory slots
- descriptor queue
- publish only pushes "which slot is readable" to each subscriber
The direct consequences are:
- the payload itself is not copied again between publisher and subscriber
- each subscriber only consumes descriptors
- the same slot can be held by multiple subscribers until the last reference is released
That is the basis for zero-copy behavior.
3. Why the hot path uses atomic + futex
The key constraints of the implementation are:
- no mutex on the hot path
- publish and consume side should advance state mostly with atomics
- only the waiting path should sleep with futex
So the target is not "absolutely no waiting". It is "take mutex out of the publish/consume hot path and compress real waiting into futex sleep".
4. Why slot reclaim is refcount-based instead of overwrite
One of the most dangerous problems in a shared-memory queue is:
- publisher wants to keep writing
- but a subscriber is still reading the old payload
The chosen model is:
- refcounted slot reclamation
- backpressure on publisher when slots are exhausted
instead of:
- overwrite while in use
The cost is:
- under high pressure, publisher may fail because slots are exhausted
The upside is:
- payload will not be overwritten while a subscriber still holds it
That is an explicit safety-first boundary.
5. What the three subscription policies trade off
The subscription modes are:
BROADCAST_FULLBROADCAST_DROP_OLDBALANCE_RR
They are not merely "different features". They optimize for different goals.
BROADCAST_FULL
Goal:
- preserve every published item
Cost:
- one slow subscriber with a full queue reduces overall publish success
BROADCAST_DROP_OLD
Goal:
- preserve publisher throughput as much as possible
- let slow subscribers bias toward newer data
Cost:
- old samples are dropped
BALANCE_RR
Goal:
- distribute load across multiple workers
Cost:
- the same message is not broadcast to every worker
So it is really a shared-load mode, not a broadcast mode.
6. The practical difference between FULL and DROP_OLD
This is not only a conceptual distinction. Under a slow-subscriber overload:
- with
FULL, the slow subscriber's full queue directly turns into publisher backpressure - with
DROP_OLD, the slow subscriber loses history but keeps tracking newer data
The choice is really about whether the system values complete delivery more than freshness and throughput.
7. Positioning
LinuxSharedTopic<T> is the right tool when the requirement is inter-process communication, large
shared payloads, and zero-copy read-side behavior. Ordinary Topic remains the in-process,
lightweight publish-subscribe path. The two are related by topic semantics, but they are not the
same transport model.