Commit 56a22c45 by Tobias Fuchs

containers_cpp: split LLX/SCX into core implementation and convenience interface

parent 746bd2fc
...@@ -31,8 +31,6 @@ ...@@ -31,8 +31,6 @@
#include <embb/base/thread.h> #include <embb/base/thread.h>
#include <embb/base/atomic.h> #include <embb/base/atomic.h>
#include <embb/base/memory_allocation.h> #include <embb/base/memory_allocation.h>
#include <vector>
#include <stdarg.h>
namespace embb { namespace embb {
namespace containers { namespace containers {
...@@ -124,61 +122,7 @@ bool LlxScx<UserData, ValuePool>::TryLoadLinked( ...@@ -124,61 +122,7 @@ bool LlxScx<UserData, ValuePool>::TryLoadLinked(
} }
template< typename UserData, typename ValuePool > template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::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<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType> * field,
FieldType value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps) {
embb::base::Atomic<cas_t> * cas_field =
reinterpret_cast<embb::base::Atomic<cas_t> *>(field);
cas_t cas_value = static_cast<cas_t>(value);
return TryStoreConditionalCAS(cas_field, cas_value, linked_deps, finalize_deps);
}
template< typename UserData, typename ValuePool >
template< typename FieldType >
bool LlxScx<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType*> * field,
FieldType * value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps) {
embb::base::Atomic<cas_t> * cas_field =
reinterpret_cast<embb::base::Atomic<cas_t> *>(field);
cas_t cas_value = reinterpret_cast<cas_t>(value);
return TryStoreConditionalCAS(cas_field, cas_value, linked_deps, finalize_deps);
}
template< typename UserData, typename ValuePool >
template< typename FieldType >
bool LlxScx<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType> * field,
FieldType value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps) {
embb::containers::internal::FixedSizeList<DataRecord_t *> finalize_deps(0);
return TryStoreConditional(field, value, linked_deps, finalize_deps);
}
template< typename UserData, typename ValuePool >
template< typename FieldType >
bool LlxScx<UserData, ValuePool>::TryStoreConditional( bool LlxScx<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType*> * field,
FieldType * value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps) {
embb::containers::internal::FixedSizeList<DataRecord_t *> finalize_deps(0);
return TryStoreConditional(field, value, linked_deps, finalize_deps);
}
template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::TryStoreConditionalCAS(
embb::base::Atomic<cas_t> * cas_field, embb::base::Atomic<cas_t> * cas_field,
cas_t cas_value, cas_t cas_value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps, embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
...@@ -193,28 +137,40 @@ bool LlxScx<UserData, ValuePool>::TryStoreConditionalCAS( ...@@ -193,28 +137,40 @@ bool LlxScx<UserData, ValuePool>::TryStoreConditionalCAS(
// any I_r was linearized. // any I_r was linearized.
unsigned int thread_id = ThreadId(); unsigned int thread_id = ThreadId();
// Let info_fields be a table in shared memory containing for each r in V, // 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_); 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 it;
dr_list_t::const_iterator end; dr_list_t::const_iterator end;
end = linked_deps.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) { for (it = linked_deps.begin(); it != end; ++it) {
typedef embb::containers::internal::FixedSizeList<LlxResult> typedef embb::containers::internal::FixedSizeList<LlxResult>
llx_result_list; llx_result_list;
llx_result_list::iterator l_it = thread_llx_results_[thread_id]->begin(); llx_result_list::iterator llx_result_it;
llx_result_list::iterator l_end = thread_llx_results_[thread_id]->end(); 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: // Find LLX result of r in thread-local LLX results:
for (; l_it != l_end && l_it->data_record != *it; ++l_it); for (llx_result_it = thread_llx_results_[thread_id]->begin();
if (l_it == l_end) { 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 // Missing LLX result for given linked data record, user did not
// load-link a data record this SCX depends on. // load-link a data record this SCX depends on.
EMBB_THROW(embb::base::ErrorException, 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( 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(); thread_llx_results_[thread_id]->clear();
// Announce SCX operation. Lists linked_deps and finalize_dep are // Announce SCX operation. Lists linked_deps and finalize_dep are
// guaranteed to remain on the stack until this announced operation // guaranteed to remain on the stack until this announced operation
...@@ -267,23 +223,23 @@ bool LlxScx<UserData, ValuePool>::Help( ...@@ -267,23 +223,23 @@ bool LlxScx<UserData, ValuePool>::Help(
ScxRecord<DataRecord_t> * rinfo_old = *scx_op_it; ScxRecord<DataRecord_t> * rinfo_old = *scx_op_it;
// Try to freeze the data record by setting its SCX info field // Try to freeze the data record by setting its SCX info field
// to this SCX operation description: // 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) { if (r->ScxInfo().Load() != scx) {
// could not freeze r because it is frozen for another SCX: // could not freeze r because it is frozen for another SCX:
if (scx->all_frozen_) { if (scx->all_frozen_) {
// SCX already completed: // SCX already completed by any other thread:
return true; return true;
} }
// Atomically unfreeze all nodes frozen for this SCX (see LLX): // Atomically unfreeze all nodes frozen for this SCX (see LLX):
scx->state_ = ScxRecord_t::Aborted; scx->state_ = ScxRecord_t::Aborted;
return false; 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 // finished freezing data records
...@@ -313,7 +269,7 @@ bool LlxScx<UserData, ValuePool>::Help( ...@@ -313,7 +269,7 @@ bool LlxScx<UserData, ValuePool>::Help(
template< typename UserData > template< typename UserData >
LlxScxRecord<UserData>::LlxScxRecord() LlxScxRecord<UserData>::LlxScxRecord()
: marked_for_finalize_(false) { : marked_for_finalize_(false) {
scx_op_.Store(&dummy_scx); scx_op_.Store(&DummyScx);
} }
template< typename UserData > template< typename UserData >
...@@ -321,12 +277,12 @@ LlxScxRecord<UserData>::LlxScxRecord( ...@@ -321,12 +277,12 @@ LlxScxRecord<UserData>::LlxScxRecord(
const UserData & user_data) const UserData & user_data)
: user_data_(user_data), : user_data_(user_data),
marked_for_finalize_(false) { marked_for_finalize_(false) {
scx_op_.Store(&dummy_scx); scx_op_.Store(&DummyScx);
} }
template< typename UserData > template< typename UserData >
ScxRecord< LlxScxRecord<UserData> > ScxRecord< LlxScxRecord<UserData> >
LlxScxRecord<UserData>::dummy_scx = LlxScxRecord<UserData>::DummyScx =
ScxRecord< LlxScxRecord<UserData> >(); ScxRecord< LlxScxRecord<UserData> >();
} // namespace internal } // namespace internal
......
...@@ -39,171 +39,6 @@ namespace embb { ...@@ -39,171 +39,6 @@ namespace embb {
namespace containers { namespace containers {
namespace internal { 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<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps
/**< [IN] Data records to be finalized in this store operation */
);
/**
* Tentatively performs a single-record Store-Conditional operation on
* given LLX/SCX data record.
* Returns true if the given value has been stored successfully, otherwise
* false.
*/
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps
/**< [IN] Data records linked to this store operation */
);
/**
* 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 * SCX operation description. An SCX record contains all information
* required to allow any process to complete a pending SCX operation. * required to allow any process to complete a pending SCX operation.
...@@ -335,10 +170,11 @@ class LlxScxRecord { ...@@ -335,10 +170,11 @@ class LlxScxRecord {
typedef internal::ScxRecord<self_t> ScxRecord_t; typedef internal::ScxRecord<self_t> ScxRecord_t;
typedef typename ScxRecord_t::OperationState OperationState; typedef typename ScxRecord_t::OperationState OperationState;
public:
/** /**
* The dummy SCX record always has state = Aborted. * The dummy SCX record always has state = Aborted.
*/ */
static ScxRecord_t dummy_scx; static ScxRecord_t DummyScx;
public: public:
/** /**
...@@ -472,7 +308,7 @@ class LlxScx { ...@@ -472,7 +308,7 @@ class LlxScx {
private: private:
typedef size_t cas_t; typedef size_t cas_t;
typedef LlxScxRecord<UserData> DataRecord_t; typedef LlxScxRecord< UserData > DataRecord_t;
typedef internal::ScxRecord< LlxScxRecord<UserData> > ScxRecord_t; typedef internal::ScxRecord< LlxScxRecord<UserData> > ScxRecord_t;
typedef typename ScxRecord_t::OperationState OperationState; typedef typename ScxRecord_t::OperationState OperationState;
...@@ -506,41 +342,17 @@ class LlxScx { ...@@ -506,41 +342,17 @@ class LlxScx {
); );
/** /**
* Tentatively performs an LLX (extended load-linked) operation on given * Actual implementation of StoreConditional operating on unified fields/values
* LLX/SCX data record. * of type cas_t.
* 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 * Tentatively performs a single-record Store-Conditional operation on
* given LLX/SCX data record. * given LLX/SCX data record.
* Returns true if the given value has been stored successfully, otherwise * Returns true if the given value has been stored successfully, otherwise
* false. * false.
*/ */
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps
/**< [IN] Data records to be finalized in this store operation */
);
template< typename FieldType >
bool TryStoreConditional( bool TryStoreConditional(
embb::base::Atomic<FieldType *> * field, embb::base::Atomic<cas_t> * cas_field,
/**< [IN] Pointer to the field the value will be stored into */ /**< [IN] Pointer to the field the value will be stored into */
FieldType * value, cas_t cas_value,
/**< [IN] Value to store */ /**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps, embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */ /**< [IN] Data records linked to this store operation */
...@@ -549,32 +361,6 @@ class LlxScx { ...@@ -549,32 +361,6 @@ class LlxScx {
); );
/** /**
* Tentatively performs a single-record Store-Conditional operation on
* given LLX/SCX data record.
* Returns true if the given value has been stored successfully, otherwise
* false.
*/
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps
/**< [IN] Data records linked to this store operation */
);
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<FieldType*> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType * value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps
/**< [IN] Data records linked to this store operation */
);
/**
* Performs a VLX (extended validate link) operation on given LLX data * Performs a VLX (extended validate link) operation on given LLX data
* record. * record.
* Before calling this method, the given LLX/SCX record must have been * Before calling this method, the given LLX/SCX record must have been
...@@ -589,7 +375,10 @@ class LlxScx { ...@@ -589,7 +375,10 @@ class LlxScx {
); );
private: private:
/**
* Result of a Load-Linked operation, to be stored in thread-specific
* array range within thread_llx_results_.
*/
typedef struct { typedef struct {
DataRecord_t * data_record; DataRecord_t * data_record;
ScxRecord_t * scx_record; ScxRecord_t * scx_record;
...@@ -602,6 +391,11 @@ class LlxScx { ...@@ -602,6 +391,11 @@ class LlxScx {
unsigned int ThreadId(); 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. * Maximum number of active links created via TryLoadLinked per thread.
*/ */
size_t max_links_; size_t max_links_;
...@@ -616,7 +410,7 @@ class LlxScx { ...@@ -616,7 +410,7 @@ class LlxScx {
* thread's local table of LLX results. * thread's local table of LLX results.
*/ */
embb::containers::ObjectPool< embb::containers::ObjectPool<
embb::containers::internal::FixedSizeList<ScxRecord_t *>, ValuePool > embb::containers::internal::FixedSizeList< ScxRecord_t * >, ValuePool >
scx_record_list_pool_; scx_record_list_pool_;
/** /**
...@@ -627,7 +421,7 @@ class LlxScx { ...@@ -627,7 +421,7 @@ class LlxScx {
/** /**
* Thread-specific list of LLX results performed by the thread. * Thread-specific list of LLX results performed by the thread.
*/ */
embb::containers::internal::FixedSizeList<LlxResult> ** embb::containers::internal::FixedSizeList< LlxResult > **
thread_llx_results_; thread_llx_results_;
/** /**
...@@ -645,35 +439,8 @@ class LlxScx { ...@@ -645,35 +439,8 @@ class LlxScx {
*/ */
LlxScx & operator=(const 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_t> * 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<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps
/**< [IN] Data records to be finalized in this store operation */
);
template < typename FieldType >
cas_t ToCASValue(FieldType value) {
return static_cast<cas_t>(value);
}
template < typename FieldType >
cas_t ToCASValue(FieldType * value) {
return reinterpret_cast<cas_t>(value);
}
}; };
#endif // DOXYGEN
} // namespace primitives } // namespace primitives
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
......
/*
* 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 <embb/containers/internal/multiword_ll_sc-inl.h>
#include <embb/containers/internal/fixed_size_list.h>
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
namespace embb {
namespace containers {
namespace internal {
template< typename UserData, typename ValuePool >
MultiwordLLSC<UserData, ValuePool>::MultiwordLLSC(size_t max_links)
: llx_scx_(max_links) {
}
template< typename UserData, typename ValuePool >
MultiwordLLSC<UserData, ValuePool>::~MultiwordLLSC() {
}
template< typename UserData, typename ValuePool >
bool MultiwordLLSC<UserData, ValuePool>::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<UserData, ValuePool>::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<UserData, ValuePool>::TryStoreConditional(
Link & linked_dep,
embb::base::Atomic<FieldType> * field,
FieldType value) {
embb::containers::internal::FixedSizeList<Link *> linked_deps(1);
linked_deps.PushBack(&linked_dep);
embb::containers::internal::FixedSizeList<Link *> finalize_deps(0);
embb::base::Atomic<cas_t> * cas_field =
reinterpret_cast<embb::base::Atomic<cas_t> *>(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<UserData, ValuePool>::TryStoreConditional(
Link & linked_dep_1,
Link & linked_dep_2,
embb::base::Atomic<FieldType> * field,
FieldType value) {
embb::containers::internal::FixedSizeList<Link *> linked_deps(2);
linked_deps.PushBack(&linked_dep_1);
linked_deps.PushBack(&linked_dep_2);
embb::containers::internal::FixedSizeList<Link *> finalize_deps(0);
embb::base::Atomic<cas_t> * cas_field =
reinterpret_cast<embb::base::Atomic<cas_t> *>(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<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType> * field,
FieldType value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps) {
embb::containers::internal::FixedSizeList<DataRecord_t *> finalize_deps(0);
return TryStoreConditional(field, value, linked_deps, finalize_deps);
}
template< typename UserData, typename ValuePool >
template< typename FieldType >
bool MultiwordLLSC<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType*> * field,
FieldType * value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps) {
embb::base::Atomic<cas_t> * cas_field =
reinterpret_cast<embb::base::Atomic<cas_t> *>(field);
cas_t cas_value = reinterpret_cast<cas_t>(value);
return llx_scx_.TryStoreConditional(cas_field, cas_value, linked_deps, finalize_deps);
}
template< typename UserData, typename ValuePool >
template< typename FieldType >
bool MultiwordLLSC<UserData, ValuePool>::TryStoreConditional(
embb::base::Atomic<FieldType*> * field,
FieldType * value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps) {
embb::containers::internal::FixedSizeList<DataRecord_t *> finalize_deps(0);
return TryStoreConditional(field, value, linked_deps, finalize_deps);
}
#endif
template< typename UserData, typename ValuePool >
bool MultiwordLLSC<UserData, ValuePool>::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_
/*
* 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 <embb/base/thread.h>
#include <embb/base/atomic.h>
#include <embb/base/function.h>
#include <embb/base/thread_specific_storage.h>
#include <embb/containers/object_pool.h>
#include <embb/containers/lock_free_tree_value_pool.h>
#include <embb/containers/internal/fixed_size_list.h>
#include <embb/containers/internal/llx_scx.h>
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<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps
/**< [IN] Data records to be finalized in this store operation */
);
/**
* Tentatively performs a single-record Store-Conditional operation on
* given LLX/SCX data record.
* Returns true if the given value has been stored successfully, otherwise
* false.
*/
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<FieldType> * field,
/**< [IN] Pointer to the field the value will be stored into */
FieldType value,
/**< [IN] Value to store */
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps
/**< [IN] Data records linked to this store operation */
);
/**
* 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<UserData> 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType> * 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<FieldType *> * 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<UserData, ValuePool> 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<FieldType> & source,
embb::base::Atomic<FieldType> & target) {
target.Store(source.Load());
}
};
#endif // DOXYGEN
} // namespace primitives
} // namespace containers
} // namespace embb
#include <embb/containers/internal/llx_scx-inl.h>
#endif // EMBB_CONTAINERS_MULTIWORD_LL_SC_H_
\ No newline at end of file
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <llx_scx_test.h> #include <llx_scx_test.h>
#include <embb/containers/internal/fixed_size_list.h> #include <embb/containers/internal/fixed_size_list.h>
#include <embb/containers/internal/llx_scx.h> #include <embb/containers/internal/llx_scx.h>
// #include <embb/containers/multiword_ll_sc.h>
namespace embb { namespace embb {
namespace containers { namespace containers {
...@@ -44,11 +45,11 @@ LlxScxTest::LlxScxTest() : ...@@ -44,11 +45,11 @@ LlxScxTest::LlxScxTest() :
head(0, '-'), head(0, '-'),
tail_llx(tail), tail_llx(tail),
head_llx(head) { head_llx(head) {
CreateUnit("SerialArrayTest").Add(&LlxScxTest::SerialArrayTest, this); CreateUnit("SerialArrayTest")
CreateUnit("SerialListTest").Add(&LlxScxTest::SerialListTest, this); .Add(&LlxScxTest::SerialArrayTest, this);
// CreateUnit("ParallelTest") CreateUnit("ParallelTest")
// .Add(&LlxScxTest::ParallelTest, this) .Add(&LlxScxTest::ParallelTest, this)
// .Post(&LlxScxTest::ParallelTestPost, this); .Post(&LlxScxTest::ParallelTestPost, this);
} }
void LlxScxTest::ParallelTest() { void LlxScxTest::ParallelTest() {
...@@ -56,7 +57,6 @@ void LlxScxTest::ParallelTest() { ...@@ -56,7 +57,6 @@ void LlxScxTest::ParallelTest() {
int return_val = embb_internal_thread_index(&thread_index); int return_val = embb_internal_thread_index(&thread_index);
if (return_val != EMBB_SUCCESS) if (return_val != EMBB_SUCCESS)
EMBB_THROW(embb::base::ErrorException, "Could not get thread id!"); EMBB_THROW(embb::base::ErrorException, "Could not get thread id!");
// Threads try to append n nodes to a linked list in parallel // Threads try to append n nodes to a linked list in parallel
for (char value = 'a'; value <= 'z';) { for (char value = 'a'; value <= 'z';) {
// Find node to append new element on: // Find node to append new element on:
...@@ -67,20 +67,31 @@ void LlxScxTest::ParallelTest() { ...@@ -67,20 +67,31 @@ void LlxScxTest::ParallelTest() {
next = next->Data().next_; next = next->Data().next_;
} }
Node n; 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) { 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<LlxScxRecord<Node> *> linked_deps(1); internal::FixedSizeList<LlxScxRecord<Node> *> linked_deps(1);
internal::FixedSizeList<LlxScxRecord<Node> *> finalize_deps(0);
linked_deps.PushBack(node); linked_deps.PushBack(node);
// Create new node: // Create new node:
Node new_node(static_cast<int>(thread_index), value); Node new_node(static_cast<int>(thread_index), value);
internal::LlxScxRecord<Node> * new_node_ptr = internal::LlxScxRecord<Node> * new_node_ptr =
new internal::LlxScxRecord<Node>(new_node); new internal::LlxScxRecord<Node>(new_node);
// Convert node pointer to size_t:
size_t new_cas_value = reinterpret_cast<size_t>(new_node_ptr);
// Convert target field pointer to size_t*:
embb::base::Atomic<size_t> * field_cas_ptr =
reinterpret_cast< embb::base::Atomic<size_t> * >(
&(node->Data().next_));
// Call SCX:
bool element_inserted = bool element_inserted =
llxscx_.TryStoreConditional( llxscx_.TryStoreConditional(
&(node->Data().next_), field_cas_ptr,
new_node_ptr, new_cas_value,
linked_deps); linked_deps,
finalize_deps);
if (element_inserted) { if (element_inserted) {
// Value has been added to list, continue with next value // Value has been added to list, continue with next value
++value; ++value;
...@@ -109,88 +120,40 @@ void LlxScxTest::SerialArrayTest() { ...@@ -109,88 +120,40 @@ void LlxScxTest::SerialArrayTest() {
// a specialization for atomics that uses a.Store(b.Load()). // a specialization for atomics that uses a.Store(b.Load()).
AtomicField field(23); AtomicField field(23);
LlxScxRecord< Payload > * my_list = LlxScxRecord< Payload > r1(100);
new LlxScxRecord<Payload>[10]; LlxScxRecord< Payload > r2(200);
for (int i = 0; i != 10; ++i) { LlxScxRecord< Payload > r3(300);
my_list[i] = i;
}
Payload l1, l2; Payload l1, l2, l3;
PT_ASSERT(llxscx.TryLoadLinked(&my_list[0], l1)); bool finalized;
PT_ASSERT(llxscx.TryLoadLinked(&my_list[5], l2)); 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<Payload> * >
links(3);
links.PushBack(&r1);
links.PushBack(&r2);
links.PushBack(&r3);
FixedSizeList< LlxScxRecord<Payload> * > FixedSizeList< LlxScxRecord<Payload> * >
links(2); finalize_links(1);
links.PushBack(&my_list[0]); finalize_links.PushBack(&r3);
links.PushBack(&my_list[5]);
// Try to store new value depending on links: // Try to store new value depending on links:
size_t a = 42; 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: // New value should have been changed successfully:
PT_ASSERT_EQ(field.Load(), a); PT_ASSERT_EQ(field.Load(), a);
} }
void LlxScxTest::SerialListTest() {
typedef LlxScxTest::Node Node;
// Global:
LlxScx<Node> llxscx(3);
// Multiset { a, b, b, c }
Node n1(1, 'a');
Node n2(2, 'b');
Node n3(1, 'c');
// V = { dr1, dr2, dr3 }
// R = { dr1, dr2 }
LlxScxRecord<Node> dr1(n1);
LlxScxRecord<Node> dr2(n2);
LlxScxRecord<Node> dr3(n3);
dr1->next_.Store(&dr2);
dr2->next_.Store(&dr3);
// Thread-local:
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<Node> * >
linked_deps(3);
linked_deps.PushBack(&dr1);
linked_deps.PushBack(&dr2);
linked_deps.PushBack(&dr3);
FixedSizeList< LlxScxRecord<Node> * >
finalize_deps(2);
finalize_deps.PushBack(&dr2);
finalize_deps.PushBack(&dr3);
typedef LlxScxRecord<Node> * field_t;
LlxScxRecord<Node> new_node(n3);
PT_ASSERT(
llxscx.TryStoreConditional(
&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 test
} // namespace containers } // namespace containers
} // namespace embb } // namespace embb
...@@ -89,7 +89,6 @@ class LlxScxTest : public partest::TestCase { ...@@ -89,7 +89,6 @@ class LlxScxTest : public partest::TestCase {
private: private:
void SerialArrayTest(); void SerialArrayTest();
void SerialListTest();
void ParallelTest(); void ParallelTest();
void ParallelTestPost(); void ParallelTestPost();
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#include "./hazard_pointer_test.h" #include "./hazard_pointer_test.h"
#include "./object_pool_test.h" #include "./object_pool_test.h"
#include "./llx_scx_test.h" #include "./llx_scx_test.h"
// #include "./multiset_test.h"
#define COMMA , #define COMMA ,
...@@ -58,7 +57,6 @@ using embb::containers::test::QueueTest; ...@@ -58,7 +57,6 @@ using embb::containers::test::QueueTest;
using embb::containers::test::StackTest; using embb::containers::test::StackTest;
using embb::containers::test::ObjectPoolTest; using embb::containers::test::ObjectPoolTest;
using embb::containers::test::LlxScxTest; using embb::containers::test::LlxScxTest;
// using embb::containers::test::MultisetTest;
PT_MAIN("Data Structures C++") { PT_MAIN("Data Structures C++") {
unsigned int max_threads = static_cast<unsigned int>( unsigned int max_threads = static_cast<unsigned int>(
...@@ -75,7 +73,6 @@ PT_MAIN("Data Structures C++") { ...@@ -75,7 +73,6 @@ PT_MAIN("Data Structures C++") {
PT_RUN(ObjectPoolTest< LockFreeTreeValuePool<bool COMMA false > >); PT_RUN(ObjectPoolTest< LockFreeTreeValuePool<bool COMMA false > >);
PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool<bool COMMA false> >); PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool<bool COMMA false> >);
PT_RUN(LlxScxTest); PT_RUN(LlxScxTest);
//PT_RUN(MultisetTest);
PT_EXPECT(embb_get_bytes_allocated() == 0); PT_EXPECT_EQ(embb_get_bytes_allocated(), static_cast<size_t>(0));
} }
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#define CONTAINERS_CPP_TEST_MULTISET_TEST_H_ #define CONTAINERS_CPP_TEST_MULTISET_TEST_H_
#include <partest/partest.h> #include <partest/partest.h>
#include <embb/containers/primitives/llx_scx.h> #include <embb/containers/internal/llx_scx.h>
namespace embb { namespace embb {
namespace containers { namespace containers {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment