density
C++11 library for paged memory management, function queues, heterogeneous queues and lifo memory management
Public Types | Public Member Functions | Static Public Attributes | Friends | List of all members
lf_function_queue< CALLABLE, ALLOCATOR_TYPE, ERASURE, PROD_CARDINALITY, CONSUMER_CARDINALITY, CONSISTENCY_MODEL > Class Template Reference

#include <lf_function_queue.h>

Public Types

template<typename ELEMENT_COMPLETE_TYPE >
using put_transaction = typename UnderlyingQueue::template put_transaction< ELEMENT_COMPLETE_TYPE >
 
template<typename ELEMENT_COMPLETE_TYPE >
using reentrant_put_transaction = typename UnderlyingQueue::template reentrant_put_transaction< ELEMENT_COMPLETE_TYPE >
 
using consume_operation = typename UnderlyingQueue::consume_operation
 
using reentrant_consume_operation = typename UnderlyingQueue::reentrant_consume_operation
 

Public Member Functions

 lf_function_queue () noexcept=default
 
 lf_function_queue (lf_function_queue &&i_source) noexcept=default
 
lf_function_queueoperator= (lf_function_queue &&i_source) noexcept=default
 
 ~lf_function_queue ()
 
template<typename ELEMENT_COMPLETE_TYPE >
void push (ELEMENT_COMPLETE_TYPE &&i_source)
 
template<typename ELEMENT_COMPLETE_TYPE , typename... CONSTRUCTION_PARAMS>
void emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
template<typename ELEMENT_TYPE >
put_transaction< typename std::decay< ELEMENT_TYPE >::type > start_push (ELEMENT_TYPE &&i_source)
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
put_transaction< ELEMENT_TYPE > start_emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
template<typename ELEMENT_COMPLETE_TYPE >
void reentrant_push (ELEMENT_COMPLETE_TYPE &&i_source)
 
template<typename ELEMENT_COMPLETE_TYPE , typename... CONSTRUCTION_PARAMS>
void reentrant_emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
template<typename ELEMENT_TYPE >
reentrant_put_transaction< typename std::decay< ELEMENT_TYPE >::type > start_reentrant_push (ELEMENT_TYPE &&i_source)
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
reentrant_put_transaction< ELEMENT_TYPE > start_reentrant_emplace (CONSTRUCTION_PARAMS &&...i_construction_params)
 
template<typename ELEMENT_COMPLETE_TYPE >
bool try_push (progress_guarantee i_progress_guarantee, ELEMENT_COMPLETE_TYPE &&i_source) noexcept(noexcept(m_queue .try_push( i_progress_guarantee, std::forward< ELEMENT_COMPLETE_TYPE >(i_source))))
 
template<typename ELEMENT_COMPLETE_TYPE , typename... CONSTRUCTION_PARAMS>
bool try_emplace (progress_guarantee i_progress_guarantee, CONSTRUCTION_PARAMS &&...i_construction_params) noexcept( noexcept(m_queue.template try_emplace< ELEMENT_COMPLETE_TYPE >( i_progress_guarantee, std::forward< CONSTRUCTION_PARAMS >(i_construction_params)...)))
 
template<typename ELEMENT_TYPE >
put_transaction< typename std::decay< ELEMENT_TYPE >::type > try_start_push (progress_guarantee i_progress_guarantee, ELEMENT_TYPE &&i_source) noexcept(noexcept(m_queue .try_start_push( i_progress_guarantee, std::forward< ELEMENT_TYPE >(i_source))))
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
put_transaction< ELEMENT_TYPE > try_start_emplace (progress_guarantee i_progress_guarantee, CONSTRUCTION_PARAMS &&...i_construction_params) noexcept(noexcept(m_queue .template try_start_emplace< ELEMENT_TYPE >( i_progress_guarantee, std::forward< ELEMENT_TYPE >( i_construction_params)...)))
 
template<typename ELEMENT_COMPLETE_TYPE >
bool try_reentrant_push (progress_guarantee i_progress_guarantee, ELEMENT_COMPLETE_TYPE &&i_source) noexcept(noexcept(m_queue .try_reentrant_push( i_progress_guarantee, std::forward< ELEMENT_COMPLETE_TYPE >(i_source))))
 
