Commit 56a22c45 by Tobias Fuchs

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

parent 746bd2fc
......@@ -31,8 +31,6 @@
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
#include <embb/base/memory_allocation.h>
#include <vector>
#include <stdarg.h>
namespace embb {
namespace containers {
......@@ -124,61 +122,7 @@ bool LlxScx<UserData, ValuePool>::TryLoadLinked(
}
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(
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,
cas_t cas_value,
embb::containers::internal::FixedSizeList<DataRecord_t *> & linked_deps,
......@@ -193,28 +137,40 @@ bool LlxScx<UserData, ValuePool>::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<LlxResult>
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,23 +223,23 @@ bool LlxScx<UserData, ValuePool>::Help(
ScxRecord<DataRecord_t> * 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
......@@ -313,7 +269,7 @@ bool LlxScx<UserData, ValuePool>::Help(
template< typename UserData >
LlxScxRecord<UserData>::LlxScxRecord()
: marked_for_finalize_(false) {
scx_op_.Store(&dummy_scx);
scx_op_.Store(&DummyScx);
}
template< typename UserData >
......@@ -321,12 +277,12 @@ LlxScxRecord<UserData>::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<UserData> >
LlxScxRecord<UserData>::dummy_scx =
LlxScxRecord<UserData>::DummyScx =
ScxRecord< LlxScxRecord<UserData> >();
} // namespace internal
......
......@@ -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<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
* required to allow any process to complete a pending SCX operation.
......@@ -335,10 +170,11 @@ class LlxScxRecord {
typedef internal::ScxRecord<self_t> 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:
/**
......@@ -472,7 +308,7 @@ class LlxScx {
private:
typedef size_t cas_t;
typedef LlxScxRecord<UserData> DataRecord_t;
typedef LlxScxRecord< UserData > DataRecord_t;
typedef internal::ScxRecord< LlxScxRecord<UserData> > ScxRecord_t;
typedef typename ScxRecord_t::OperationState OperationState;
......@@ -506,41 +342,17 @@ 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<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(
embb::base::Atomic<FieldType *> * field,
embb::base::Atomic<cas_t> * 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<DataRecord_t *> & linked_deps,
/**< [IN] Data records linked to this store operation */
......@@ -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
* record.
* Before calling this method, the given LLX/SCX record must have been
......@@ -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;
......@@ -602,6 +391,11 @@ class LlxScx {
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.
*/
size_t max_links_;
......@@ -616,7 +410,7 @@ class LlxScx {
* thread's local table of LLX results.
*/
embb::containers::ObjectPool<
embb::containers::internal::FixedSizeList<ScxRecord_t *>, 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<LlxResult> **
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_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 containers
} // 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 @@
#include <llx_scx_test.h>
#include <embb/containers/internal/fixed_size_list.h>
#include <embb/containers/internal/llx_scx.h>
// #include <embb/containers/multiword_ll_sc.h>
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,20 +67,31 @@ 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<LlxScxRecord<Node> *> linked_deps(1);
internal::FixedSizeList<LlxScxRecord<Node> *> finalize_deps(0);
linked_deps.PushBack(node);
// Create new node:
Node new_node(static_cast<int>(thread_index), value);
internal::LlxScxRecord<Node> * new_node_ptr =
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 =
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<Payload>[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<Payload> * >
links(3);
links.PushBack(&r1);
links.PushBack(&r2);
links.PushBack(&r3);
FixedSizeList< LlxScxRecord<Payload> * >
links(2);
links.PushBack(&my_list[0]);
links.PushBack(&my_list[5]);
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<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 containers
} // namespace embb
......@@ -89,7 +89,6 @@ class LlxScxTest : public partest::TestCase {
private:
void SerialArrayTest();
void SerialListTest();
void ParallelTest();
void ParallelTestPost();
......
......@@ -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<unsigned int>(
......@@ -75,7 +73,6 @@ PT_MAIN("Data Structures C++") {
PT_RUN(ObjectPoolTest< LockFreeTreeValuePool<bool COMMA false > >);
PT_RUN(ObjectPoolTest< WaitFreeArrayValuePool<bool COMMA false> >);
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 @@
#define CONTAINERS_CPP_TEST_MULTISET_TEST_H_
#include <partest/partest.h>
#include <embb/containers/primitives/llx_scx.h>
#include <embb/containers/internal/llx_scx.h>
namespace embb {
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