Skip to content

unevens/lockfree-async

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lockfree-async

lockfree-async is a C++ header-only simple template library for lock-free inter-thread communication and sharing of resources.

The fundamental building block of this library is the MpmcPopAllLifoStack, a multiple-producer multiple-consumer LIFO stack implementing the "IBM Freelist" (Treiber stack) algorithm with push and pop_all operations only. The absence of a single-node pop makes it immune to the ABA problem. The library originally relied on Ross Bencina's Queue World for this primitive; it now ships its own minimal modern-C++ implementation.

MpmcPopAllLifoStack.hpp

The template class MpmcPopAllLifoStack<Node> is a lock-free MPMC LIFO stack. The Node type must expose a Node* next member.

Messenger.hpp

The template class Messenger<T> is a wrapper around MpmcPopAllLifoStack. It implements functionality to send and receive data of type T between threads in a lock-free way, and to preallocate the resources to do so.

RealtimeObject.hpp

The template class RealtimeObject<T> owns an object of class T which can be shared between a non realtime thread and a realtime thread, so that the non realtime thread can perform any blocking operation, such as creating the object, and the realtime thread can use the object.

AsyncObject.hpp

The template class AsyncObject manages asynchronous creation, modification, and destruction of several instances of an object, that can be used from realtime threads, while being created, modified and destroyed by a timer thread.

API changes

Commit 3695325 (the doctest suite + audit-driven fixes + QueueWorld removal pass) made the following backwards-incompatible API changes. Code written against the previous revision will need the migrations described below.

RealtimeObject: realtime-side getters now return Object const*

getOnRealtimeThread() and receiveChangesOnRealtimeThread() previously returned Object*. They now return Object const*, enforcing through the type system the long-standing rule that the realtime thread must not mutate the shared Object (mutation would race with the next non-realtime-side change() call, which reads through Object const&). getOnRealtimeThread() is also const-qualified.

Migration:

  • If you only read the realtime-side object — no change needed.
  • If you previously mutated it — restructure so any mutable, RT-only state lives next to the RT code (a member of whatever owns the RealtimeObject), not inside the shared Object. The shared Object should be treated as immutable from the RT thread's perspective.

MessageNode<T> simplified

MessageNode<T> no longer exposes the QueueWorld-style link array. The following members are gone:

  • enum { LINK_INDEX_1, PREV_LINK, LINK_COUNT }
  • MessageNode* links_[2]
  • MessageNode*& next() accessor method
  • MessageNode*& prev() accessor method
  • MessageNode* last() (was O(n))
  • int count() (was O(n) and the source of a previous null-deref UB)

They are replaced by:

  • MessageNode* next direct member (replaces links_[0] / next())
  • MessageNode* prev direct member (replaces links_[1] / prev())

Migration: rewrite node->next() as node->next, node->prev() as node->prev. If you depended on last() or count(), walk the chain inline (each is a 3-line loop) or compute the tail alongside the consumption walk (e.g. via the new value returned by handleMessageStack, see below).

QueueWorld dependency removed

The lockfree/QueueWorld/ directory (and LICENSE-QUEUE-WORLD) have been deleted. Code that included the QueueWorld headers directly will no longer compile:

  • #include "lockfree/QueueWorld/QwMpmcPopAllLifoStack.h" — gone
  • QwMpmcPopAllLifoStack<NodePtrT, LINK_INDEX> — gone
  • QwLinkTraits, QwConfig — gone

Migration: use lockfree::MpmcPopAllLifoStack<Node> from the new lockfree/MpmcPopAllLifoStack.hpp. The new stack operates on a plain Node type that exposes a Node* next member (no link array, no link-index template parameter, no trait specialisation). The algorithm and memory orderings are unchanged.

LifoStack<T> alias retargeted

The lockfree::LifoStack<T> alias still exists and still exposes push, push_multiple, pop_all, empty with the same semantics, but it now resolves to MpmcPopAllLifoStack<MessageNode<T>> instead of the QueueWorld template. Code that used the alias through its documented operations is unaffected; code that relied on the underlying type or template parameters needs updating.

handleMessageStack returns the chain tail

The free function lockfree::handleMessageStack used to return void. It now returns MessageNode<T>* — the tail of the chain (the original LIFO bottom, i.e. the first-sent node), or nullptr if head was nullptr. Returning the tail lets callers recycle the consumed chain via the new O(1) Messenger::recycle(head, tail) overload without re-walking it.

Migration: callers that ignored the return value are source-compatible (the result simply gets discarded). Callers that used the function in a context requiring a void return (e.g. using F = decltype(...) aliases or strict signature matching for higher-order code) will need to update their types.

About

A simple C++17 header-only template library for lock-free inter-thread communication.

Topics

Resources

License

Stars

7 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors