Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::runtime::Handoff< T, RetireCapacity > Class Template Reference

Lock-free transfer of ownership to a reader that must never block, plus a ring for handing the displaced value back so the audio thread never frees. More...

#include <slot.hpp>

Public Member Functions

 Handoff ()=default
 Handoff (const Handoff &)=delete
Handoff & operator= (const Handoff &)=delete
 ~Handoff ()
 Reclaims anything still parked. Both threads must have stopped.
bool publish (std::unique_ptr< T > next)
 Producer thread.
std::unique_ptr< T > try_consume () noexcept
 Audio-thread-callable.
bool retire (std::unique_ptr< T > &displaced) noexcept
 Audio-thread-callable.
bool has_retire_capacity (std::size_t n=1) const noexcept
 Room in the retire ring for n more values.
std::size_t drain_retired ()
 Producer / worker thread. Free every parked value. Returns the count.
std::unique_ptr< T > drain_retired_one ()
 Producer / worker thread.
bool has_pending () const noexcept
bool has_retired () const noexcept

Static Public Member Functions

static constexpr std::size_t retire_capacity () noexcept

Detailed Description

template<typename T, std::size_t RetireCapacity = 8>
class pulp::runtime::Handoff< T, RetireCapacity >

Lock-free transfer of ownership to a reader that must never block, plus a ring for handing the displaced value back so the audio thread never frees.

This is the pattern Timur Doumler and Ross Bencina describe as standard practice for real-time audio: build the object off-thread, swap a pointer, return the old one through a lock-free FIFO for a non-RT thread to destroy.

// Worker thread:
swapper.publish(build_ir(...));
// Audio thread, at a block boundary:
if (swapper.has_pending() && swapper.has_retire_capacity()) {
auto next = swapper.try_consume();
if (next) {
auto displaced = std::exchange(current_, std::move(next));
(void)swapper.retire(displaced); // capacity reserved above
}
}
// Worker thread, periodically:
swapper.drain_retired();
bool retire(std::unique_ptr< T > &displaced) noexcept
Audio-thread-callable.
Definition slot.hpp:297
bool has_retire_capacity(std::size_t n=1) const noexcept
Room in the retire ring for n more values.
Definition slot.hpp:307
bool has_pending() const noexcept
Definition slot.hpp:325
std::size_t drain_retired()
Producer / worker thread. Free every parked value. Returns the count.
Definition slot.hpp:312
bool publish(std::unique_ptr< T > next)
Producer thread.
Definition slot.hpp:271
std::unique_ptr< T > try_consume() noexcept
Audio-thread-callable.
Definition slot.hpp:284
Warning
One producer thread, one consumer (audio) thread.

Constructor & Destructor Documentation

◆ Handoff() [1/2]

template<typename T, std::size_t RetireCapacity = 8>
pulp::runtime::Handoff< T, RetireCapacity >::Handoff ( )
default

◆ Handoff() [2/2]

template<typename T, std::size_t RetireCapacity = 8>
pulp::runtime::Handoff< T, RetireCapacity >::Handoff ( const Handoff< T, RetireCapacity > & )
delete

◆ ~Handoff()

template<typename T, std::size_t RetireCapacity = 8>
pulp::runtime::Handoff< T, RetireCapacity >::~Handoff ( )
inline

Reclaims anything still parked. Both threads must have stopped.

Member Function Documentation

◆ drain_retired()

template<typename T, std::size_t RetireCapacity = 8>
std::size_t pulp::runtime::Handoff< T, RetireCapacity >::drain_retired ( )
inline

Producer / worker thread. Free every parked value. Returns the count.

◆ drain_retired_one()

template<typename T, std::size_t RetireCapacity = 8>
std::unique_ptr< T > pulp::runtime::Handoff< T, RetireCapacity >::drain_retired_one ( )
inline

Producer / worker thread.

Pop one parked value, for callers that want to inspect it before it dies. Returns nullptr when none are queued.

◆ has_pending()

template<typename T, std::size_t RetireCapacity = 8>
bool pulp::runtime::Handoff< T, RetireCapacity >::has_pending ( ) const
inlinenoexcept

◆ has_retire_capacity()

template<typename T, std::size_t RetireCapacity = 8>
bool pulp::runtime::Handoff< T, RetireCapacity >::has_retire_capacity ( std::size_t n = 1) const
inlinenoexcept

Room in the retire ring for n more values.

Reserve before committing to a swap so a retire never fails inline and strands a value on the audio thread.

◆ has_retired()

template<typename T, std::size_t RetireCapacity = 8>
bool pulp::runtime::Handoff< T, RetireCapacity >::has_retired ( ) const
inlinenoexcept

◆ operator=()

template<typename T, std::size_t RetireCapacity = 8>
Handoff & pulp::runtime::Handoff< T, RetireCapacity >::operator= ( const Handoff< T, RetireCapacity > & )
delete

◆ publish()

template<typename T, std::size_t RetireCapacity = 8>
bool pulp::runtime::Handoff< T, RetireCapacity >::publish ( std::unique_ptr< T > next)
inline

Producer thread.

Publish a value for the audio thread to claim. If a previously published value has not been consumed yet, it is replaced and freed here — on the producer thread, never the audio thread.

Returns false only for a null argument.

◆ retire()

template<typename T, std::size_t RetireCapacity = 8>
bool pulp::runtime::Handoff< T, RetireCapacity >::retire ( std::unique_ptr< T > & displaced)
inlinenodiscardnoexcept

Audio-thread-callable.

Park the value the audio thread is displacing so a non-RT thread can free it.

Returns false when the retire ring is full; on false the caller still owns displaced (the unique_ptr is left intact). Gate on has_retire_capacity() before consuming so this never happens in well-paced operation.

RT contract: never allocates, never blocks, never frees.

◆ retire_capacity()

template<typename T, std::size_t RetireCapacity = 8>
constexpr std::size_t pulp::runtime::Handoff< T, RetireCapacity >::retire_capacity ( )
inlinestaticconstexprnoexcept

◆ try_consume()

template<typename T, std::size_t RetireCapacity = 8>
std::unique_ptr< T > pulp::runtime::Handoff< T, RetireCapacity >::try_consume ( )
inlinenodiscardnoexcept

Audio-thread-callable.

Atomically claim the most recently published value, or nullptr if there is none.

RT contract: never allocates, never blocks, never frees.


The documentation for this class was generated from the following file:
  • /home/runner/work/pulp/pulp/core/runtime/include/pulp/runtime/slot.hpp