template<typename ELEMENT_COMPLETE_TYPE , typename... CONSTRUCTION_PARAMS>
bool try_reentrant_emplace (progress_guarantee i_progress_guarantee, CONSTRUCTION_PARAMS &&...i_construction_params) noexcept(noexcept(m_queue .template try_reentrant_emplace< ELEMENT_COMPLETE_TYPE >( i_progress_guarantee, std::forward< CONSTRUCTION_PARAMS >( i_construction_params)...)))
 
template<typename ELEMENT_TYPE >
reentrant_put_transaction< typename std::decay< ELEMENT_TYPE >::type > try_start_reentrant_push (progress_guarantee i_progress_guarantee, ELEMENT_TYPE &&i_source) noexcept(noexcept(m_queue .try_start_reentrant_push( i_progress_guarantee, std::forward< ELEMENT_TYPE >(i_source))))
 
template<typename ELEMENT_TYPE , typename... CONSTRUCTION_PARAMS>
reentrant_put_transaction< ELEMENT_TYPE > try_start_reentrant_emplace (progress_guarantee i_progress_guarantee, CONSTRUCTION_PARAMS &&...i_construction_params) noexcept(noexcept(m_queue .template try_start_reentrant_emplace< ELEMENT_TYPE >( i_progress_guarantee, std::forward< ELEMENT_TYPE >( i_construction_params)...)))
 
std::conditional< std::is_void< RET_VAL >::value, bool, optional< RET_VAL > >::type try_consume (PARAMS...i_params)
 
std::conditional< std::is_void< RET_VAL >::value, bool, optional< RET_VAL > >::type try_consume (consume_operation &i_consume, PARAMS...i_params)
 
std::conditional< std::is_void< RET_VAL >::value, bool, optional< RET_VAL > >::type try_reentrant_consume (PARAMS...i_params)
 
std::conditional< std::is_void< RET_VAL >::value, bool, optional< RET_VAL > >::type try_reentrant_consume (reentrant_consume_operation &i_consume, PARAMS...i_params)
 
template<function_type_erasure ERASURE_ = ERASURE, typename std::enable_if< ERASURE_!=function_manual_clear >::type * = nullptr>
void clear () noexcept
 
bool empty () noexcept
 

Static Public Attributes

static constexpr bool concurrent_puts = PROD_CARDINALITY == concurrency_multiple
 
static constexpr bool concurrent_consumes = CONSUMER_CARDINALITY == concurrency_multiple
 
static constexpr bool concurrent_put_consumes = true
 
static constexpr bool is_seq_cst = CONSISTENCY_MODEL == consistency_sequential
 

Friends

void swap (lf_function_queue &i_first, lf_function_queue &i_second) noexcept
 

Detailed Description

template<typename CALLABLE, typename ALLOCATOR_TYPE = default_allocator, function_type_erasure ERASURE = function_standard_erasure, concurrency_cardinality PROD_CARDINALITY = concurrency_multiple, concurrency_cardinality CONSUMER_CARDINALITY = concurrency_multiple, consistency_model CONSISTENCY_MODEL = consistency_sequential>
class density::lf_function_queue< CALLABLE, ALLOCATOR_TYPE, ERASURE, PROD_CARDINALITY, CONSUMER_CARDINALITY, CONSISTENCY_MODEL >

Heterogeneous FIFO pseudo-container specialized to hold callable objects. lf_function_queue is an adaptor for lf_heter_queue.

Template Parameters
CALLABLESignature required to the callable objects. Must be in the form RET_VAL (PARAMS...)
ALLOCATOR_TYPEAllocator type to be used. This type must satisfy the requirements of both UntypedAllocator and PagedAllocator. The default is density::default_allocator.
ERASUREType erasure to use the callable objects. Must be a member of density::function_type_erasure.
PROD_CARDINALITYspecifies whether multiple threads can do put transactions concurrently. Must be a member of density::concurrency_cardinality.
CONSUMER_CARDINALITYspecifies whether multiple threads can do consume operations concurrently. Must be a member of density::concurrency_cardinality.
CONSISTENCY_MODELSpecifies whether the queue is linearizable. Must be a member of density::consistency_model. Implementation note: Currently this parameter affects only put operations, and only in case of multiple producers: in case of relaxed consistency model, for a small amount of time, during the first phase of a put transaction, the queue is truncated, so any thread can successfully put further elements, but those elements are not observable to any thread, even the one who did the put.

If ERASURE == function_manual_clear, lf_function_queue is not able to destroy the callable objects without invoking them. This produces a performance benefit, but:


