From fb4b4604a4c70acb3bcb9b196eb308494ab23ea5 Mon Sep 17 00:00:00 2001 From: Tobias Fuchs Date: Fri, 27 Mar 2015 03:36:16 +0100 Subject: [PATCH] containers_cpp: added specialization of LLX/SCX for pointer types, added another simple unit test --- containers_cpp/include/embb/containers/internal/llx_scx-inl.h | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/llx_scx.h | 685 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h | 302 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- containers_cpp/include/embb/containers/primitives/llx_scx.h | 474 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ containers_cpp/test/llx_scx_test.cc | 53 +++++++++++++++++++++++++++++++++++++++++++---------- containers_cpp/test/llx_scx_test.h | 11 ++++++----- 6 files changed, 1065 insertions(+), 791 deletions(-) create mode 100644 containers_cpp/include/embb/containers/internal/llx_scx-inl.h create mode 100644 containers_cpp/include/embb/containers/internal/llx_scx.h delete mode 100644 containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h delete mode 100644 containers_cpp/include/embb/containers/primitives/llx_scx.h diff --git a/containers_cpp/include/embb/containers/internal/llx_scx-inl.h b/containers_cpp/include/embb/containers/internal/llx_scx-inl.h new file mode 100644 index 0000000..5bda60d --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/llx_scx-inl.h @@ -0,0 +1,331 @@ +/* + * 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_INTERNAL_LLX_SCX_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace internal { + +template< typename UserData, typename ValuePool > +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< typename UserData, typename ValuePool > +LlxScx::LlxScx(size_t max_links) +: max_links_(max_links), + max_threads_(embb::base::Thread::GetThreadsMaxCount()), + scx_record_list_pool_(max_threads_), + scx_record_pool_(max_threads_) { + typedef embb::containers::internal::FixedSizeList + llx_result_list_t; + typedef embb::containers::internal::FixedSizeList + scx_record_list_t; + // Allocate a list of LLX results for every thread: + thread_llx_results_ = static_cast( + embb::base::Allocation::AllocateCacheAligned( + max_threads_ * sizeof(llx_result_list_t*))); + // Using Allocation::New to create every list instance as FixedSizeList + // does not provide a default constructor and Allocation::Allocate does + // not allow constructor arguments. + for (unsigned int thread_idx = 0; thread_idx < max_threads_; ++thread_idx) { + thread_llx_results_[thread_idx] = + embb::base::Allocation::New(max_links_); + } +} + +template< typename UserData, typename ValuePool > +LlxScx::~LlxScx() { + // Delete thread-specific lists of LLX results: + for (unsigned int thread_idx = 0; thread_idx < max_threads_; ++thread_idx) { + embb::base::Allocation::Delete(thread_llx_results_[thread_idx]); + } + // Delete array of list pointers: + embb::base::Allocation::FreeAligned(thread_llx_results_); +} + +template< typename UserData, typename ValuePool > +bool LlxScx::TryLoadLinked( + DataRecord_t * const data_record, + UserData & user_data, + bool & finalized) { + finalized = false; + unsigned int thread_id = ThreadId(); + // 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().Load() == 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_[thread_id]->PushBack(llx_result); + // Set return value: + user_data = user_data_local; + return true; + } + } + if (marked_1 && + (curr_scx->State() == OperationState::Comitted || + (curr_scx->State() == OperationState::InProgress && curr_scx->Help()))) { + // Successfully completed active SCX: + finalized = true; + return false; + } + if (data_record->ScxInfo().Load()->State() == OperationState::InProgress) { + // Help active SCX: + data_record->ScxInfo().Load()->Help(); + } + return false; +} + +template< typename UserData, typename ValuePool > +bool LlxScx::TryLoadLinked( + DataRecord_t * const data_record, + UserData & user_data) { + bool finalized; + return TryLoadLinked(data_record, user_data, finalized); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool LlxScx::TryStoreConditional( + embb::base::Atomic * field, + FieldType value, + embb::containers::internal::FixedSizeList & linked_deps, + embb::containers::internal::FixedSizeList & finalize_deps) { + embb::base::Atomic * cas_field = + reinterpret_cast *>(field); + cas_t cas_value = static_cast(value); + return TryStoreConditionalCAS(cas_field, cas_value, linked_deps, finalize_deps); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool LlxScx::TryStoreConditional( + embb::base::Atomic * field, + FieldType * value, + embb::containers::internal::FixedSizeList & linked_deps, + embb::containers::internal::FixedSizeList & finalize_deps) { + embb::base::Atomic * cas_field = + reinterpret_cast *>(field); + cas_t cas_value = reinterpret_cast(value); + return TryStoreConditionalCAS(cas_field, cas_value, linked_deps, finalize_deps); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool LlxScx::TryStoreConditional( + embb::base::Atomic * field, + FieldType value, + embb::containers::internal::FixedSizeList & linked_deps) { + embb::containers::internal::FixedSizeList finalize_deps(0); + return TryStoreConditional(field, value, linked_deps, finalize_deps); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool LlxScx::TryStoreConditional( + embb::base::Atomic * field, + FieldType * value, + embb::containers::internal::FixedSizeList & linked_deps) { + embb::containers::internal::FixedSizeList finalize_deps(0); + return TryStoreConditional(field, value, linked_deps, finalize_deps); +} + +template< typename UserData, typename ValuePool > +bool LlxScx::TryStoreConditionalCAS( + embb::base::Atomic * cas_field, + cas_t cas_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 scx_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: + scx_op_list_t * info_fields = scx_record_list_pool_.Allocate(max_links_); + dr_list_t::const_iterator it; + dr_list_t::const_iterator end; + end = linked_deps.end(); + // for each r in linked_deps ... + for (it = linked_deps.begin(); it != end; ++it) { + typedef embb::containers::internal::FixedSizeList + llx_result_list; + llx_result_list::iterator l_it = thread_llx_results_[thread_id]->begin(); + llx_result_list::iterator l_end = thread_llx_results_[thread_id]->end(); + // Find LLX result of r in thread-local LLX results: + 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"); + } + // ScxRecord_t scx_op(*(l_it->data_record->ScxInfo().Load())); + info_fields->PushBack( + l_it->data_record->ScxInfo().Load()); + } + // 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 new_scx( + linked_deps, + finalize_deps, + // target field: + cas_field, + // new value: + cas_value, + // old value: + cas_field->Load(), + // linked SCX operations: + info_fields, + // initial operation state: + OperationState::InProgress); + // Allocate from pool as this operation description is global: + ScxRecord_t * scx = scx_record_pool_.Allocate(new_scx); + return scx->Help(); +} + +template< typename UserData, typename ValuePool > +bool LlxScx::TryValidateLink( + const DataRecord_t & field) { + return true; // @TODO +} + +// LlxScxRecord + +template< typename UserData > +LlxScxRecord::LlxScxRecord() +: marked_for_finalize_(false) { + scx_op_.Store(&dummy_scx); +} + +template< typename UserData > +LlxScxRecord::LlxScxRecord( + const UserData & user_data) +: user_data_(user_data), + marked_for_finalize_(false) { + scx_op_.Store(&dummy_scx); +} + +// internal::ScxRecord + +template< typename DataRecord > +bool 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; + typedef embb::containers::internal::FixedSizeList op_list_t; + // Freeze all data records in data_records (i.e. reserve them for this + // SCX operation) 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(); + op_list_t::iterator scx_op_it = scx_ops_->begin(); + op_list_t::iterator scx_op_end = scx_ops_->end(); + for (; linked_it != linked_end && scx_op_it != scx_op_end; + ++linked_it, ++scx_op_it) { + DataRecord * r = *linked_it; + ScxRecord * rinfo_old = *scx_op_it; + // Try to freeze the data record by setting its SCX info field + // to this SCX operation description: + if (!r->ScxInfo().CompareAndSwap(rinfo_old, 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 (see LLX): + state_ = Aborted; + return false; + } + } + else { + // free_scx_ops.PushBack(rinfo_old); + } + } + // 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 (; finalize_it != finalize_end; ++finalize_it) { + (*finalize_it)->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; +} + +template< typename UserData > +ScxRecord< LlxScxRecord > + LlxScxRecord::dummy_scx = + ScxRecord< LlxScxRecord >(); + +} // namespace internal +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_ diff --git a/containers_cpp/include/embb/containers/internal/llx_scx.h b/containers_cpp/include/embb/containers/internal/llx_scx.h new file mode 100644 index 0000000..5235bcc --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/llx_scx.h @@ -0,0 +1,685 @@ +/* + * 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_INTERNAL_LLX_SCX_H_ +#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace internal { + +#ifdef DOXYGEN +/** +* Wraps user-defined data with fields required for LLX/SCX algorithm. +* Mutable fields must each be contained in a single word. +*/ +template< typename UserData > +class LlxScxRecord { + 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); + + /** + * Assignment operator. + */ + LlxScxRecord & operator=(const LlxScxRecord & rhs); + + /** + * Destructor. + */ + ~LlxScxRecord(); + + /** + * Returns user data payload of this LLX/SCX record + */ + UserData & Data(); + + /** + * Allows pointer semantics, returns user data payload of this + * LLX/SCX record + */ + UserData * operator*(); + + /** + * Allows pointer semantics, returns user data payload of this + * LLX/SCX record + */ + UserData * operator->(); + + /** + * Whether this data record is marked for finalizing. + */ + bool IsMarkedForFinalize() const; +}; + +/** + * Multiword LL/SC + * + * Implementation of the LLX/STX primitive as presented in + * "Pragmatic Primitives for Non-blocking Data Structures" + * (Brown et al., 2013). + * + * \tparam UserData Type containing mutable fields + * \tparam ValuePool Type containing mutable fields + */ +template< + typename UserData, + typename ValuePool = embb::containers::LockFreeTreeValuePool< bool, false > +> +class LlxScx { + 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 */ + UserData & data, + /**< [OUT] Atomic snapshot of data record */ + bool & finalized + /**< [OUT] Indicating whether requested fields have been finalized */ + ); + + /** + * 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 */ + UserData & data + /**< [OUT] Atomic snapshot of data record */ + ); + + /** + * 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 */ + ); + + /** + * 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 */ + ); + + /** + * Performs a VLX (extended validate link) operation on given LLX data + * record. + * Before calling this method, the given LLX/SCX record must have been + * linked via \c TryLoadLinked. + * + * \returns True if the calling thread's link obtained by its most recent + * invocation of SCX is still valid. + */ + bool TryValidateLink( + const DataRecord_t & data_record + /**< [IN] Linked data record to validate */ + ); +}; + +#else + +/** + * SCX operation description. An SCX record contains all information + * required to allow any process to complete a pending SCX operation. + * This class is non-public. + */ +template< typename 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::Comitted), + 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 */ + +/** + * Wraps user-defined data with fields required for LLX/SCX algorithm. + * Mutable fields must each be contained in a single word. + */ +template< typename 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_op_.Load()); + marked_for_finalize_ = other.marked_for_finalize_; + } + + /** + * Assignment operator. + */ + LlxScxRecord & operator=(const LlxScxRecord & rhs) { + if (this != &rhs) { + user_data_ = rhs.user_data_; + scx_op_.Store(rhs.scx_op_.Load()); + marked_for_finalize_ = rhs.marked_for_finalize_; + } + return *this; + } + + /** + * 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_; + } + + /** + * 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 UserData Type containing mutable fields + * \tparam ValuePool Type containing mutable fields + */ +template< + typename UserData, + typename ValuePool = embb::containers::LockFreeTreeValuePool< bool, false > +> +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 */ + UserData & data, + /**< [OUT] Atomic snapshot of data record */ + bool & finalized + /**< [OUT] Indicating whether requested fields have been finalized */ + ); + + /** + * 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 */ + UserData & data + /**< [OUT] Atomic snapshot of data record */ + ); + + /** + * 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 */ + ); + + 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 */ + ); + + /** + * 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 */ + ); + + 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 */ + ); + + /** + * Performs a VLX (extended validate link) operation on given LLX data + * record. + * Before calling this method, the given LLX/SCX record must have been + * linked via \c TryLoadLinked. + * + * \returns True if the calling thread's link obtained by its most recent + * invocation of SCX is still valid. + */ + 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_; + + /** + * Maximum number of threads engaging in operations on this LLX/SCX instance. + */ + unsigned int max_threads_; + + /** + * Shared table containing for each r in V, a copy of r's info value in this + * thread's local table of LLX results. + */ + embb::containers::ObjectPool< + embb::containers::internal::FixedSizeList, ValuePool > + scx_record_list_pool_; + + /** + * Pool for SCX records allocated in TryStoreConditional + */ + embb::containers::ObjectPool< ScxRecord_t, ValuePool > scx_record_pool_; + + /** + * Thread-specific list of LLX results performed by the thread. + */ + embb::containers::internal::FixedSizeList ** + thread_llx_results_; + + /** + * Prevent default construction. + */ + LlxScx(); + + /** + * Prevent copy construction. + */ + LlxScx(const LlxScx &); + + /** + * Prevent assignment. + */ + LlxScx & operator=(const LlxScx &); + + /** + * Actual implementation of StoreConditional operating on unified fields/values + * of type cas_t. + */ + bool TryStoreConditionalCAS( + embb::base::Atomic * cas_field, + /**< [IN] Pointer to the field the value will be stored into */ + cas_t cas_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 */ + ); + + template < typename FieldType > + cas_t ToCASValue(FieldType value) { + return static_cast(value); + } + + template < typename FieldType > + cas_t ToCASValue(FieldType * value) { + return reinterpret_cast(value); + } +}; + +#endif // DOXYGEN +} // namespace primitives +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_ \ No newline at end of file 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 deleted file mode 100644 index 03f5054..0000000 --- a/containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - * 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 { - -template< typename UserData, typename ValuePool > -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< typename UserData, typename ValuePool > -LlxScx::LlxScx(size_t max_links) -: max_links_(max_links), - max_threads_(embb::base::Thread::GetThreadsMaxCount()), - scx_record_list_pool_(max_threads_), - scx_record_pool_(max_threads_) { - typedef embb::containers::internal::FixedSizeList - llx_result_list_t; - typedef embb::containers::internal::FixedSizeList - scx_record_list_t; - // Allocate a list of LLX results for every thread: - thread_llx_results_ = static_cast( - embb::base::Allocation::AllocateCacheAligned( - max_threads_ * sizeof(llx_result_list_t*))); - // Using Allocation::New to create every list instance as FixedSizeList - // does not provide a default constructor and Allocation::Allocate does - // not allow constructor arguments. - for (unsigned int thread_idx = 0; thread_idx < max_threads_; ++thread_idx) { - thread_llx_results_[thread_idx] = - embb::base::Allocation::New(max_links_); - } -} - -template< typename UserData, typename ValuePool > -LlxScx::~LlxScx() { - // Delete thread-specific lists of LLX results: - for (unsigned int thread_idx = 0; thread_idx < max_threads_; ++thread_idx) { - embb::base::Allocation::Delete(thread_llx_results_[thread_idx]); - } - // Delete array of list pointers: - embb::base::Allocation::FreeAligned(thread_llx_results_); -} - -template< typename UserData, typename ValuePool > -bool LlxScx::TryLoadLinked( - DataRecord_t * const data_record, - DataRecord_t & user_data, - bool & finalized) { - finalized = false; - unsigned int thread_id = ThreadId(); - // 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().Load() == 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_[thread_id]->PushBack(llx_result); - // Set return value: - user_data = user_data_local; - return true; - } - } - if (marked_1 && - (curr_scx->State() == OperationState::Comitted || - (curr_scx->State() == OperationState::InProgress && curr_scx->Help()))) { - // Successfully completed active SCX: - finalized = true; - return false; - } - if (data_record->ScxInfo().Load()->State() == OperationState::InProgress) { - // Help active SCX: - data_record->ScxInfo().Load()->Help(); - } - return false; -} - -template< typename UserData, typename ValuePool > -bool LlxScx::TryLoadLinked( - DataRecord_t * const data_record, - DataRecord_t & user_data) { - bool finalized; - return TryLoadLinked(data_record, user_data, finalized); -} - -template< typename UserData, typename ValuePool > -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 scx_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: - scx_op_list_t * info_fields = scx_record_list_pool_.Allocate(max_links_); - dr_list_t::const_iterator it; - dr_list_t::const_iterator end; - end = linked_deps.end(); - // for each r in linked_deps ... - for (it = linked_deps.begin(); it != end; ++it) { - typedef embb::containers::internal::FixedSizeList - llx_result_list; - llx_result_list::iterator l_it = thread_llx_results_[thread_id]->begin(); - llx_result_list::iterator l_end = thread_llx_results_[thread_id]->end(); - // Find LLX result of r in thread-local LLX results: - 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"); - } - // ScxRecord_t scx_op(*(l_it->data_record->ScxInfo().Load())); - info_fields->PushBack( - l_it->data_record->ScxInfo().Load()); - } - // 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 new_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, - // initial operation state: - OperationState::InProgress); - // Allocate from pool as this operation description is global: - ScxRecord_t * scx = scx_record_pool_.Allocate(new_scx); - return scx->Help(); -} - -template< typename UserData, typename ValuePool > -template< typename FieldType > -bool LlxScx::TryStoreConditional( - embb::base::Atomic * field, - FieldType value, - embb::containers::internal::FixedSizeList & linked_deps) { - embb::containers::internal::FixedSizeList finalize_deps(0); - return TryStoreConditional(field, value, linked_deps, finalize_deps); -} - -template< typename UserData, typename ValuePool > -bool LlxScx::TryValidateLink( - const DataRecord_t & field) { - return true; // @TODO -} - -// LlxScxRecord - -template< typename UserData > -LlxScxRecord::LlxScxRecord() -: marked_for_finalize_(false) { - scx_op_.Store(&dummy_scx); -} - -template< typename UserData > -LlxScxRecord::LlxScxRecord( - const UserData & user_data) -: user_data_(user_data), - marked_for_finalize_(false) { - scx_op_.Store(&dummy_scx); -} - -// internal::ScxRecord - -template< typename 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; - typedef embb::containers::internal::FixedSizeList op_list_t; - // Freeze all data records in data_records (i.e. reserve them for this - // SCX operation) 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(); - op_list_t::iterator scx_op_it = scx_ops_->begin(); - op_list_t::iterator scx_op_end = scx_ops_->end(); - for (; linked_it != linked_end && scx_op_it != scx_op_end; - ++linked_it, ++scx_op_it) { - DataRecord * r = *linked_it; - ScxRecord * rinfo_old = *scx_op_it; - // Try to freeze the data record by setting its SCX info field - // to this SCX operation description: - if (!r->ScxInfo().CompareAndSwap(rinfo_old, 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 (see LLX): - state_ = Aborted; - return false; - } - } - else { - // free_scx_ops.PushBack(rinfo_old); - } - } - // 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 (; finalize_it != finalize_end; ++finalize_it) { - (*finalize_it)->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; -} - -template< typename UserData > -internal::ScxRecord< LlxScxRecord > - LlxScxRecord::dummy_scx = - internal::ScxRecord< LlxScxRecord >(); - -} // 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 deleted file mode 100644 index d6476cb..0000000 --- a/containers_cpp/include/embb/containers/primitives/llx_scx.h +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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 -#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< typename 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::Comitted), - 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< typename 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_op_.Load()); - marked_for_finalize_ = other.marked_for_finalize_; - } - - /** - * Assignment operator. - */ - LlxScxRecord & operator=(const LlxScxRecord & rhs) { - if (this != &rhs) { - user_data_ = rhs.user_data_; - scx_op_.Store(rhs.scx_op_.Load()); - marked_for_finalize_ = rhs.marked_for_finalize_; - } - return *this; - } - - /** - * 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_; - } - - /** - * 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 UserData Type containing mutable fields - * \tparam ValuePool Type containing mutable fields - */ -template< - typename UserData, - typename ValuePool = embb::containers::LockFreeTreeValuePool< bool, false > -> -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 data record */ - bool & finalized - /**< [OUT] Indicating whether requested fields have been finalized */ - ); - - /** - * 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 data record */ - ); - - /** - * 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 */ - ); - - /** - * 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 */ - ); - - /** - * 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_; - - /** - * Maximum number of threads engaging in operations on this LLX/SCX instance. - */ - unsigned int max_threads_; - - /** - * Shared table containing for each r in V, a copy of r's info - * value in this thread's local table of LLX results. - */ - embb::containers::ObjectPool< - embb::containers::internal::FixedSizeList, ValuePool > - scx_record_list_pool_; - - embb::containers::ObjectPool< ScxRecord_t, ValuePool > scx_record_pool_; - - /** - * Thread-specific list of LLX results performed by the thread. - */ - 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 index 0275a0d..3599511 100644 --- a/containers_cpp/test/llx_scx_test.cc +++ b/containers_cpp/test/llx_scx_test.cc @@ -26,15 +26,15 @@ #include #include -#include +#include namespace embb { namespace containers { namespace test { using embb::containers::internal::FixedSizeList; -using embb::containers::primitives::LlxScxRecord; -using embb::containers::primitives::LlxScx; +using embb::containers::internal::LlxScxRecord; +using embb::containers::internal::LlxScx; LlxScxTest::LlxScxTest() : num_threads_( @@ -42,7 +42,8 @@ LlxScxTest::LlxScxTest() : llxscx_(3), tail(0, '-'), head(0, '-', Node::node_ptr_t(&tail)) { - CreateUnit("SerialTest").Add(&LlxScxTest::SerialTest, this); + CreateUnit("SerialArrayTest").Add(&LlxScxTest::SerialArrayTest, this); + CreateUnit("SerialListTest").Add(&LlxScxTest::SerialListTest, this); CreateUnit("ParallelTest").Add(&LlxScxTest::ParallelTest, this); } @@ -57,7 +58,39 @@ void LlxScxTest::ParallelTest() { // Threads try to append n nodes to a linked list in parallel } -void LlxScxTest::SerialTest() { +void LlxScxTest::SerialArrayTest() { + typedef int Payload; + typedef embb::base::Atomic AtomicField; + // LLX/SCX with maximum of 3 active load-links in every thread: + LlxScx llxscx(3); + + // Atomic not assignable, TryStoreConditional requires + // a specialization for atomics that uses a.Store(b.Load()). + AtomicField field(23); + // Initialize + LlxScxRecord< Payload > * my_list = + new LlxScxRecord[10]; + for (int i = 0; i != 10; ++i) { + my_list[i] = i; + } + + Payload l1, l2; + PT_ASSERT(llxscx.TryLoadLinked(&my_list[0], l1)); + PT_ASSERT(llxscx.TryLoadLinked(&my_list[5], l2)); + + FixedSizeList< LlxScxRecord * > + links(2); + links.PushBack(&my_list[0]); + links.PushBack(&my_list[5]); + + // Try to store new value depending on links: + size_t a = 42; + PT_ASSERT(llxscx.TryStoreConditional(&field, a, links)); + // New value should have been changed successfully: + PT_ASSERT_EQ(field.Load(), a); +} + +void LlxScxTest::SerialListTest() { typedef LlxScxTest::Node Node; // Global: LlxScx llxscx(3); @@ -77,17 +110,17 @@ void LlxScxTest::SerialTest() { dr2->next_.Store(&dr3); // Thread-local: - LlxScxRecord l1, l2, l3; + Node l1, l2, l3; bool finalized; PT_ASSERT(llxscx.TryLoadLinked(&dr1, l1, finalized)); PT_ASSERT(!finalized); - PT_ASSERT_EQ(l1->value_, dr1->value_); + PT_ASSERT_EQ(l1.value_, dr1->value_); PT_ASSERT(llxscx.TryLoadLinked(&dr2, l2, finalized)); PT_ASSERT(!finalized); - PT_ASSERT_EQ(l2->value_, dr2->value_); + PT_ASSERT_EQ(l2.value_, dr2->value_); PT_ASSERT(llxscx.TryLoadLinked(&dr3, l3, finalized)); PT_ASSERT(!finalized); - PT_ASSERT_EQ(l3->value_, dr3->value_); + PT_ASSERT_EQ(l3.value_, dr3->value_); FixedSizeList< LlxScxRecord * > linked_deps(3); @@ -103,7 +136,7 @@ void LlxScxTest::SerialTest() { LlxScxRecord new_node(n3); PT_ASSERT( - llxscx.TryStoreConditional *>( + llxscx.TryStoreConditional( &n2.next_, // fld: field to update &new_node, // new value linked_deps, // V: dependencies, must be LL'd before diff --git a/containers_cpp/test/llx_scx_test.h b/containers_cpp/test/llx_scx_test.h index 3228c1f..44def8c 100644 --- a/containers_cpp/test/llx_scx_test.h +++ b/containers_cpp/test/llx_scx_test.h @@ -28,7 +28,7 @@ #define CONTAINERS_CPP_TEST_LLX_SCX_TEST_H_ #include -#include +#include namespace embb { namespace containers { @@ -39,10 +39,10 @@ class LlxScxTest : public partest::TestCase { private: class Node { public: - typedef primitives::LlxScxRecord * node_ptr_t; + typedef internal::LlxScxRecord * node_ptr_t; public: - embb::base::Atomic *> next_; + embb::base::Atomic *> next_; embb::base::Atomic count_; char value_; @@ -88,11 +88,12 @@ class LlxScxTest : public partest::TestCase { LlxScxTest(); private: - void SerialTest(); + void SerialArrayTest(); + void SerialListTest(); void ParallelTest(); int num_threads_; - primitives::LlxScx llxscx_; + internal::LlxScx llxscx_; Node tail; Node head; }; -- libgit2 0.26.0