density
C++11 library for paged memory management, function queues, heterogeneous queues and lifo memory management
Public Member Functions | Friends | List of all members
heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE >::put_transaction< ELEMENT_COMPLETE_TYPE > Class Template Reference

#include <heter_queue.h>

Public Member Functions

 put_transaction () noexcept
 
 put_transaction (const put_transaction &)=delete
 
put_transactionoperator= (const put_transaction &)=delete
 
template<typename OTHERTYPE , typename = typename std::enable_if< std::is_same<OTHERTYPE, ELEMENT_COMPLETE_TYPE>::value || std::is_void<ELEMENT_COMPLETE_TYPE>::value>::type>
 put_transaction (put_transaction< OTHERTYPE > &&i_source) noexcept
 
template<typename OTHERTYPE , typename = typename std::enable_if< std::is_same<OTHERTYPE, ELEMENT_COMPLETE_TYPE>::value || std::is_void<ELEMENT_COMPLETE_TYPE>::value>::type>
put_transactionoperator= (put_transaction< OTHERTYPE > &&i_source) noexcept
 
void * raw_allocate (size_t i_size, size_t i_alignment)
 
template<typename INPUT_ITERATOR >
std::iterator_traits< INPUT_ITERATOR >::value_typeraw_allocate_copy (INPUT_ITERATOR i_begin, INPUT_ITERATOR i_end)
 
template<typename INPUT_RANGE >
auto raw_allocate_copy (const INPUT_RANGE &i_source_range) -> decltype(std::declval< put_transaction >().raw_allocate_copy( std::begin(i_source_range), std::end(i_source_range)))
 
void commit () noexcept
 
void cancel () noexcept
 
bool empty () const noexcept
 
 operator bool () const noexcept
 
heter_queuequeue () const noexcept
 
void * element_ptr () const noexcept
 
ELEMENT_COMPLETE_TYPE & element () const noexcept
 
const RUNTIME_TYPE & complete_type () const noexcept
 
 ~put_transaction ()
 
 put_transaction (PrivateType, heter_queue *i_queue, Allocation i_push_data) noexcept
 

Friends

template<typename OTHERTYPE >
class put_transaction
 
void swap (put_transaction &i_first, put_transaction &i_second) noexcept
 

Detailed Description

template<typename RUNTIME_TYPE = runtime_type<>, typename ALLOCATOR_TYPE = default_allocator>
template<typename ELEMENT_COMPLETE_TYPE = void>
class density::heter_queue< RUNTIME_TYPE, ALLOCATOR_TYPE >::put_transaction< ELEMENT_COMPLETE_TYPE >

Move-only class template that can be bound to a put transaction, otherwise it's empty.