Thread safeness: A thread doing put operations and another thread doing consumes don't need to be synchronized. If PROD_CARDINALITY is concurrency_multiple, multiple threads are allowed to put without any synchronization. If CONSUMER_CARDINALITY is concurrency_multiple, multiple threads are allowed to consume without any synchronization.
Exception safeness: Any function of lf_function_queue is noexcept or provides the strong exception guarantee.

This class template uses lock-free algorithms for both put operations and consume operations. Anyway, for the overall put or consume to be lock-free, if a memory operation is necessary, it must be lock-free too. The default allocator, density::default_allocator, can manage pages in lock freedom within its current capacity (i.e. the memory it has managed until now). This capacity is composed by all the allocated, pinned, thread-owned and free pages. If the capacity must exceed its previous peek, and all the memory regions are exhausted, default_allocator must request a new memory region to the system. In this case it can't guarantee lock-freedom. The static functions default_allocator::reserve_lockfree_page_memory and default_allocator::try_reserve_lockfree_page_memory can be used to reserve a capacity in advance.
default_allocator delegates legacy memory operations to the system. Since the storage of elements whose size exceeds a fixed limit can't be allocated in a page, they require a legacy memory allocation, and in this case the put can't be lock-free.

This class template provides all the put functions provided by density::function_queue, and furthermore it adds the try_ variants, that:

Member Typedef Documentation

using put_transaction = typename UnderlyingQueue::template put_transaction<ELEMENT_COMPLETE_TYPE>
using reentrant_put_transaction = typename UnderlyingQueue::template reentrant_put_transaction<ELEMENT_COMPLETE_TYPE>
using consume_operation = typename UnderlyingQueue::consume_operation
using reentrant_consume_operation = typename UnderlyingQueue::reentrant_consume_operation

Constructor & Destructor Documentation

lf_function_queue ( )
defaultnoexcept

Default constructor.

int(float, double),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
assert(queue.empty());
lf_function_queue ( lf_function_queue< CALLABLE, ALLOCATOR_TYPE, ERASURE, PROD_CARDINALITY, CONSUMER_CARDINALITY, CONSISTENCY_MODEL > &&  i_source)
defaultnoexcept

Move constructor.

