Motion Master 6.0.0-alpha.86
Next-generation motion control software
Loading...
Searching...
No Matches
mm::node::ScalarCell Struct Reference

One scalar value, and the only part of a DeviceParameter that two threads touch at once. More...

#include <device_parameter.h>

Public Member Functions

 ScalarCell ()=default
 
 ~ScalarCell ()=default
 
 ScalarCell (const ScalarCell &other)
 
 ScalarCell (ScalarCell &&other) noexcept
 
ScalarCelloperator= (const ScalarCell &other)
 
ScalarCelloperator= (ScalarCell &&other) noexcept
 
uint64_t load () const
 Reads the value. Lock-free, non-allocating — safe from the RT loop.
 
void store (uint64_t v) const
 Writes the value. Lock-free, non-allocating — safe from the RT loop.
 

Public Attributes

uint64_t value {0}
 

Detailed Description

One scalar value, and the only part of a DeviceParameter that two threads touch at once.

The value is a plain uint64_t reached through std::atomic_ref, so the lock-free guarantee sits on the access rather than on the storage. That is what keeps DeviceParameter copyable and movable — a std::atomic member would be neither, and the struct must be both, because Device::parameter and parametersOrdered hand out copies.

The copy has to be atomic too, and this type is why. A bare member would be read by the compiler-generated copy constructor as an ordinary load, racing the RT thread's store — undefined behaviour that ThreadSanitizer reports on every Device::parameter call, and that the concurrency tests found the first time they ran. Wrapping the value fixes it without writing DeviceParameter's five special members by hand: a field added to that struct later cannot break this, because the compiler still generates its copy and this type still does the atomic access. Hand-written special members are the hazard the wrapper avoids — a field added and forgotten in one of them loses data silently.

Relaxed ordering throughout, and that is the whole requirement: the value is self-contained with no companion state to order against, and a reader is by definition unsynchronised with the writer that published it. No torn or invented value, and the last store becomes visible — which is exactly what a cyclic task needs.

mutable because a load is a const operation while std::atomic_ref needs a non-const lvalue, and because an output cell is written through a const DeviceParameter*.

Constructor & Destructor Documentation

◆ ScalarCell() [1/3]

mm::node::ScalarCell::ScalarCell ( )
default

◆ ~ScalarCell()

mm::node::ScalarCell::~ScalarCell ( )
default

◆ ScalarCell() [2/3]

mm::node::ScalarCell::ScalarCell ( const ScalarCell other)
inline

◆ ScalarCell() [3/3]

mm::node::ScalarCell::ScalarCell ( ScalarCell &&  other)
inlinenoexcept

Member Function Documentation

◆ load()

uint64_t mm::node::ScalarCell::load ( ) const
inline

Reads the value. Lock-free, non-allocating — safe from the RT loop.

◆ operator=() [1/2]

ScalarCell & mm::node::ScalarCell::operator= ( const ScalarCell other)
inline

◆ operator=() [2/2]

ScalarCell & mm::node::ScalarCell::operator= ( ScalarCell &&  other)
inlinenoexcept

◆ store()

void mm::node::ScalarCell::store ( uint64_t  v) const
inline

Writes the value. Lock-free, non-allocating — safe from the RT loop.

Member Data Documentation

◆ value

uint64_t mm::node::ScalarCell::value {0}
mutable

The documentation for this struct was generated from the following file: