diff --git a/containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h b/containers_cpp/include/embb/containers/internal/llx_scx-inl.h similarity index 81% rename from containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h rename to containers_cpp/include/embb/containers/internal/llx_scx-inl.h index 03f5054..5bda60d 100644 --- a/containers_cpp/include/embb/containers/internal/primitives/llx_scx-inl.h +++ b/containers_cpp/include/embb/containers/internal/llx_scx-inl.h @@ -24,25 +24,19 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_ -#define EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_ +#ifndef EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_ -#include +#include #include #include #include #include #include -/** - * Implementation of the LX/STX primitive as presented in - * "Pragmatic Primitives for Non-blocking Data Structures" - * (Brown et al., 2013). - */ - namespace embb { namespace containers { -namespace primitives { +namespace internal { template< typename UserData, typename ValuePool > unsigned int LlxScx::ThreadId() { @@ -89,7 +83,7 @@ LlxScx::~LlxScx() { template< typename UserData, typename ValuePool > bool LlxScx::TryLoadLinked( DataRecord_t * const data_record, - DataRecord_t & user_data, + UserData & user_data, bool & finalized) { finalized = false; unsigned int thread_id = ThreadId(); @@ -132,7 +126,7 @@ bool LlxScx::TryLoadLinked( template< typename UserData, typename ValuePool > bool LlxScx::TryLoadLinked( DataRecord_t * const data_record, - DataRecord_t & user_data) { + UserData & user_data) { bool finalized; return TryLoadLinked(data_record, user_data, finalized); } @@ -144,6 +138,51 @@ bool LlxScx::TryStoreConditional( 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: @@ -184,11 +223,11 @@ bool LlxScx::TryStoreConditional( linked_deps, finalize_deps, // target field: - reinterpret_cast *>(field), + cas_field, // new value: - reinterpret_cast(value), + cas_value, // old value: - reinterpret_cast(field->Load()), + cas_field->Load(), // linked SCX operations: info_fields, // initial operation state: @@ -199,16 +238,6 @@ bool LlxScx::TryStoreConditional( } 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 @@ -233,7 +262,7 @@ LlxScxRecord::LlxScxRecord( // internal::ScxRecord template< typename DataRecord > -bool internal::ScxRecord::Help() { +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' @@ -291,12 +320,12 @@ bool internal::ScxRecord::Help() { } template< typename UserData > -internal::ScxRecord< LlxScxRecord > +ScxRecord< LlxScxRecord > LlxScxRecord::dummy_scx = - internal::ScxRecord< LlxScxRecord >(); + ScxRecord< LlxScxRecord >(); -} // namespace primitives +} // namespace internal } // namespace containers } // namespace embb -#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_ +#endif // EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_ diff --git a/containers_cpp/include/embb/containers/primitives/llx_scx.h b/containers_cpp/include/embb/containers/internal/llx_scx.h similarity index 61% rename from containers_cpp/include/embb/containers/primitives/llx_scx.h rename to containers_cpp/include/embb/containers/internal/llx_scx.h index d6476cb..5235bcc 100644 --- a/containers_cpp/include/embb/containers/primitives/llx_scx.h +++ b/containers_cpp/include/embb/containers/internal/llx_scx.h @@ -24,8 +24,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_ -#define EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_ +#ifndef EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_ +#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_ #include #include @@ -36,13 +36,177 @@ namespace embb { namespace containers { -namespace primitives { - 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 { @@ -163,8 +327,6 @@ class ScxRecord { }; /* 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. @@ -341,7 +503,7 @@ class LlxScx { bool TryLoadLinked( DataRecord_t * const data_record, /**< [IN] Pointer to data record to load */ - DataRecord_t & data, + UserData & data, /**< [OUT] Atomic snapshot of data record */ bool & finalized /**< [OUT] Indicating whether requested fields have been finalized */ @@ -356,7 +518,7 @@ class LlxScx { bool TryLoadLinked( DataRecord_t * const data_record, /**< [IN] Pointer to data record to load */ - DataRecord_t & data + UserData & data /**< [OUT] Atomic snapshot of data record */ ); @@ -378,6 +540,18 @@ class LlxScx { /**< [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. @@ -393,22 +567,31 @@ class LlxScx { 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. - * Returns true if the calling thread's link obtained by its most recent - * invocation of SCX is still valid. + * Before calling this method, the given LLX/SCX record must have been + * linked via \c TryLoadLinked. * - * Precondition: - * - for each data record r in data_records, the active thread - * has performed and r.LLX linked to this VLX. + * \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 { @@ -433,13 +616,16 @@ class LlxScx { 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. + * 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_; /** @@ -463,12 +649,37 @@ class LlxScx { */ 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 +#include -#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_ \ No newline at end of file +#endif // EMBB_CONTAINERS_INTERNAL_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; };