int(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.push([] { return 6; });
auto queue_1(std::move(queue));
assert(queue.empty());
auto result = queue_1.try_consume();
assert(result && *result == 6);
~lf_function_queue ( )
inline

Destructor

Member Function Documentation

lf_function_queue& operator= ( lf_function_queue< CALLABLE, ALLOCATOR_TYPE, ERASURE, PROD_CARDINALITY, CONSUMER_CARDINALITY, CONSISTENCY_MODEL > &&  i_source)
defaultnoexcept

Move assignment.

int(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue, queue_1;
queue.push([] { return 6; });
queue_1 = std::move(queue);
auto result = queue_1.try_consume();
assert(result && *result == 6);
void push ( ELEMENT_COMPLETE_TYPE &&  i_source)
inline

Adds at the end of the queue a callable object.

See lf_heter_queue::push for a detailed description.

void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.push([] { std::cout << "Hello"; });
queue.push([] { std::cout << " world"; });
queue.push([] { std::cout << "!!!"; });
queue.push([] { std::cout << std::endl; });
while (queue.try_consume())
;
double last_val = 1.;
auto func = [&last_val] { return last_val /= 2.; };
double(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
for (int i = 0; i < 10; i++)
queue.push(func);
while (auto const return_value = queue.try_consume())
std::cout << *return_value << std::endl;
#if !defined(_MSC_VER) || !defined(_M_X64) /* the size of a type must always be a multiple of
the alignment, but in the microsoft's compiler, on 64-bit targets, pointers to data
member are 4 bytes big, but are aligned to 8 bytes.
Test code:
using T = int Struct::*;
std::cout << sizeof(T) << std::endl;
std::cout << alignof(T) << std::endl;
*/
struct Struct
{
int func_1() { return 1; }
int func_2() { return 2; }
int var_1 = 3;
int var_2 = 4;
};
lf_function_queue<int(Struct *)> queue;
queue.push(&Struct::func_1);
queue.push(&Struct::func_2);
queue.push(&Struct::var_1);
queue.push(&Struct::var_2);
Struct struct_instance;
int sum = 0;
while (auto const return_value = queue.try_consume(&struct_instance))
sum += *return_value;
assert(sum == 10);
#endif
void emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Adds at the end of the queue a callable object of type ELEMENT_COMPLETE_TYPE, in-place-constructing it from a perfect forwarded parameter pack.
Note: the template argument ELEMENT_COMPLETE_TYPE can't be deduced from the parameters so it must explicitly specified.

See lf_heter_queue::emplace for a detailed description.

/* This local struct is unmovable and uncopyable, so emplace is the only
option to add it to the queue. Note that operator () returns an int,
but we add it to a void() function queue. This is ok, as we are just
discarding the return value. */
struct Func
{
int const m_value;
Func(int i_value) : m_value(i_value) {}
Func(const Func &) = delete;
Func & operator=(const Func &) = delete;
int operator()() const
{
std::cout << m_value << std::endl;
return m_value;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.template emplace<Func>(7);
bool const invoked = queue.try_consume();
assert(invoked);
put_transaction<typename std::decay<ELEMENT_TYPE>::type> start_push ( ELEMENT_TYPE &&  i_source)
inline

Begins a transaction that appends an element of type ELEMENT_TYPE, copy-constructing or move-constructing it from the source.

See lf_heter_queue::start_push for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
auto transaction = queue.start_push(Func{});
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
bool const invoked = queue.try_consume();
assert(invoked);
put_transaction<ELEMENT_TYPE> start_emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Begins a transaction that appends an element of a type ELEMENT_TYPE, in-place-constructing it from a perfect forwarded parameter pack.

See lf_heter_queue::start_emplace for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
auto transaction = queue.template start_emplace<Func>();
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
bool const invoked = queue.try_consume();
assert(invoked);
void reentrant_push ( ELEMENT_COMPLETE_TYPE &&  i_source)
inline

Adds at the end of the queue a callable object.

See lf_heter_queue::reentrant_push for a detailed description.

void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.reentrant_push([] { std::cout << "Hello"; });
queue.reentrant_push([] { std::cout << " world"; });
queue.reentrant_push([] { std::cout << "!!!"; });
queue.reentrant_push([] { std::cout << std::endl; });
while (queue.try_reentrant_consume())
;
void reentrant_emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Adds at the end of the queue a callable object of type ELEMENT_COMPLETE_TYPE, in-place-constructing it from a perfect forwarded parameter pack.
Note: the template argument ELEMENT_COMPLETE_TYPE can't be deduced from the parameters so it must explicitly specified.

See lf_heter_queue::reentrant_emplace for a detailed description.

/* This local struct is unmovable and uncopyable, so emplace is the only
option to add it to the queue. Note that operator () returns an int,
but we add it to a void() function queue. This is ok, as we are just
discarding the return value. */
struct Func
{
int const m_value;
Func(int i_value) : m_value(i_value) {}
Func(const Func &) = delete;
Func & operator=(const Func &) = delete;
int operator()() const
{
std::cout << m_value << std::endl;
return m_value;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.template reentrant_emplace<Func>(7);
bool const invoked = queue.try_reentrant_consume();
assert(invoked);
reentrant_put_transaction<typename std::decay<ELEMENT_TYPE>::type> start_reentrant_push ( ELEMENT_TYPE &&  i_source)
inline

Begins a transaction that appends an element of type ELEMENT_TYPE, copy-constructing or move-constructing it from the source.

See lf_heter_queue::start_reentrant_push for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
auto transaction = queue.start_reentrant_push(Func{});
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
// now transaction is empty
bool const invoked = queue.try_reentrant_consume();
assert(invoked);
reentrant_put_transaction<ELEMENT_TYPE> start_reentrant_emplace ( CONSTRUCTION_PARAMS &&...  i_construction_params)
inline

Begins a transaction that appends an element of a type ELEMENT_TYPE, in-place-constructing it from a perfect forwarded parameter pack.

See lf_heter_queue::start_reentrant_emplace for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
auto transaction = queue.template start_reentrant_emplace<Func>();
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
bool const invoked = queue.try_reentrant_consume();
assert(invoked);
bool try_push ( progress_guarantee  i_progress_guarantee,
ELEMENT_COMPLETE_TYPE &&  i_source 
)
inlinenoexcept

Tries to add at the end of the queue a callable object respecting a progress guarantee.

See lf_heter_queue::try_push for a detailed description.

void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool const ok =
queue.try_push(progress_lock_free, [] { std::cout << "Hello world!"; });
while (queue.try_consume())
;
bool try_emplace ( progress_guarantee  i_progress_guarantee,
CONSTRUCTION_PARAMS &&...  i_construction_params 
)
inlinenoexcept

Tries to add at the end of the queue a callable object of type ELEMENT_COMPLETE_TYPE respecting a progress guarantee, in-place-constructing it from a perfect forwarded parameter pack.
Note: the template argument ELEMENT_COMPLETE_TYPE can't be deduced from the parameters so it must explicitly specified.

See lf_heter_queue::try_emplace for a detailed description.

/* This local struct is unmovable and uncopyable, so emplace is the only
option to add it to the queue. Note that operator () returns an int,
but we add it to a void() function queue. This is ok, as we are just
discarding the return value. */
struct Func
{
int const m_value;
Func(int i_value) : m_value(i_value) {}
Func(const Func &) = delete;
Func & operator=(const Func &) = delete;
int operator()() const
{
std::cout << m_value << std::endl;
return m_value;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool invoked = false;
if (queue.template try_emplace<Func>(progress_lock_free, 7))
{
invoked = queue.try_consume();
assert(invoked);
}
put_transaction<typename std::decay<ELEMENT_TYPE>::type> try_start_push ( progress_guarantee  i_progress_guarantee,
ELEMENT_TYPE &&  i_source 
)
inlinenoexcept

Tries to begin a transaction that appends an element of type ELEMENT_TYPE, copy-constructing or move-constructing it from the source.

See lf_heter_queue::try_start_push for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool invoked = false;
if (auto transaction = queue.try_start_push(progress_lock_free, Func{}))
{
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
invoked = queue.try_consume();
assert(invoked);
}
put_transaction<ELEMENT_TYPE> try_start_emplace ( progress_guarantee  i_progress_guarantee,
CONSTRUCTION_PARAMS &&...  i_construction_params 
)
inlinenoexcept

Tries to begin a transaction appends an element of a type ELEMENT_TYPE, in-place-constructing it from a perfect forwarded parameter pack.

See lf_heter_queue::try_start_emplace for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool invoked = false;
if (auto transaction = queue.template try_start_emplace<Func>(progress_lock_free))
{
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
invoked = queue.try_consume();
assert(invoked);
}
bool try_reentrant_push ( progress_guarantee  i_progress_guarantee,
ELEMENT_COMPLETE_TYPE &&  i_source 
)
inlinenoexcept

Tries to add at the end of the queue a callable object.

See lf_heter_queue::try_reentrant_push for a detailed description.

void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
if (queue.try_reentrant_push(
progress_lock_free, [] { std::cout << "Hello world"; }))
{
while (queue.try_reentrant_consume())
;
}
bool try_reentrant_emplace ( progress_guarantee  i_progress_guarantee,
CONSTRUCTION_PARAMS &&...  i_construction_params 
)
inlinenoexcept

Tries to add at the end of the queue a callable object of type ELEMENT_COMPLETE_TYPE, in-place-constructing it from a perfect forwarded parameter pack.
Note: the template argument ELEMENT_COMPLETE_TYPE can't be deduced from the parameters so it must explicitly specified.

See lf_heter_queue::try_reentrant_emplace for a detailed description.

/* This local struct is unmovable and uncopyable, so emplace is the only
option to add it to the queue. Note that operator () returns an int,
but we add it to a void() function queue. This is ok, as we are just
discarding the return value. */
struct Func
{
int const m_value;
Func(int i_value) : m_value(i_value) {}
Func(const Func &) = delete;
Func & operator=(const Func &) = delete;
int operator()() const
{
std::cout << m_value << std::endl;
return m_value;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool invoked = false;
if (queue.template try_reentrant_emplace<Func>(progress_lock_free, 7))
{
invoked = queue.try_reentrant_consume();
assert(invoked);
}
reentrant_put_transaction<typename std::decay<ELEMENT_TYPE>::type> try_start_reentrant_push ( progress_guarantee  i_progress_guarantee,
ELEMENT_TYPE &&  i_source 
)
inlinenoexcept

Tries to begin a transaction appends an element of type ELEMENT_TYPE, copy-constructing or move-constructing it from the source.

See lf_heter_queue::try_start_reentrant_push for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool invoked = false;
if (auto transaction = queue.try_start_reentrant_push(progress_lock_free, Func{}))
{
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
// now transaction is empty
invoked = queue.try_reentrant_consume();
assert(invoked);
}
reentrant_put_transaction<ELEMENT_TYPE> try_start_reentrant_emplace ( progress_guarantee  i_progress_guarantee,
CONSTRUCTION_PARAMS &&...  i_construction_params 
)
inlinenoexcept

Tries to begin a transaction appends an element of a type ELEMENT_TYPE, in-place-constructing it from a perfect forwarded parameter pack.

See lf_heter_queue::try_start_reentrant_emplace for a detailed description.

Examples

struct Func
{
const char * m_string_1;
const char * m_string_2;
void operator()()
{
std::cout << m_string_1 << std::endl;
std::cout << m_string_2 << std::endl;
}
};
void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
bool invoked = false;
if (
auto transaction =
queue.template try_start_reentrant_emplace<Func>(progress_lock_free))
{
// in case of exception here, since the transaction is not committed, it is discarded with no observable effects
transaction.element().m_string_1 = transaction.raw_allocate_copy("Hello world");
transaction.element().m_string_2 =
transaction.raw_allocate_copy("\t(I'm so happy)!!");
transaction.commit();
invoked = queue.try_reentrant_consume();
assert(invoked);
}
std::conditional<std::is_void<RET_VAL>::value, bool, optional<RET_VAL> >::type try_consume ( PARAMS...  i_params)
inline

If the queue is not empty, invokes the first function object of the queue and then deletes it from the queue. Otherwise no operation is performed.

Parameters
i_params...parameters to be forwarded to the function object
Returns
If RET_VAL is void, the return value is a boolean indicating whether a callable object was consumed. Otherwise the return value is an optional that contains the value returned by the callable object, or an empty optional in case the queue was empty.

This function is not reentrant: if the callable object accesses in any way this queue, the behavior is undefined. Use lf_function_queue::try_reentrant_consume if you are not sure about what the callable object may do.

Throws: unspecified
Exception guarantee: strong (in case of exception the function has no observable effects).

int(std::vector<std::string> & vect),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.push([](std::vector<std::string> & vect) {
vect.push_back("Hello");
return 2;
});
queue.push([](std::vector<std::string> & vect) {
vect.push_back(" world!");
return 3;
});
std::vector<std::string> strings;
int sum = 0;
while (auto const return_value = queue.try_consume(strings))
{
sum += *return_value;
}
assert(sum == 5);
for (auto const & str : strings)
std::cout << str;
std::cout << std::endl;
std::conditional<std::is_void<RET_VAL>::value, bool, optional<RET_VAL> >::type try_consume ( consume_operation i_consume,
PARAMS...  i_params 
)
inline

If the queue is not empty, invokes the first function object of the queue and then deletes it from the queue. Otherwise no operation is performed.

The consume operation is performed using the provided consume_operation object. If the element to consume is in the same page of the last element visited by the provided consume operation, the implementation does not need to pin page. For this reason this overload of try_consume is much faster than the other.

Parameters
i_consumeobject to use for the consume operation
i_params...parameters to be forwarded to the function object
Returns
If RET_VAL is void, the return value is a boolean indicating whether a callable object was consumed. Otherwise the return value is an optional that contains the value returned by the callable object, or an empty optional in case the queue was empty.

This function is not reentrant: if the callable object accesses in any way this queue, the behavior is undefined. Use lf_function_queue::try_reentrant_consume if you are not sure about what the callable object may do.

Throws: unspecified
Exception guarantee: strong (in case of exception the function has no observable effects).

using Queue = lf_function_queue<
int(std::vector<std::string> & vect),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>;
Queue queue;
queue.push([](std::vector<std::string> & vect) {
vect.push_back("Hello");
return 2;
});
queue.push([](std::vector<std::string> & vect) {
vect.push_back(" world!");
return 3;
});
std::vector<std::string> strings;
// providing a cached consume_operation gives better performances
typename Queue::consume_operation consume;
int sum = 0;
while (auto const return_value = queue.try_consume(consume, strings))
{
sum += *return_value;
}
assert(sum == 5);
for (auto const & str : strings)
std::cout << str;
std::cout << std::endl;
std::conditional<std::is_void<RET_VAL>::value, bool, optional<RET_VAL> >::type try_reentrant_consume ( PARAMS...  i_params)
inline

If the queue is not empty, invokes the first function object of the queue and then deletes it from the queue. Otherwise no operation is performed.

Parameters
i_params...parameters to be forwarded to the function object
Returns
If RET_VAL is void, the return value is a boolean indicating whether a callable object was consumed. Otherwise the return value is an optional that contains the value returned by the callable object, or an empty optional in case the queue was empty.

This function is reentrant: the callable object can access in any way this queue.

Throws: unspecified
Exception guarantee: strong (in case of exception the function has no observable effects).

void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
auto func1 = [&queue] {
std::cout << (queue.empty() ? "The queue is empty" : "The queue is not empty")
<< std::endl;
};
auto func2 = [&queue, func1] { queue.push(func1); };
queue.push(func1);
queue.push(func2);
/* The callable objects we are going to invoke will access the queue, so we
must use a reentrant consume. Note: during the invoke of the last function
the queue is empty to any observer. */
while (queue.try_reentrant_consume())
;
// Output:
// The queue is not empty
// The queue is empty
std::conditional<std::is_void<RET_VAL>::value, bool, optional<RET_VAL> >::type try_reentrant_consume ( reentrant_consume_operation i_consume,
PARAMS...  i_params 
)
inline

If the queue is not empty, invokes the first function object of the queue and then deletes it from the queue. Otherwise no operation is performed.

The consume operation is performed using the provided consume_operation object. If the element to consume is in the same page of the last element visited by the provided consume operation, the implementation does not need to pin page. For this reason this overload of try_reentrant_consume is much faster than the other.

Parameters
i_consumeobject to use for the consume operation
i_params...parameters to be forwarded to the function object
Returns
If RET_VAL is void, the return value is a boolean indicating whether a callable object was consumed. Otherwise the return value is an optional that contains the value returned by the callable object, or an empty optional in case the queue was empty.

This function is reentrant: the callable object can access in any way this queue.

Throws: unspecified
Exception guarantee: strong (in case of exception the function has no observable effects).

void(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
auto func1 = [&queue] {
std::cout << (queue.empty() ? "The queue is empty" : "The queue is not empty")
<< std::endl;
};
auto func2 = [&queue, func1] { queue.push(func1); };
queue.push(func1);
queue.push(func2);
// providing a cached consume_operation gives much better performances
typename decltype(queue)::reentrant_consume_operation consume;
/* The callable objects we are going to invoke will access the queue, so we
must use a reentrant consume. Note: during the invoke of the last function
the queue is empty to any observer. */
while (queue.try_reentrant_consume(consume))
;
// Output:
// The queue is not empty
// The queue is empty
void clear ( )
inlinenoexcept

Deletes all the callable objects in the queue. This function is disabled at compile-time if ERASURE is function_manual_clear.


Effects on iterators : all the iterators are invalidated
Throws: nothing
Complexity: linear.

int(),
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue;
queue.push([] { return 6; });
queue.clear();
assert(queue.empty());
bool empty ( )
inlinenoexcept

Returns whether this container is empty

Friends And Related Function Documentation

void swap ( lf_function_queue< CALLABLE, ALLOCATOR_TYPE, ERASURE, PROD_CARDINALITY, CONSUMER_CARDINALITY, CONSISTENCY_MODEL > &  i_first,
lf_function_queue< CALLABLE, ALLOCATOR_TYPE, ERASURE, PROD_CARDINALITY, CONSUMER_CARDINALITY, CONSISTENCY_MODEL > &  i_second 
)
friend

Swaps two function queues.

int(),
ERASURE,
PROD_CARDINALITY,
CONSUMER_CARDINALITY,
CONSISTENCY_MODEL>
queue, queue_1;
queue.push([] { return 6; });
std::swap(queue, queue_1);
assert(queue.empty());
auto result = queue_1.try_consume();
assert(result && *result == 6);

Member Data Documentation

constexpr bool concurrent_puts = PROD_CARDINALITY == concurrency_multiple
static

Whether multiple threads can do put operations on the same queue without any further synchronization.

constexpr bool concurrent_consumes = CONSUMER_CARDINALITY == concurrency_multiple
static

Whether multiple threads can do consume operations on the same queue without any further synchronization.

constexpr bool concurrent_put_consumes = true
static

Whether puts and consumes can be done concurrently without any further synchronization. In any case unsynchronized concurrency is constrained by concurrent_puts and concurrent_consumes.

constexpr bool is_seq_cst = CONSISTENCY_MODEL == consistency_sequential
static

Whether this queue is sequential consistent.


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