Commit bc8d07e3 by Tobias Fuchs

containers_cpp: implementing LLX/SCX, using FixedSizeList for list parameters

parent 16ccb48e
......@@ -30,28 +30,43 @@
namespace embb {
namespace containers {
namespace internal {
template< typename ElementT >
FixedSizeList<ElementT>::FixedSizeList(size_t capacity) :
max_size_(capacity),
size_(0) {
elementsArray = static_cast<ElementT*>(
embb::base::Allocation::Allocate(sizeof(ElementT) *
max_size_));
}
template< typename ElementT >
FixedSizeList<ElementT>::FixedSizeList(size_t max_size) :
max_size(max_size),
size(0) {
FixedSizeList<ElementT>::FixedSizeList(const FixedSizeList & other) :
max_size_(other.max_size_),
size_(0) {
elementsArray = static_cast<ElementT*>(
embb::base::Allocation::Allocate(sizeof(ElementT) *
max_size));
max_size_));
const_iterator it;
const_iterator end = other.end();
for (it = other.begin(); it != end; ++it) {
PushBack(*it);
}
}
template< typename ElementT >
inline size_t FixedSizeList<ElementT>::GetSize() const {
return size;
return size_;
}
template< typename ElementT >
inline size_t FixedSizeList<ElementT>::GetMaxSize() const {
return max_size;
return max_size_;
}
template< typename ElementT >
inline void FixedSizeList<ElementT>::clear() {
size = 0;
size_ = 0;
}
template< typename ElementT >
......@@ -63,31 +78,31 @@ FixedSizeList<ElementT>::begin() const {
template< typename ElementT >
typename FixedSizeList<ElementT>::iterator
FixedSizeList<ElementT>::end() const {
return &elementsArray[size];
return &elementsArray[size_];
}
template< typename ElementT >
FixedSizeList< ElementT > &
FixedSizeList<ElementT>::operator= (const FixedSizeList & other) {
size = 0;
if (max_size < other.size) {
size_ = 0;
if (max_size_ < other.size_) {
EMBB_THROW(embb::base::ErrorException, "Copy target to small");
}
for (const_iterator it = other.begin(); it != other.end(); ++it) {
const_iterator it;
const_iterator end = other.end();
for (it = other.begin(); it != end; ++it) {
PushBack(*it);
}
return *this;
}
template< typename ElementT >
bool FixedSizeList<ElementT>::PushBack(ElementT const el) {
if (size + 1 > max_size) {
bool FixedSizeList<ElementT>::PushBack(const ElementT & el) {
if (size_ + 1 > max_size_) {
return false;
}
elementsArray[size] = el;
size++;
elementsArray[size_] = el;
size_++;
return true;
}
......@@ -100,4 +115,4 @@ FixedSizeList<ElementT>::~FixedSizeList() {
} // namespace containers
} // namespace embb
#endif // EMBB_CONTAINERS_INTERNAL_FIXED_SIZE_LIST_INL_H_
#endif // EMBB_CONTAINERS_INTERNAL_FIXED_SIZE_LIST_INL_H_
\ No newline at end of file
......@@ -30,6 +30,7 @@
namespace embb {
namespace containers {
namespace internal {
/**
* A list with fixed size, implemented as an array. Replaces std::vector that
* was used in previous hazard pointer implementation.
......@@ -41,28 +42,21 @@ namespace internal {
template< typename ElementT >
class FixedSizeList {
private:
/**
* Capacity of the list
*/
size_t max_size;
/**
* Capacity of the list
*/
size_t max_size_;
/**
* Size of the list
*/
size_t size;
size_t size_;
/**
* Pointer to the array containing the list
*/
ElementT* elementsArray;
/**
* Copy constructor not implemented. Would require dynamic memory allocation.
*/
FixedSizeList(
const FixedSizeList &
/**< [IN] Other list */);
public:
/**
* Definition of an iterator
......@@ -78,20 +72,38 @@ class FixedSizeList {
* Constructor, initializes list with given capacity
*/
FixedSizeList(
size_t max_size
/**< [IN] Capacity of the list */);
size_t capacity
/**< [IN] Capacity of the list */
);
/**
* Copy constructor.
*/
FixedSizeList(
const FixedSizeList &
/**< [IN] Other list */
);
/**
* Copies the elements of another list to this list. The capacity of
* this list has to be greater than or equal to the size of the other list.
*/
FixedSizeList & operator=(
const FixedSizeList & other
/**< [IN] Other list */
);
/**
* Gets the current size of the list
* Gets the current number of element in the list
*
* \return Size of the list
*/
inline size_t GetSize() const;
/**
* Gets the capacity of the list
* Gets the maximum number of elements that the list can hold
*
* \return The capacity of the list
* \return The element capacity of the list
*/
inline size_t GetMaxSize() const;
......@@ -115,22 +127,15 @@ class FixedSizeList {
iterator end() const;
/**
* Copies the elements of another list to this list. The capacity of
* this list has to be greater than or equal to the size of the other list.
*/
FixedSizeList & operator=(
const FixedSizeList & other
/**< [IN] Other list */);
/**
* Appends an element to the end of the list
*
* \return \c false if the operation was not successful because the list is
* full, otherwise \c true.
*/
bool PushBack(
ElementT const el
/**< [IN] Element to append to the list */);
const ElementT & el
/**< [IN] Element to append to the list */
);
/**
* Destructs the list.
......@@ -142,6 +147,6 @@ class FixedSizeList {
} // namespace containers
} // namespace embb
#include "./fixed_size_list-inl.h"
#include <embb/containers/internal/fixed_size_list-inl.h>
#endif // EMBB_CONTAINERS_INTERNAL_FIXED_SIZE_LIST_H_
/*
* Copyright (c) 2014, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_
#define EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_
#include <embb/containers/primitives/llx_scx.h>
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
#include <embb/base/memory_allocation.h>
#include <vector>
#include <stdarg.h>
/**
* Implementation of the LX/STX primitive as presented in
* "Pragmatic Primitives for Non-blocking Data Structures"
* (Brown et al., 2013).
*/
namespace embb {
namespace containers {
namespace primitives {
namespace internal {
#if 0
/**
* RAII-style implementation of a simplified hazard
* pointer scheme. SmartHazardPointer instances should
* reside only on the stack, never the heap, and T*
* values should reside only in the heap. A NodePtr can
* be automatically converted to a Node*, but an explicit
* constructor is needed to go the other way.
*
* Example:
* SmartHazardPointer<Node> shp = SmartHazardPointer(
* nodePool?>allocate());
* // assign via default copy constructor:
* *shp = Node(...);
*/
template< typename T >
class SmartHazardPointer {
public:
SmartHazardPointer(T ** node) {
while (true) {
ptr = *node;
table_->add(ptr);
// Full fence:
embb_atomic_memory_barrier();
T * reread = *node;
// @TODO: Prove practical wait-freedom
if (read == reread) {
return;
}
}
}
SmartHazardPointer(T * node) {
ptr = node;
}
/**
* Dereference operator.
*/
T * operator->() {
return ptr;
}
/**
* Dereference lvalue.
*/
T & operator*() {
return *ptr;
}
/**
* Equality test with regular pointer
*/
bool operator==(T * const other) {
return ptr == other;
}
/**
* Equality test with another SmartHazardPointer
*/
bool operator==(const SmartHazardPointer & other) {
return this->ptr == other->ptr;
}
/**
* Destructor, retires hazard pointer.
*/
~SmartHazardPointer() {
table_->remove(ptr);
}
/**
* Conversion to regular pointer.
*/
operator T*() {
return ptr;
}
private:
static HazardPointerTable * table_;
T * ptr;
};
#endif
} // namespace internal
template< class UserData >
unsigned int LlxScx<UserData>::ThreadId() {
unsigned int thread_index;
int return_val = embb_internal_thread_index(&thread_index);
if (return_val != EMBB_SUCCESS)
EMBB_THROW(embb::base::ErrorException, "Could not get thread id!");
return thread_index;
}
template< class UserData >
LlxScx<UserData>::LlxScx(size_t max_links)
: max_links_(max_links) {
typedef embb::containers::internal::FixedSizeList<LlxResult>
llx_result_list_t;
typedef embb::containers::internal::FixedSizeList<ScxRecord_t>
scx_record_list_t;
unsigned int num_threads = embb::base::Thread::GetThreadsMaxCount();
// Table in shared memory containing for each r in TryStoreConditional's
// dependent links, a copy of r's info value in this threads local table
// of LLX results.
scx_record_list_t empty_scx_list(3);
info_fields_ = static_cast<scx_record_list_t *>(
embb::base::Allocation::AllocateCacheAligned(
num_threads * sizeof(empty_scx_list)));
}
template< class UserData >
LlxScx<UserData>::~LlxScx() {
embb::base::Allocation::FreeAligned(info_fields_);
}
template< class UserData >
bool LlxScx<UserData>::TryLoadLinked(
DataRecord_t * const data_record,
DataRecord_t & user_data,
bool & finalized) {
finalized = false;
// Order of initialization matters:
bool marked_1 = data_record->IsMarkedForFinalize();
ScxRecord_t * curr_scx = data_record->ScxInfo().Load();
OperationState curr_state = curr_scx->State();
bool marked_2 = data_record->IsMarkedForFinalize();
if (curr_state == OperationState::Aborted ||
(curr_state == OperationState::Comitted && !marked_2)) {
// read mutable fields into local variable:
UserData user_data_local(data_record->Data());
if (data_record->ScxInfo() == curr_scx) {
// store <r, curr_scx, user_data_local> in
// the thread-specific table:
LlxResult llx_result;
llx_result.data_record = data_record;
llx_result.scx_record = curr_scx;
llx_result.user_data = user_data_local;
thread_llx_results_.Get().PushBack(llx_result);
// Set return value:
user_data = user_data_local;
return true;
}
}
// @TODO: Re-check if logical precedence is okay here (see paper):
if (curr_scx->State() == OperationState::Comitted ||
(curr_scx->State() == OperationState::InProgress &&
curr_scx->Help() &&
marked_1)) {
// Successfully completed active SCX:
finalized = true;
return false;
}
if (data_record->ScxInfo()->State() == OperationState::InProgress) {
// Help active SCX:
data_record->ScxInfo()->Help();
}
return false;
}
template< class UserData >
template< typename FieldType >
bool LlxScx<UserData>::TryStoreConditional(
embb::base::Atomic<FieldType> * field,
FieldType value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps) {
typedef embb::containers::internal::FixedSizeList<DataRecord_t *> dr_list_t;
//typedef embb::containers::internal::FixedSizeList<ScxRecord_t *> op_list_t;
// Preconditions:
// 1. For each r in linked_deps, this thread has performed an invocation
// I_r of LLX(r) linked to this SCX.
// 2. Given value is not initial value of field.
// 3. For each r in V, no SCX(V',R',field,value) has been linearized before
// any I_r was linearized.
unsigned int thread_id = ThreadId();
// Let info_fields be a table in shared memory containing for each r in V,
// a copy of r's info value in this threads local table of LLX results:
info_fields_[thread_id].clear();
dr_list_t::const_iterator it;
dr_list_t::const_iterator end;
end = linked_deps.end();
// for each r in V ...
for (it = linked_deps.begin(); it != end; ++it) {
// Find LLX result of r in thread-local table of LLX results:
typedef embb::containers::internal::FixedSizeList<LlxResult>
llx_result_list;
llx_result_list::iterator l_it = thread_llx_results_.Get().begin();
llx_result_list::iterator l_end = thread_llx_results_.Get().end();
for (; l_it != l_end && l_it->data_record != *it; ++l_it);
if (l_it == l_end) {
// Missing LLX result for given linked data record, user did not
// load-link a data record this SCX depends on.
EMBB_THROW(embb::base::ErrorException,
"Missing preceding LLX on a data record used for SCX");
}
// ... copy of r's info value in this threads local table of LLX results
ScxRecord_t scx_op(*(l_it->data_record->ScxInfo().Load()));
info_fields_[thread_id].PushBack(scx_op);
}
// Announce SCX operation. Lists linked_deps and finalize_dep are
// guaranteed to remain on the stack until this announced operation
// is completed, so no allocation/pool is necessary.
ScxRecord_t scx(
linked_deps,
finalize_deps,
// target field:
reinterpret_cast<embb::base::Atomic<cas_t> *>(field),
// new value:
reinterpret_cast<cas_t>(value),
// old value:
reinterpret_cast<cas_t>(field->Load()),
// linked SCX operations:
&info_fields_[thread_id],
// initial operation state:
OperationState::InProgress);
return scx.Help();
}
template< class UserData >
bool LlxScx<UserData>::TryValidateLink(
const DataRecord_t & field) {
return true; // @TODO
}
// LlxScxRecord
template< class UserData >
LlxScxRecord<UserData>::LlxScxRecord()
: marked_for_finalize_(false) {
scx_op_.Store(&dummy_scx);
}
template< class UserData >
LlxScxRecord<UserData>::LlxScxRecord(
const UserData & user_data)
: user_data_(user_data),
marked_for_finalize_(false) {
scx_op_.Store(&dummy_scx);
}
// internal::ScxRecord
template< class DataRecord >
bool internal::ScxRecord<DataRecord>::Help() {
// We ensure that an SCX S does not change a data record
// while it is frozen for another SCX S'. Instead, S uses
// the information in the SCX record of S' to help S'
// complete, so that the data record can be unfrozen.
typedef embb::containers::internal::FixedSizeList<DataRecord *> dr_list_t;
// Freeze all data records in data_records to protect their
// mutable fields from being changed by other SCXs:
dr_list_t::iterator linked_it = linked_data_records_->begin();
dr_list_t::iterator linked_end = linked_data_records_->end();
for (unsigned int fieldIdx = 0;
linked_it != linked_end;
++linked_it, ++fieldIdx) {
DataRecord * r = *linked_it;
// pointer indexed by r in this->info_fields:
ScxRecord<DataRecord> * rinfo = &info_fields_[fieldIdx];
if (!r->ScxInfo().CompareAndSwap(rinfo, this)) {
if (r->ScxInfo().Load() != this) {
// could not freeze r because it is frozen for
// another SCX:
if (all_frozen_) {
// SCX already completed:
return true;
}
// atomically unfreeze all nodes frozen for this SCX:
state_ = Aborted;
return false;
}
}
}
// finished freezing data records
assert(state_ == InProgress || state_ == Comitted);
// frozen step:
all_frozen_ = true;
// mark step:
dr_list_t::iterator finalize_it = finalize_data_records_->begin();
dr_list_t::iterator finalize_end = finalize_data_records_->end();
for (unsigned int field_idx = finalize_range_.first;
finalize_it != finalize_end;
++finalize_it, ++fieldRangeIdx) {
linked_data_records_[fieldRangeIdx]->MarkForFinalize();
}
// update CAS:
cas_t expected_old_value = old_value_;
field_->CompareAndSwap(expected_old_value, new_value_);
// Commit step.
// Finalizes all r in data_records within finalize range and
// unfreezes all r in data_records outside of finalize range.
// Linearization point of this operation.
state_ = Comitted;
return true;
}
} // namespace primitives
} // namespace containers
} // namespace embb
#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_
/*
* Copyright (c) 2014, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_
#define EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
#include <embb/base/thread_specific_storage.h>
#include <embb/containers/internal/fixed_size_list.h>
namespace embb {
namespace containers {
namespace primitives {
namespace internal {
/**
* SCX operation description. An SCX record contains all information
* required to allow any process to complete a pending SCX operation.
*/
template< class DataRecord >
class ScxRecord {
private:
typedef size_t cas_t;
typedef ScxRecord< DataRecord > self_t;
public:
/**
* Possible states of an LLX/SCX operation.
*/
typedef enum {
Undefined = 0,
Comitted,
Aborted,
InProgress
} OperationState;
public:
/**
* Default constructor, creates sentinel instance of ScxRecord.
*/
ScxRecord()
: linked_data_records_(0),
finalize_data_records_(0)
new_value_(0),
old_value_(0),
scx_ops_(0),
state_(OperationState::Undefined),
all_frozen_(false) {
field_ = 0;
}
/**
* Constructor.
*/
ScxRecord(
embb::containers::internal::FixedSizeList<DataRecord *> & linked_data_records,
embb::containers::internal::FixedSizeList<DataRecord *> & finalize_data_records,
embb::base::Atomic<cas_t> * field,
cas_t new_value,
cas_t old_value,
embb::containers::internal::FixedSizeList<self_t *> * scx_ops,
OperationState operation_state)
: linked_data_records_(&linked_data_records),
finalize_data_records_(&finalize_data_records),
new_value_(new_value),
old_value_(old_value),
scx_ops_(scx_ops),
state_(operation_state),
all_frozen_(false) {
field_ = field;
}
OperationState State() const {
return state_;
}
/**
* Returns true if helped operation has been completed.
*/
bool Help();
private:
/**
* Sequence of load-linked data records for this SCX operation.
* Named 'V' in original publication.
*/
const embb::containers::internal::FixedSizeList<DataRecord *> *
linked_data_records_;
/**
* Sequence of data records to be finalized in this SCX operation.
* Named 'R' in original publication.
*/
const embb::containers::internal::FixedSizeList<DataRecord *> *
finalize_data_records_;
/**
* Pointer to a mutable field of a data record in data_records the
* new value is to be stored.
* Named 'fld' in original publication.
*/
embb::base::Atomic<cas_t> * field_;
/**
* Value to be written in field referenced by field_index.
* Required to be compatible with atomic operations.
*/
cas_t new_value_;
/**
* Value previously read from field referenced by field_index.
* Required to be compatible with atomic operations.
*/
cas_t old_value_;
/**
* List of SCX operation descriptions associated with data records
* linked with this SCX operation.
*/
embb::containers::internal::FixedSizeList<self_t *> scx_ops_;
/**
* Current state of this SCX record.
*/
OperationState state_;
/**
* Whether all fields are currently frozen, initially false.
* Set to true after all data records in data_records V have
* been frozen for the SCX.
*/
bool all_frozen_;
}; /* class ScxRecord */
} // namespace internal
/**
* Wraps user-defined data with fields required for LLX/SCX algorithm.
* Mutable fields must each be contained in a single word.
*/
template< class UserData >
class LlxScxRecord {
private:
typedef LlxScxRecord<UserData> self_t;
typedef internal::ScxRecord<self_t> ScxRecord_t;
typedef typename ScxRecord_t::OperationState OperationState;
/**
* The dummy SCX record always has state = Aborted.
*/
static ScxRecord_t dummy_scx;
public:
/**
* Default constructor.
*/
LlxScxRecord();
/**
* Constructor. Creates an instance of \c DataRecord_t wrapping a user
* data object.
*/
LlxScxRecord(const UserData & user_data);
/**
* Copy constructor
*/
LlxScxRecord(const LlxScxRecord & other)
: user_data_(other.user_data_) {
scx_op_.Store(other.scx_info_.Load());
marked_for_finalize_ = other.marked_for_finalize_;
}
/**
* Assignment operator.
*/
LlxScxRecord & operator=(const LlxScxRecord & rhs) {
user_data_ = rhs.user_data_;
scx_op_.Store(rhs.scx_info_.Load());
marked_for_finalize_ = rhs.marked_for_finalize_;
}
/**
* Destructor.
*/
~LlxScxRecord() {
}
/**
* Returns list of \c NumMutableElements mutable fields in this
* data record.
*/
UserData & Data() {
return user_data_;
}
UserData * operator*() {
return &user_data_;
}
UserData * operator->() {
return &user_data_;
}
/**
* While this SCX is active, the info field acts as a kind of lock
* on the data record, granting exclusive access to this SCX, rather
* than to a process.
* A data record r is frozen for an SCX-record U if r.info points to
* U and either U.state is InProgress, or U.state is Committed and r
* is marked.
* While a data record r is frozen for an SCX record U, a mutable
* field f of r can be changed only if f is the field pointed to by
* U.fld, and it can only be changed by a process helping the SCX
* that created U.
*/
inline bool IsFrozenFor(const ScxRecord_t & scx) const {
return scx_op_ == &scx &&
(scx.State() == InProgress ||
(scx.State() == Comitted && marked_));
}
/**
* Returns the pointer to the SCX record holding exclusive access to
* this data record.
*/
inline embb::base::Atomic<ScxRecord_t *> & ScxInfo() {
return scx_op_;
}
/**
* Mark this data record for finalizing.
*/
inline void MarkForFinalize() {
marked_for_finalize_ = true;
}
/**
* Mark this data record for finalizing.
*/
inline bool IsMarkedForFinalize() const {
return marked_for_finalize_;
}
private:
/**
* Instance of the user-defined data type containing mutable
* fields.
*/
UserData user_data_;
/**
* Pointer to SCX record that describes the last SCX that accessed this
* data record, initialized with dummy SCX record.
*/
embb::base::Atomic<ScxRecord_t *> scx_op_;
/**
* Marked flag, whether this data record has been finalized.
* The marked bit is initially false and only ever changes from false
* to true.
*/
bool marked_for_finalize_;
}; // class LlxScxRecord
/**
* Multiword LL/SC
*
* Implementation of the LLX/STX primitive as presented in
* "Pragmatic Primitives for Non-blocking Data Structures"
* (Brown et al., 2013).
*
* \tparam MaxLinks Maximum number of active LL-dependencies per thread
* \tparam UserData Type containing mutable fields
*/
template< class UserData >
class LlxScx {
private:
typedef size_t cas_t;
typedef LlxScxRecord<UserData> DataRecord_t;
typedef internal::ScxRecord< LlxScxRecord<UserData> > ScxRecord_t;
typedef typename ScxRecord_t::OperationState OperationState;
public:
/**
* Constructs a new instance of LlxScx.
*/
LlxScx(
size_t max_links
/**< [IN] Maximum number of links depending on a single SCX operation */
);
/**
* Destructor, frees memory.
*/
~LlxScx();
/**
* Tentatively performs an LLX (extended load-linked) operation on given
* LLX/SCX data record.
* Returns true and stores result in given reference variable on success,
* otherwise returns false.
*/
bool TryLoadLinked(
DataRecord_t * const data_record,
/**< [IN] Pointer to data record to load */
DataRecord_t & data,
/**< [OUT] Atomic snapshot of \c NumMutableFields fields in data
record at given index */
bool & finalized
/**< [OUT] Indicating whether requested fields have been finalized */
);
/**
* Tentatively performs a single-record Store-Conditional operation on
* given LLX/SCX data record.
* Returns true if the given value has been stored successfully, otherwise
* false.
*/
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps
/**< [IN] Data records to be finalized in this store operation */
);
/**
* Performs a VLX (extended validate link) operation on given LLX data
* record.
* Returns true if the calling thread's link obtained by its most recent
* invocation of SCX is still valid.
*
* Precondition:
* - for each data record r in data_records, the active thread
* has performed and r.LLX linked to this VLX.
*/
bool TryValidateLink(
const DataRecord_t & data_record
/**< [IN] Linked data record to validate */
);
private:
typedef struct {
DataRecord_t * data_record;
ScxRecord_t * scx_record;
UserData user_data;
} LlxResult;
/**
* Resolves the calling thread's Id.
*/
unsigned int ThreadId();
/**
* Maximum number of active links created via TryLoadLinked per thread.
*/
size_t max_links_;
/**
* Shared table containing for each r in V, a copy of r's info
* value in this thread's local table of LLX results.
*
* thread_id -> {
* r_1 in V -> *ScxRecord(thread_llx_results_[r_1].data_record.ScxInfo()),
* ...
* r_i in V -> *ScxRecord(thread_llx_results_[r_i].data_record.ScxInfo())
* }
*/
embb::containers::internal::FixedSizeList<ScxRecord_t> * info_fields_;
/**
* Thread-specific list of LLX results performed by the thread.
*/
embb::base::ThreadSpecificStorage<
embb::containers::internal::FixedSizeList<LlxResult> >
thread_llx_results_;
/**
* Prevent default construction.
*/
LlxScx();
/**
* Prevent copy construction.
*/
LlxScx(const LlxScx &);
/**
* Prevent assignment.
*/
LlxScx & operator=(const LlxScx &);
};
} // namespace primitives
} // namespace containers
} // namespace embb
#include <embb/containers/internal/primitives/llx_scx-inl.h>
#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_
\ No newline at end of file
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <llx_scx_test.h>
#include <embb/containers/internal/fixed_size_list.h>
#include <embb/containers/primitives/llx_scx.h>
namespace embb {
namespace containers {
namespace test {
using embb::containers::internal::FixedSizeList;
using embb::containers::primitives::LlxScxRecord;
using embb::containers::primitives::LlxScx;
LlxScxTest::LlxScxTest() :
num_threads_(static_cast<int>
(partest::TestSuite::GetDefaultNumThreads())) {
CreateUnit("SerialTest").Add(&LlxScxTest::SerialTest, this);
}
void LlxScxTest::SerialTest() {
typedef LlxScxTest::Node Node;
// Global:
LlxScx<Node> llxscx(3);
// Multiset { a, b, b, c }
Node n1(1, 'a');
Node n2(2, 'b');
Node n3(1, 'c');
// V = { dr1, dr2, dr3 }
// R = { dr1, dr2 }
LlxScxRecord<Node> dr1(n1);
LlxScxRecord<Node> dr2(n2);
LlxScxRecord<Node> dr3(n3);
dr1->next_.Store(&dr2);
dr2->next_.Store(&dr3);
// Thread-local:
LlxScxRecord<Node> l1, l2, l3;
bool finalized;
PT_ASSERT(llxscx.TryLoadLinked(&dr1, l1, finalized));
PT_ASSERT(!finalized);
PT_ASSERT(llxscx.TryLoadLinked(&dr2, l2, finalized));
PT_ASSERT(!finalized);
PT_ASSERT(llxscx.TryLoadLinked(&dr3, l3, finalized));
PT_ASSERT(!finalized);
FixedSizeList< LlxScxRecord<Node> * >
linked_deps(3);
linked_deps.PushBack(&dr1);
linked_deps.PushBack(&dr2);
linked_deps.PushBack(&dr3);
FixedSizeList< LlxScxRecord<Node> * >
finalize_deps(2);
finalize_deps.PushBack(&dr2);
finalize_deps.PushBack(&dr3);
typedef LlxScxRecord<Node> * field_t;
LlxScxRecord<Node> new_node(n3);
PT_ASSERT(
llxscx.TryStoreConditional<LlxScxRecord<Node> *>(
&n2.next_, // fld: field to update
&new_node, // new value
linked_deps, // V: dependencies, must be LL'd before
finalize_deps // R: Subsequence of V to be finalized
));
}
} // namespace test
} // namespace containers
} // namespace embb
/*
* Copyright (c) 2014-2015, Siemens AG. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CONTAINERS_CPP_TEST_LLX_SCX_TEST_H_
#define CONTAINERS_CPP_TEST_LLX_SCX_TEST_H_
#include <partest/partest.h>
#include <embb/containers/primitives/llx_scx.h>
namespace embb {
namespace containers {
namespace test {
class LlxScxTest : public partest::TestCase {
private:
class Node {
public:
typedef primitives::LlxScxRecord<Node> * node_ptr_t;
private:
char value_;
public:
embb::base::Atomic<primitives::LlxScxRecord<Node> *> next_;
embb::base::Atomic<int> count_;
public:
Node()
: value_(-1) {
next_.Store(reinterpret_cast<node_ptr_t>(0));
count_.Store(0);
}
Node(int count, char value)
: value_(value) {
count_.Store(count);
next_.Store(reinterpret_cast<node_ptr_t>(0));
}
Node(int count, char value, node_ptr_t const next_node)
: value_(value) {
count_.Store(count);
next_.Store(next_node);
}
Node & operator=(const Node & rhs) {
count_.Store(rhs.count_.Load());
next_.Store(rhs.next_.Load());
value_ = rhs.value_;
}
};
public:
/**
* Adds test methods.
*/
LlxScxTest();
private:
void SerialTest();
int num_threads_;
};
} // namespace test
} // namespace containers
} // namespace embb
#endif // CONTAINERS_CPP_TEST_LLX_SCX_TEST_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment