From bc8d07e3f693eb9de509ecb97f65dffe51935c6a Mon Sep 17 00:00:00 2001 From: Tobias Fuchs Date: Wed, 25 Mar 2015 17:01:44 +0100 Subject: [PATCH] containers_cpp: implementing LLX/SCX, using FixedSizeList for list parameters --- containers_cpp/include/embb/containers/internal/fixed_size_list-inl.h | 51 +++++++++++++++++++++++++++++++++------------------ containers_cpp/include/embb/containers/internal/fixed_size_list.h | 61 +++++++++++++++++++++++++++++++++---------------------------- containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h | 354 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/primitives/llx_scx.h | 435 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/llx_scx_test.cc | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/test/llx_scx_test.h | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1047 insertions(+), 46 deletions(-) create mode 100644 containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h create mode 100644 containers_cpp/include/embb/containers/primitives/llx_scx.h create mode 100644 containers_cpp/test/llx_scx_test.cc create mode 100644 containers_cpp/test/llx_scx_test.h diff --git a/containers_cpp/include/embb/containers/internal/fixed_size_list-inl.h b/containers_cpp/include/embb/containers/internal/fixed_size_list-inl.h index 9f2b4eb..54c7918 100644 --- a/containers_cpp/include/embb/containers/internal/fixed_size_list-inl.h +++ b/containers_cpp/include/embb/containers/internal/fixed_size_list-inl.h @@ -30,28 +30,43 @@ namespace embb { namespace containers { namespace internal { + +template< typename ElementT > +FixedSizeList::FixedSizeList(size_t capacity) : + max_size_(capacity), + size_(0) { + elementsArray = static_cast( + embb::base::Allocation::Allocate(sizeof(ElementT) * + max_size_)); +} + template< typename ElementT > -FixedSizeList::FixedSizeList(size_t max_size) : - max_size(max_size), - size(0) { +FixedSizeList::FixedSizeList(const FixedSizeList & other) : + max_size_(other.max_size_), + size_(0) { elementsArray = static_cast( 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::GetSize() const { - return size; + return size_; } template< typename ElementT > inline size_t FixedSizeList::GetMaxSize() const { - return max_size; + return max_size_; } template< typename ElementT > inline void FixedSizeList::clear() { - size = 0; + size_ = 0; } template< typename ElementT > @@ -63,31 +78,31 @@ FixedSizeList::begin() const { template< typename ElementT > typename FixedSizeList::iterator FixedSizeList::end() const { - return &elementsArray[size]; + return &elementsArray[size_]; } template< typename ElementT > FixedSizeList< ElementT > & FixedSizeList::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::PushBack(ElementT const el) { - if (size + 1 > max_size) { +bool FixedSizeList::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::~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 diff --git a/containers_cpp/include/embb/containers/internal/fixed_size_list.h b/containers_cpp/include/embb/containers/internal/fixed_size_list.h index 0f5893d..e4006de 100644 --- a/containers_cpp/include/embb/containers/internal/fixed_size_list.h +++ b/containers_cpp/include/embb/containers/internal/fixed_size_list.h @@ -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 #endif // EMBB_CONTAINERS_INTERNAL_FIXED_SIZE_LIST_H_ diff --git a/containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h b/containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h new file mode 100644 index 0000000..b10be44 --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h @@ -0,0 +1,354 @@ +/* + * 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 +#include +#include +#include +#include +#include + +/** + * 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 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::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::LlxScx(size_t max_links) +: max_links_(max_links) { + typedef embb::containers::internal::FixedSizeList + llx_result_list_t; + typedef embb::containers::internal::FixedSizeList + 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( + embb::base::Allocation::AllocateCacheAligned( + num_threads * sizeof(empty_scx_list))); +} + +template< class UserData > +LlxScx::~LlxScx() { + embb::base::Allocation::FreeAligned(info_fields_); +} + +template< class UserData > +bool LlxScx::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 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::TryStoreConditional( + embb::base::Atomic * field, + FieldType value, + embb::containers::internal::FixedSizeList & linked_deps, + embb::containers::internal::FixedSizeList & finalize_deps) { + typedef embb::containers::internal::FixedSizeList dr_list_t; +//typedef embb::containers::internal::FixedSizeList 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 + 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 *>(field), + // new value: + reinterpret_cast(value), + // old value: + reinterpret_cast(field->Load()), + // linked SCX operations: + &info_fields_[thread_id], + // initial operation state: + OperationState::InProgress); + return scx.Help(); +} + +template< class UserData > +bool LlxScx::TryValidateLink( + const DataRecord_t & field) { + return true; // @TODO +} + +// LlxScxRecord + +template< class UserData > +LlxScxRecord::LlxScxRecord() +: marked_for_finalize_(false) { + scx_op_.Store(&dummy_scx); +} + +template< class UserData > +LlxScxRecord::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::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 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 * 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_ diff --git a/containers_cpp/include/embb/containers/primitives/llx_scx.h b/containers_cpp/include/embb/containers/primitives/llx_scx.h new file mode 100644 index 0000000..e657634 --- /dev/null +++ b/containers_cpp/include/embb/containers/primitives/llx_scx.h @@ -0,0 +1,435 @@ +/* + * 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 +#include +#include +#include + +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 & linked_data_records, + embb::containers::internal::FixedSizeList & finalize_data_records, + embb::base::Atomic * field, + cas_t new_value, + cas_t old_value, + embb::containers::internal::FixedSizeList * 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 * + linked_data_records_; + + /** + * Sequence of data records to be finalized in this SCX operation. + * Named 'R' in original publication. + */ + const embb::containers::internal::FixedSizeList * + 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 * 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 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 self_t; + typedef internal::ScxRecord 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 & 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 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 DataRecord_t; + typedef internal::ScxRecord< LlxScxRecord > 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 * field, + /**< [IN] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + embb::containers::internal::FixedSizeList & linked_deps, + /**< [IN] Data records linked to this store operation */ + embb::containers::internal::FixedSizeList & 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 * info_fields_; + + /** + * Thread-specific list of LLX results performed by the thread. + */ + embb::base::ThreadSpecificStorage< + embb::containers::internal::FixedSizeList > + 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 + +#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_ \ No newline at end of file diff --git a/containers_cpp/test/llx_scx_test.cc b/containers_cpp/test/llx_scx_test.cc new file mode 100644 index 0000000..3e77768 --- /dev/null +++ b/containers_cpp/test/llx_scx_test.cc @@ -0,0 +1,99 @@ +/* + * 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 +#include +#include + +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 + (partest::TestSuite::GetDefaultNumThreads())) { + CreateUnit("SerialTest").Add(&LlxScxTest::SerialTest, this); +} + +void LlxScxTest::SerialTest() { + typedef LlxScxTest::Node Node; + + // Global: + LlxScx 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 dr1(n1); + LlxScxRecord dr2(n2); + LlxScxRecord dr3(n3); + + dr1->next_.Store(&dr2); + dr2->next_.Store(&dr3); + + // Thread-local: + LlxScxRecord 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 * > + linked_deps(3); + linked_deps.PushBack(&dr1); + linked_deps.PushBack(&dr2); + linked_deps.PushBack(&dr3); + FixedSizeList< LlxScxRecord * > + finalize_deps(2); + finalize_deps.PushBack(&dr2); + finalize_deps.PushBack(&dr3); + + typedef LlxScxRecord * field_t; + + LlxScxRecord new_node(n3); + PT_ASSERT( + llxscx.TryStoreConditional *>( + &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 diff --git a/containers_cpp/test/llx_scx_test.h b/containers_cpp/test/llx_scx_test.h new file mode 100644 index 0000000..17e925f --- /dev/null +++ b/containers_cpp/test/llx_scx_test.h @@ -0,0 +1,93 @@ +/* + * 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 +#include + +namespace embb { +namespace containers { +namespace test { + +class LlxScxTest : public partest::TestCase { + + private: + class Node { + public: + typedef primitives::LlxScxRecord * node_ptr_t; + private: + char value_; + + public: + embb::base::Atomic *> next_; + embb::base::Atomic count_; + + public: + Node() + : value_(-1) { + next_.Store(reinterpret_cast(0)); + count_.Store(0); + } + + Node(int count, char value) + : value_(value) { + count_.Store(count); + next_.Store(reinterpret_cast(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_ -- libgit2 0.26.0