Template Parameters
ELEMENT_COMPLETE_TYPEComplete type of elements that can be handled by a transaction, or void. ELEMENT_COMPLETE_TYPE must decay to itself (it can't be cv-qualified).

Transactional put functions on heter_queue return a non-empty put_transaction that can be used to allocate raw memory in the queue, inspect or alter the element while it is still not observable in the queue, and commit or cancel the push.

A put_transaction is empty when:

Calling raw_allocate, raw_allocate_copy, commit, cancel, element_ptr, element or complete_type on an empty put_transaction triggers undefined behavior.

A void put_transaction can be move constructed/assigned from any put_transaction. A typed put_transaction can be move constructed/assigned only from a put_transaction with the same ELEMENT_COMPLETE_TYPE.

Constructor & Destructor Documentation

put_transaction ( )
inlinenoexcept

Constructs an empty put transaction

put_transaction ( const put_transaction< ELEMENT_COMPLETE_TYPE > &  )
delete

Copy construction is not allowed.

static_assert(!std::is_copy_constructible<heter_queue<>::put_transaction<>>::value, "");
static_assert(
!std::is_copy_constructible<heter_queue<int>::put_transaction<>>::value, "");
put_transaction ( put_transaction< OTHERTYPE > &&  i_source)
inlinenoexcept

Move constructs a put_transaction, transferring the state from the source.

Parameters
i_sourcesource to move from. It becomes empty after the call.
heter_queue<> queue;
auto transaction1 = queue.start_push(1);
// move from transaction1 to transaction2
auto transaction2(std::move(transaction1));
assert(transaction1.empty());
assert(transaction2.element() == 1);
// commit transaction2
transaction2.commit();
assert(transaction2.empty());
// put_transaction<void> can be move constructed from any put_transaction<T>
static_assert(
std::is_constructible<
heter_queue<>::put_transaction<void>,
heter_queue<>::put_transaction<void> &&>::value,
"");
static_assert(
std::is_constructible<
heter_queue<>::put_transaction<void>,
heter_queue<>::put_transaction<int> &&>::value,
"");
// put_transaction<T> can be move constructed only from put_transaction<T>
static_assert(
!std::is_constructible<
heter_queue<>::put_transaction<int>,
heter_queue<>::put_transaction<void> &&>::value,
"");
static_assert(
!std::is_constructible<
heter_queue<>::put_transaction<int>,
heter_queue<>::put_transaction<float> &&>::value,
"");
static_assert(
std::is_constructible<
heter_queue<>::put_transaction<int>,
heter_queue<>::put_transaction<int> &&>::value,
"");
~put_transaction ( )
inline

If this transaction is empty the destructor has no side effects. Otherwise it cancels it.

queue.start_push(42); /* this transaction is destroyed without being committed,
so it gets canceled automatically. */

Member Function Documentation

put_transaction& operator= ( const put_transaction< ELEMENT_COMPLETE_TYPE > &  )
delete

Copy assignment is not allowed.

static_assert(!std::is_copy_assignable<heter_queue<>::put_transaction<>>::value, "");
static_assert(!std::is_copy_assignable<heter_queue<int>::put_transaction<>>::value, "");
put_transaction& operator= ( put_transaction< OTHERTYPE > &&  i_source)
inlinenoexcept

Move assigns a put_transaction, transferring the state from the source.

Parameters
i_sourcesource to move from. It becomes empty after the call.
heter_queue<> queue;
auto transaction1 = queue.start_push(1);
heter_queue<>::put_transaction<> transaction2;
transaction2 = std::move(transaction1);
assert(transaction1.empty());
transaction2.commit();
assert(transaction2.empty());
// put_transaction<void> can be move assigned from any put_transaction<T>
static_assert(
std::is_assignable<
heter_queue<>::put_transaction<void>,
heter_queue<>::put_transaction<void> &&>::value,
"");
static_assert(
std::is_assignable<
heter_queue<>::put_transaction<void>,
heter_queue<>::put_transaction<int> &&>::value,
"");
// put_transaction<T> can be move assigned only from put_transaction<T>
static_assert(
!std::is_assignable<
heter_queue<>::put_transaction<int>,
heter_queue<>::put_transaction<void> &&>::value,
"");
static_assert(
!std::is_assignable<
heter_queue<>::put_transaction<int>,
heter_queue<>::put_transaction<float> &&>::value,
"");
static_assert(
std::is_assignable<
heter_queue<>::put_transaction<int>,
heter_queue<>::put_transaction<int> &&>::value,
"");
void* raw_allocate ( size_t  i_size,
size_t  i_alignment 
)
inline

Allocates a memory block associated to the element being added in the queue. The block may be allocated contiguously with the elements in the memory pages. If the block does not fit in one page, the block is allocated using non-paged memory services of the allocator.


The block doesn't need to be deallocated, and is guaranteed to be valid until the associated element is destroyed. The initial content of the block is undefined.

Parameters
i_sizesize in bytes of the block to allocate.
i_alignmentalignment of the block to allocate. It must be a non-zero power of 2, and less than or equal to i_size.
Precondition
The behavior is undefined if either:
  • this transaction is empty
  • the alignment is not valid
  • the size is not a multiple of the alignment

Complexity: Unspecified.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

heter_queue<> queue;
struct Msg
{
std::chrono::high_resolution_clock::time_point m_time =
std::chrono::high_resolution_clock::now();
size_t m_len = 0;
void * m_data = nullptr;
};
auto post_message = [&queue](const void * i_data, size_t i_len) {
auto transaction = queue.start_emplace<Msg>();
transaction.element().m_len = i_len;
transaction.element().m_data = transaction.raw_allocate(i_len, 1);
memcpy(transaction.element().m_data, i_data, i_len);
assert(
!transaction
.empty()); // a put transaction is not empty if it's bound to an element being put
transaction.commit();
assert(transaction.empty()); // the commit makes the transaction empty
};
auto const start_time = std::chrono::high_resolution_clock::now();
auto consume_all_msgs = [&queue, &start_time] {
while (auto consume = queue.try_start_consume())
{
auto const checksum =
compute_checksum(consume.element<Msg>().m_data, consume.element<Msg>().m_len);
std::cout << "Message with checksum " << checksum << " at ";
std::cout << (consume.element<Msg>().m_time - start_time).count() << std::endl;
consume.commit();
}
};
int msg_1 = 42, msg_2 = 567;
post_message(&msg_1, sizeof(msg_1));
post_message(&msg_2, sizeof(msg_2));
consume_all_msgs();
std::iterator_traits<INPUT_ITERATOR>::value_type* raw_allocate_copy ( INPUT_ITERATOR  i_begin,
INPUT_ITERATOR  i_end 
)
inline

Allocates a memory block associated to the element being added in the queue, and copies the content from a range of iterators. The block may be allocated contiguously with the elements in the memory pages. If the block does not fit in one page, the block is allocated using non-paged memory services of the allocator.


The block doesn't need to be deallocated, and is guaranteed to be valid until the associated element is destroyed. The initial content of the block is undefined.

Parameters
i_beginfirst element to be copied
i_endfirst element not to be copied


Requires:

  • INPUT_ITERATOR must satisfy the requirements of InputIterator
  • the value type must be trivially destructible
Precondition
The behavior is undefined if either:
  • this transaction is empty
  • i_end is not reachable from i_begin

Complexity: Unspecified.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects)

