diff --git a/containers_cpp/include/embb/containers/internal/llx_scx-inl.h b/containers_cpp/include/embb/containers/internal/llx_scx-inl.h index 5506f44..eb6d1a8 100644 --- a/containers_cpp/include/embb/containers/internal/llx_scx-inl.h +++ b/containers_cpp/include/embb/containers/internal/llx_scx-inl.h @@ -31,8 +31,6 @@ #include #include #include -#include -#include namespace embb { namespace containers { @@ -124,61 +122,7 @@ bool LlxScx::TryLoadLinked( } 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, @@ -193,28 +137,40 @@ bool LlxScx::TryStoreConditionalCAS( // 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: + // a copy of r's info value in this threads local table of LLX results. + // Will be freed in Help() once the SCX operation has been completed. scx_op_list_t * info_fields = scx_record_list_pool_.Allocate(max_links_); + if (info_fields == NULL) { + EMBB_THROW(embb::base::ErrorException, + "Could not allocate SCX record list"); + } dr_list_t::const_iterator it; dr_list_t::const_iterator end; end = linked_deps.end(); - // for each r in linked_deps ... + // Copy SCX operation of all LLX results of link dependencies into a list. + // 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(); + llx_result_list::iterator llx_result_it; + llx_result_list::iterator llx_result_end; + llx_result_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) { + for (llx_result_it = thread_llx_results_[thread_id]->begin(); + llx_result_it != llx_result_end && + llx_result_it->data_record != *it; + ++llx_result_it); + if (llx_result_it == llx_result_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"); + "Missing preceding LLX on a data record used as SCX dependency"); } + // Copy SCX operation from LLX result of link dependency into list: info_fields->PushBack( - l_it->data_record->ScxInfo().Load()); + llx_result_it->data_record->ScxInfo().Load()); } + // Clear thread-local list of LLX results thread_llx_results_[thread_id]->clear(); // Announce SCX operation. Lists linked_deps and finalize_dep are // guaranteed to remain on the stack until this announced operation @@ -267,24 +223,24 @@ bool LlxScx::Help( 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, scx)) { + if (r->ScxInfo().CompareAndSwap(rinfo_old, scx)) { + // Do not try to delete the sentinel scx record: + if (rinfo_old != &DataRecord_t::DummyScx) { + scx_record_list_pool_.Free(rinfo_old->scx_ops_); + scx_record_pool_.Free(rinfo_old); + } + } else { if (r->ScxInfo().Load() != scx) { // could not freeze r because it is frozen for another SCX: if (scx->all_frozen_) { - // SCX already completed: + // SCX already completed by any other thread: return true; } // Atomically unfreeze all nodes frozen for this SCX (see LLX): scx->state_ = ScxRecord_t::Aborted; return false; } - } else { - // Do not try to delete the sentinel scx record: - if (rinfo_old->field_ != 0) { - scx_record_list_pool_.Free(rinfo_old->scx_ops_); - scx_record_pool_.Free(rinfo_old); - } - } + } } // finished freezing data records assert(scx->state_ == ScxRecord_t::InProgress || @@ -313,7 +269,7 @@ bool LlxScx::Help( template< typename UserData > LlxScxRecord::LlxScxRecord() : marked_for_finalize_(false) { - scx_op_.Store(&dummy_scx); + scx_op_.Store(&DummyScx); } template< typename UserData > @@ -321,13 +277,13 @@ LlxScxRecord::LlxScxRecord( const UserData & user_data) : user_data_(user_data), marked_for_finalize_(false) { - scx_op_.Store(&dummy_scx); + scx_op_.Store(&DummyScx); } template< typename UserData > ScxRecord< LlxScxRecord > - LlxScxRecord::dummy_scx = - ScxRecord< LlxScxRecord >(); +LlxScxRecord::DummyScx = + ScxRecord< LlxScxRecord >(); } // namespace internal } // namespace containers diff --git a/containers_cpp/include/embb/containers/internal/llx_scx.h b/containers_cpp/include/embb/containers/internal/llx_scx.h index 3805d58..f246f32 100644 --- a/containers_cpp/include/embb/containers/internal/llx_scx.h +++ b/containers_cpp/include/embb/containers/internal/llx_scx.h @@ -39,171 +39,6 @@ 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. @@ -335,10 +170,11 @@ class LlxScxRecord { typedef internal::ScxRecord ScxRecord_t; typedef typename ScxRecord_t::OperationState OperationState; + public: /** * The dummy SCX record always has state = Aborted. */ - static ScxRecord_t dummy_scx; + static ScxRecord_t DummyScx; public: /** @@ -470,9 +306,9 @@ template< > class LlxScx { - private: + private: typedef size_t cas_t; - typedef LlxScxRecord DataRecord_t; + typedef LlxScxRecord< UserData > DataRecord_t; typedef internal::ScxRecord< LlxScxRecord > ScxRecord_t; typedef typename ScxRecord_t::OperationState OperationState; @@ -506,74 +342,24 @@ class 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 */ - ); - - /** + * Actual implementation of StoreConditional operating on unified fields/values + * of type cas_t. * 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, + embb::base::Atomic * cas_field, /**< [IN] Pointer to the field the value will be stored into */ - FieldType * value, + 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 */ - ); - - /** - * 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. @@ -589,7 +375,10 @@ class LlxScx { ); private: - + /** + * Result of a Load-Linked operation, to be stored in thread-specific + * array range within thread_llx_results_. + */ typedef struct { DataRecord_t * data_record; ScxRecord_t * scx_record; @@ -600,6 +389,11 @@ class LlxScx { * Resolves the calling thread's Id. */ unsigned int ThreadId(); + + /** + * Help complete an SCX operation referenced by the given SCX record + */ + bool Help(ScxRecord_t * scx); /** * Maximum number of active links created via TryLoadLinked per thread. @@ -616,7 +410,7 @@ class LlxScx { * thread's local table of LLX results. */ embb::containers::ObjectPool< - embb::containers::internal::FixedSizeList, ValuePool > + embb::containers::internal::FixedSizeList< ScxRecord_t * >, ValuePool > scx_record_list_pool_; /** @@ -627,7 +421,7 @@ class LlxScx { /** * Thread-specific list of LLX results performed by the thread. */ - embb::containers::internal::FixedSizeList ** + embb::containers::internal::FixedSizeList< LlxResult > ** thread_llx_results_; /** @@ -645,35 +439,8 @@ class LlxScx { */ LlxScx & operator=(const LlxScx &); - bool Help(ScxRecord_t * scx); - - /** - * 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 diff --git a/containers_cpp/include/embb/containers/internal/multiword_ll_sc-inl.h b/containers_cpp/include/embb/containers/internal/multiword_ll_sc-inl.h new file mode 100644 index 0000000..6acdb3f --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/multiword_ll_sc-inl.h @@ -0,0 +1,145 @@ +/* + * 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_MULTIWORD_LL_SC_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_MULTIWORD_LL_SC_INL_H_ + +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace internal { + +template< typename UserData, typename ValuePool > +MultiwordLLSC::MultiwordLLSC(size_t max_links) +: llx_scx_(max_links) { +} + +template< typename UserData, typename ValuePool > +MultiwordLLSC::~MultiwordLLSC() { +} + +template< typename UserData, typename ValuePool > +bool MultiwordLLSC::TryLoadLinked( + DataRecord_t * const data_record, + UserData & user_data, + bool & finalized) { + return llx_scx_.TryLoadLinked(data_record, user_data, finalized); +} + +template< typename UserData, typename ValuePool > +bool MultiwordLLSC::TryLoadLinked( + DataRecord_t * const data_record, + UserData & user_data) { + bool finalized; + return llx_scx_.TryLoadLinked(data_record, user_data, finalized); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool MultiwordLLSC::TryStoreConditional( + Link & linked_dep, + embb::base::Atomic * field, + FieldType value) { + embb::containers::internal::FixedSizeList linked_deps(1); + linked_deps.PushBack(&linked_dep); + embb::containers::internal::FixedSizeList finalize_deps(0); + embb::base::Atomic * cas_field = + reinterpret_cast *>(field); + cas_t cas_value = ToCASValue(value); + return llx_scx_.TryStoreConditional( + cas_field, cas_value, linked_deps, finalize_deps); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool MultiwordLLSC::TryStoreConditional( + Link & linked_dep_1, + Link & linked_dep_2, + embb::base::Atomic * field, + FieldType value) { + embb::containers::internal::FixedSizeList linked_deps(2); + linked_deps.PushBack(&linked_dep_1); + linked_deps.PushBack(&linked_dep_2); + embb::containers::internal::FixedSizeList finalize_deps(0); + embb::base::Atomic * cas_field = + reinterpret_cast *>(field); + cas_t cas_value = ToCASValue(value); + return llx_scx_.TryStoreConditional( + cas_field, cas_value, linked_deps, finalize_deps); +} + +#if 0 + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool MultiwordLLSC::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 MultiwordLLSC::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 llx_scx_.TryStoreConditional(cas_field, cas_value, linked_deps, finalize_deps); +} + +template< typename UserData, typename ValuePool > +template< typename FieldType > +bool MultiwordLLSC::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); +} + +#endif + +template< typename UserData, typename ValuePool > +bool MultiwordLLSC::TryValidateLink( + const DataRecord_t & field) { + return llx_scx_.TryValidateLink(field); +} + +} // namespace internal +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_MULTIWORD_LL_SC_INL_H_ diff --git a/containers_cpp/include/embb/containers/multiword_ll_sc.h b/containers_cpp/include/embb/containers/multiword_ll_sc.h new file mode 100644 index 0000000..7a43f7d --- /dev/null +++ b/containers_cpp/include/embb/containers/multiword_ll_sc.h @@ -0,0 +1,499 @@ +/* + * 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_MULTIWORD_LL_SC_H_ +#define EMBB_CONTAINERS_MULTIWORD_LL_SC_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace containers { +namespace internal { + +#ifdef DOXYGEN +/** + * 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 MultiwordLLSC { + public: + /** + * Constructs a new instance of LlxScx. + */ + MultiwordLLSC( + size_t max_links + /**< [IN] Maximum number of links depending on a single SCX operation */ + ); + + /** + * Destructor, frees memory. + */ + ~MultiwordLLSC(); + + /** + * 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 + +/** + * 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 MultiwordLLSC { + public: + /** + * Wrapper of user-defined data for LLX/SCX operations + */ + typedef internal::LlxScxRecord Link; + + public: + /** + * Constructs a new instance of LlxScx. + */ + MultiwordLLSC( + size_t max_links + /**< [IN] Maximum number of links depending on a single SCX operation */ + ); + + /** + * Destructor, frees memory. + */ + ~MultiwordLLSC(); + + /** + * 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( + Link * const link, + /**< [IN] Data record to load-link */ + UserData & data, + /**< [OUT] Atomic snapshot of data record at the time the link has been + established */ + 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( + Link * const link, + /**< [IN] Data record to load-link */ + UserData & data + /**< [OUT] Atomic snapshot of data record at the time the link has been + established */ + ); + + /** + * 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( + Link & linked_dep, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value + /**< [IN] Value to store */ + ); + + /** + * 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( + Link & linked_dep_1, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_2, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value + /**< [IN] Value to store */ + ); + + /** + * 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( + Link & linked_dep_1, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_2, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_3, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value + /**< [IN] Value to store */ + ); + + /** + * 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( + Link & linked_dep, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + Link & finalize_dep + /**< [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( + Link & linked_dep_1, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_2, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + Link & finalize_dep + /**< [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( + Link & linked_dep_1, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_2, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_3, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + Link & finalize_dep + /**< [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( + Link & linked_dep, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + Link & finalize_dep_1, + /**< [IN] Data record to be finalized in this store operation */ + Link & finalize_dep_2 + /**< [IN] Data record 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( + Link & linked_dep_1, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_2, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + Link & finalize_dep_1, + /**< [IN] Data record to be finalized in this store operation */ + Link & finalize_dep_2 + /**< [IN] Data record 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( + Link & linked_dep_1, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_2, + /**< [IN] Data record linked to this store operation */ + Link & linked_dep_3, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN,OUT] Pointer to the field the value will be stored into */ + FieldType value, + /**< [IN] Value to store */ + Link & finalize_dep_1, + /**< [IN] Data record to be finalized in this store operation */ + Link & finalize_dep_2 + /**< [IN] Data record to be finalized in this store operation */ + ); + +#if 0 + template< typename FieldType > + bool TryStoreConditional( + Link & linked_dep, + /**< [IN] Data record linked to this store operation */ + embb::base::Atomic * field, + /**< [IN] Pointer to the field the value will be stored into */ + FieldType * value, + /**< [IN] Value to store */ + Link & finalize_dep_1, + /**< [IN] Data record to be finalized in this store operation */ + Link & finalize_dep_2 + /**< [IN] Data record to be finalized in this store operation */ + ); +#endif + + /** + * 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 Link & link + /**< [IN] Linked data record to validate */ + ); + + private: + /** + * Maximum number of threads engaging in operations on this LLX/SCX instance. + */ + unsigned int max_threads_; + + /** + * Core implementation of LLX/SCX algorithm + */ + internal::LlxScx llx_scx_; + + /** + * Prevent default construction. + */ + MultiwordLLSC(); + + /** + * Prevent copy construction. + */ + MultiwordLLSC(const MultiwordLLSC &); + + /** + * Prevent assignment. + */ + MultiwordLLSC & operator=(const MultiwordLLSC &); + + /** + * Convert given value to type compatible with CAS + */ + template < typename FieldType > + inline cas_t ToCASValue(FieldType value) { + // Value punning: memcpy given value to type compatible with CAS + cas_t return_value; + FieldType native_value = value; + memcpy(&return_value, &native_value, sizeof(return_value)); + return return_value; + } + + /** + * Convert value referenced by given pointer to type compatible with CAS + */ + template < typename FieldType > + inline cas_t ToCASValue(FieldType * value) { + // Value punning: memcpy value referenced by given pointer to type + // compatible with CAS + cas_t return_value; + memcpy(&return_value, value, sizeof(return_value)); + return return_value; + } + + template < typename FieldType > + inline void Assign( + const FieldType & source, + FieldType & target) { + source = target; + } + + template < typename FieldType > + inline void Assign( + const embb::base::Atomic & source, + embb::base::Atomic & target) { + target.Store(source.Load()); + } +}; + +#endif // DOXYGEN +} // namespace primitives +} // namespace containers +} // namespace embb + +#include + +#endif // EMBB_CONTAINERS_MULTIWORD_LL_SC_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 44d2b51..26163ef 100644 --- a/containers_cpp/test/llx_scx_test.cc +++ b/containers_cpp/test/llx_scx_test.cc @@ -27,6 +27,7 @@ #include #include #include +// #include namespace embb { namespace containers { @@ -44,11 +45,11 @@ LlxScxTest::LlxScxTest() : head(0, '-'), tail_llx(tail), head_llx(head) { - CreateUnit("SerialArrayTest").Add(&LlxScxTest::SerialArrayTest, this); - CreateUnit("SerialListTest").Add(&LlxScxTest::SerialListTest, this); -// CreateUnit("ParallelTest") -// .Add(&LlxScxTest::ParallelTest, this) -// .Post(&LlxScxTest::ParallelTestPost, this); + CreateUnit("SerialArrayTest") + .Add(&LlxScxTest::SerialArrayTest, this); + CreateUnit("ParallelTest") + .Add(&LlxScxTest::ParallelTest, this) + .Post(&LlxScxTest::ParallelTestPost, this); } void LlxScxTest::ParallelTest() { @@ -56,7 +57,6 @@ void LlxScxTest::ParallelTest() { int return_val = embb_internal_thread_index(&thread_index); if (return_val != EMBB_SUCCESS) EMBB_THROW(embb::base::ErrorException, "Could not get thread id!"); - // Threads try to append n nodes to a linked list in parallel for (char value = 'a'; value <= 'z';) { // Find node to append new element on: @@ -67,25 +67,36 @@ void LlxScxTest::ParallelTest() { next = next->Data().next_; } Node n; - llxscx_.TryLoadLinked(node, n); + bool finalized; + // LLX on node the new element will be appended to: + llxscx_.TryLoadLinked(node, n, finalized); if (n.next_ == next) { - // Pointer still valid after LLX, call SCX(node, node.next, new_node) + // Pointer still valid after LLX, try to append new node internal::FixedSizeList *> linked_deps(1); + internal::FixedSizeList *> finalize_deps(0); linked_deps.PushBack(node); // Create new node: Node new_node(static_cast(thread_index), value); internal::LlxScxRecord * new_node_ptr = new internal::LlxScxRecord(new_node); + // Convert node pointer to size_t: + size_t new_cas_value = reinterpret_cast(new_node_ptr); + // Convert target field pointer to size_t*: + embb::base::Atomic * field_cas_ptr = + reinterpret_cast< embb::base::Atomic * >( + &(node->Data().next_)); + // Call SCX: bool element_inserted = llxscx_.TryStoreConditional( - &(node->Data().next_), - new_node_ptr, - linked_deps); + field_cas_ptr, + new_cas_value, + linked_deps, + finalize_deps); if (element_inserted) { // Value has been added to list, continue with next value ++value; } - } + } } } @@ -109,88 +120,40 @@ void LlxScxTest::SerialArrayTest() { // a specialization for atomics that uses a.Store(b.Load()). AtomicField field(23); - LlxScxRecord< Payload > * my_list = - new LlxScxRecord[10]; - for (int i = 0; i != 10; ++i) { - my_list[i] = i; - } + LlxScxRecord< Payload > r1(100); + LlxScxRecord< Payload > r2(200); + LlxScxRecord< Payload > r3(300); - Payload l1, l2; - PT_ASSERT(llxscx.TryLoadLinked(&my_list[0], l1)); - PT_ASSERT(llxscx.TryLoadLinked(&my_list[5], l2)); + Payload l1, l2, l3; + bool finalized; + PT_ASSERT(llxscx.TryLoadLinked(&r1, l1, finalized)); + PT_ASSERT_EQ(100, l1); + PT_ASSERT(llxscx.TryLoadLinked(&r2, l2, finalized)); + PT_ASSERT_EQ(200, l2); + PT_ASSERT(llxscx.TryLoadLinked(&r3, l3, finalized)); + PT_ASSERT_EQ(300, l3); + // links = { dr1, dr2, dr3 } FixedSizeList< LlxScxRecord * > - links(2); - links.PushBack(&my_list[0]); - links.PushBack(&my_list[5]); + links(3); + links.PushBack(&r1); + links.PushBack(&r2); + links.PushBack(&r3); + + FixedSizeList< LlxScxRecord * > + finalize_links(1); + finalize_links.PushBack(&r3); // Try to store new value depending on links: size_t a = 42; - PT_ASSERT(llxscx.TryStoreConditional(&field, a, links)); + PT_ASSERT(llxscx.TryStoreConditional( + &field, a, + links, + finalize_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); - - // 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: - 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(llxscx.TryLoadLinked(&dr2, l2, finalized)); - PT_ASSERT(!finalized); - PT_ASSERT_EQ(l2.value_, dr2->value_); - PT_ASSERT(llxscx.TryLoadLinked(&dr3, l3, finalized)); - PT_ASSERT(!finalized); - PT_ASSERT_EQ(l3.value_, dr3->value_); - - 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 - )); - // Following LLX calls on finalized data records are - // expected to fail: - PT_ASSERT(!llxscx.TryLoadLinked(&dr2, l2, finalized)); - PT_ASSERT(finalized); - PT_ASSERT(!llxscx.TryLoadLinked(&dr3, l3, finalized)); - PT_ASSERT(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 index 92de8a3..06cb419 100644 --- a/containers_cpp/test/llx_scx_test.h +++ b/containers_cpp/test/llx_scx_test.h @@ -89,7 +89,6 @@ class LlxScxTest : public partest::TestCase { private: void SerialArrayTest(); - void SerialListTest(); void ParallelTest(); void ParallelTestPost(); diff --git a/containers_cpp/test/main.cc b/containers_cpp/test/main.cc index b593efd..448b63b 100644 --- a/containers_cpp/test/main.cc +++ b/containers_cpp/test/main.cc @@ -41,7 +41,6 @@ #include "./hazard_pointer_test.h" #include "./object_pool_test.h" #include "./llx_scx_test.h" -// #include "./multiset_test.h" #define COMMA , @@ -58,7 +57,6 @@ using embb::containers::test::QueueTest; using embb::containers::test::StackTest; using embb::containers::test::ObjectPoolTest; using embb::containers::test::LlxScxTest; -// using embb::containers::test::MultisetTest; PT_MAIN("Data Structures C++") { unsigned int max_threads = static_cast( @@ -75,7 +73,6 @@ PT_MAIN("Data Structures C++") { PT_RUN(ObjectPoolTest< LockFreeTreeValuePool >); PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool >); PT_RUN(LlxScxTest); -//PT_RUN(MultisetTest); - PT_EXPECT(embb_get_bytes_allocated() == 0); + PT_EXPECT_EQ(embb_get_bytes_allocated(), static_cast(0)); } diff --git a/containers_cpp/test/multiset_test.h b/containers_cpp/test/multiset_test.h index 385b90f..fa5352d 100644 --- a/containers_cpp/test/multiset_test.h +++ b/containers_cpp/test/multiset_test.h @@ -28,7 +28,7 @@ #define CONTAINERS_CPP_TEST_MULTISET_TEST_H_ #include -#include +#include namespace embb { namespace containers {