struct Msg
{
size_t m_len = 0;
char * m_chars = nullptr;
};
auto post_message = [&queue](const char * i_data, size_t i_len) {
auto transaction = queue.start_emplace<Msg>();
transaction.element().m_len = i_len;
transaction.element().m_chars =
transaction.raw_allocate_copy(i_data, i_data + i_len);
memcpy(transaction.element().m_chars, i_data, i_len);
transaction.commit();
};
auto raw_allocate_copy ( const INPUT_RANGE &  i_source_range) -> decltype(std::declval<put_transaction>().raw_allocate_copy( std::begin(i_source_range), std::end(i_source_range)))
inline

Allocates a memory block associated to the element being added in the queue, and copies the content from a range. The block may be allocated contiguously with the elements in the memory pages. If the block does not fit in one page, the block is allocated using non-paged memory services of the allocator.


The block doesn't need to be deallocated, and is guaranteed to be valid until the associated element is destroyed. The initial content of the block is undefined.

Parameters
i_source_rangeto be copied


Requires:

  • The iterators of the range must satisfy the requirements of InputIterator
  • the value type must be trivially destructible
Precondition
The behavior is undefined if either:
  • this transaction is empty

Complexity: Unspecified.
Effects on iterators: no iterator is invalidated
Throws: unspecified.
Exception guarantee: strong (in case of exception the function has no observable effects).

struct Msg
{
char * m_chars = nullptr;
};
auto post_message = [&queue](const std::string & i_string) {
auto transaction = queue.start_emplace<Msg>();
transaction.element().m_chars = transaction.raw_allocate_copy(i_string);
transaction.commit();
};
void commit ( )
inlinenoexcept

Makes the effects of the transaction observable. This object becomes empty.

Precondition
The behavior is undefined if either:
  • this transaction is empty

Complexity: Constant.
Effects on iterators: no iterator is invalidated
Throws: Nothing.

void cancel ( )
inlinenoexcept

Cancel the transaction. This object becomes empty.

Precondition
The behavior is undefined if either:
  • this transaction is empty

Complexity: Constant.
Effects on iterators: no iterator is invalidated
Throws: Nothing.

heter_queue<> queue;
// start and cancel a put
assert(queue.empty());
auto put = queue.start_push(42);
/* assert(queue.empty()); <- this assert would trigger an undefined behavior, because it would access
the queue during a non-reentrant put transaction. */
assert(!put.empty());
put.cancel();
assert(queue.empty() && put.empty());
// start and commit a put
put = queue.start_push(42);
put.commit();
assert(std::distance(queue.cbegin(), queue.cend()) == 1);
bool empty ( ) const
inlinenoexcept

Returns true whether this object is not currently bound to a transaction.

heter_queue<>::put_transaction<> transaction;
assert(transaction.empty());
transaction = queue.start_push(1);
assert(!transaction.empty());
operator bool ( ) const
inlineexplicitnoexcept

Returns true whether this object is bound to a transaction. Same to !consume_operation::empty.

heter_queue<>::put_transaction<> transaction;
assert(!transaction);
transaction = queue.start_push(1);
assert(transaction);
heter_queue* queue ( ) const
inlinenoexcept

Returns a pointer to the target queue if a transaction is bound, otherwise returns nullptr

heter_queue<>::put_transaction<> transaction;
assert(transaction.queue() == nullptr);
transaction = queue.start_push(1);
assert(transaction.queue() == &queue);
void* element_ptr ( ) const
inlinenoexcept

Returns a pointer to the object being added.
Notes:

  • The object is constructed when the transaction is started, so this function always returns a pointer to a valid object.
  • This function returns a void pointer to the element. Non-dynamic transactional put function (start_push, start_emplace, start_reentrant_push, start_reentrant_emplace) return a typed_put_transaction or a reentrant_put_transaction, that provide the function element(), which is a better alternative to this function
Precondition
The behavior is undefined if either:
  • this transaction is empty
int value = 42;
auto put = queue.start_dyn_push_copy(runtime_type<>::make<decltype(value)>(), &value);
assert(*static_cast<int *>(put.element_ptr()) == 42);
std::cout << "Putting an " << put.complete_type().type_info().name() << "..."
<< std::endl;
auto put_1 = queue.start_push(1);
assert(*static_cast<int *>(put_1.element_ptr()) == 1); // this is fine
assert(put_1.element() == 1); // this is better
ELEMENT_COMPLETE_TYPE& element ( ) const
inlinenoexcept

Returns a reference to the element being added. This function can be used to modify the element before calling the commit.
Note: An element is observable in the queue only after commit has been called on the put_transaction. The element is constructed at the begin of the transaction, so the returned object is always valid.


Requires:

  • ELEMENT_COMPLETE_TYPE must not be void
Precondition
The behavior is undefined if:
  • this transaction is empty
int value = 42;
auto untyped_put =
queue.start_reentrant_dyn_push_copy(runtime_type<>::make<decltype(value)>(), &value);
auto typed_put = queue.start_reentrant_push(42.);
/* typed_put = std::move(untyped_put); <- this would not compile: can't assign an untyped
transaction to a typed transaction */
assert(typed_put.element() == 42.);
const RUNTIME_TYPE& complete_type ( ) const
inlinenoexcept

Returns the type of the object being added.

Precondition
The behavior is undefined if either:
  • this transaction is empty
int value = 42;
auto put = queue.start_dyn_push_copy(runtime_type<>::make<decltype(value)>(), &value);
assert(put.complete_type().is<int>());
std::cout << "Putting an " << put.complete_type().type_info().name() << "..."
<< std::endl;

Friends And Related Function Documentation

void swap ( put_transaction< ELEMENT_COMPLETE_TYPE > &  i_first,
put_transaction< ELEMENT_COMPLETE_TYPE > &  i_second 
)
friend

Swaps two instances of put_transaction.

auto transaction_1 = queue.start_push(1);
assert(!transaction_1.empty());
heter_queue<>::put_transaction<int> transaction_2;
swap(transaction_1, transaction_2);
assert(transaction_1.empty());

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