Commit fb4b4604 by Tobias Fuchs

containers_cpp: added specialization of LLX/SCX for pointer types, added another simple unit test

parent 763326c7
......@@ -24,25 +24,19 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_
#define EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_
#ifndef EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_
#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_
#include <embb/containers/primitives/llx_scx.h>
#include <embb/containers/internal/llx_scx.h>
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
#include <embb/base/memory_allocation.h>
#include <vector>
#include <stdarg.h>
/**
* Implementation of the LX/STX primitive as presented in
* "Pragmatic Primitives for Non-blocking Data Structures"
* (Brown et al., 2013).
*/
namespace embb {
namespace containers {
namespace primitives {
namespace internal {
template< typename UserData, typename ValuePool >
unsigned int LlxScx<UserData, ValuePool>::ThreadId() {
......@@ -89,7 +83,7 @@ LlxScx<UserData, ValuePool>::~LlxScx() {
template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::TryLoadLinked(
DataRecord_t * const data_record,
DataRecord_t & user_data,
UserData & user_data,
bool & finalized) {
finalized = false;
unsigned int thread_id = ThreadId();
......@@ -132,7 +126,7 @@ bool LlxScx<UserData, ValuePool>::TryLoadLinked(
template< typename UserData, typename ValuePool >
bool LlxScx<UserData, ValuePool>::TryLoadLinked(
DataRecord_t * const data_record,
DataRecord_t & user_data) {
UserData & user_data) {
bool finalized;
return TryLoadLinked(data_record, user_data, finalized);
}
......@@ -144,6 +138,51 @@ bool LlxScx<UserData, ValuePool>::TryStoreConditional(
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,
embb::containers::internal::FixedSizeList<DataRecord_t *> & finalize_deps) {
typedef embb::containers::internal::FixedSizeList<DataRecord_t *> dr_list_t;
typedef embb::containers::internal::FixedSizeList<ScxRecord_t *> scx_op_list_t;
// Preconditions:
......@@ -184,11 +223,11 @@ bool LlxScx<UserData, ValuePool>::TryStoreConditional(
linked_deps,
finalize_deps,
// target field:
reinterpret_cast<embb::base::Atomic<cas_t> *>(field),
cas_field,
// new value:
reinterpret_cast<cas_t>(value),
cas_value,
// old value:
reinterpret_cast<cas_t>(field->Load()),
cas_field->Load(),
// linked SCX operations:
info_fields,
// initial operation state:
......@@ -199,16 +238,6 @@ bool LlxScx<UserData, ValuePool>::TryStoreConditional(
}
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>::TryValidateLink(
const DataRecord_t & field) {
return true; // @TODO
......@@ -233,7 +262,7 @@ LlxScxRecord<UserData>::LlxScxRecord(
// internal::ScxRecord
template< typename DataRecord >
bool internal::ScxRecord<DataRecord>::Help() {
bool ScxRecord<DataRecord>::Help() {
// We ensure that an SCX S does not change a data record
// while it is frozen for another SCX S'. Instead, S uses
// the information in the SCX record of S' to help S'
......@@ -291,12 +320,12 @@ bool internal::ScxRecord<DataRecord>::Help() {
}
template< typename UserData >
internal::ScxRecord< LlxScxRecord<UserData> >
ScxRecord< LlxScxRecord<UserData> >
LlxScxRecord<UserData>::dummy_scx =
internal::ScxRecord< LlxScxRecord<UserData> >();
ScxRecord< LlxScxRecord<UserData> >();
} // namespace primitives
} // namespace internal
} // namespace containers
} // namespace embb
#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_INL_H_
#endif // EMBB_CONTAINERS_INTERNAL_LLX_SCX_INL_H_
......@@ -24,8 +24,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_
#define EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_
#ifndef EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_
#define EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_
#include <embb/base/thread.h>
#include <embb/base/atomic.h>
......@@ -36,13 +36,177 @@
namespace embb {
namespace containers {
namespace primitives {
namespace internal {
#ifdef DOXYGEN
/**
* Wraps user-defined data with fields required for LLX/SCX algorithm.
* Mutable fields must each be contained in a single word.
*/
template< typename UserData >
class LlxScxRecord {
public:
/**
* Default constructor.
*/
LlxScxRecord();
/**
* Constructor. Creates an instance of \c DataRecord_t wrapping a user
* data object.
*/
LlxScxRecord(const UserData & user_data);
/**
* Copy constructor
*/
LlxScxRecord(const LlxScxRecord & other);
/**
* Assignment operator.
*/
LlxScxRecord & operator=(const LlxScxRecord & rhs);
/**
* Destructor.
*/
~LlxScxRecord();
/**
* Returns user data payload of this LLX/SCX record
*/
UserData & Data();
/**
* Allows pointer semantics, returns user data payload of this
* LLX/SCX record
*/
UserData * operator*();
/**
* Allows pointer semantics, returns user data payload of this
* LLX/SCX record
*/
UserData * operator->();
/**
* Whether this data record is marked for finalizing.
*/
bool IsMarkedForFinalize() const;
};
/**
* Multiword LL/SC
*
* Implementation of the LLX/STX primitive as presented in
* "Pragmatic Primitives for Non-blocking Data Structures"
* (Brown et al., 2013).
*
* \tparam UserData Type containing mutable fields
* \tparam ValuePool Type containing mutable fields
*/
template<
typename UserData,
typename ValuePool = embb::containers::LockFreeTreeValuePool< bool, false >
>
class LlxScx {
public:
/**
* Constructs a new instance of LlxScx.
*/
LlxScx(
size_t max_links
/**< [IN] Maximum number of links depending on a single SCX operation */
);
/**
* Destructor, frees memory.
*/
~LlxScx();
/**
* Tentatively performs an LLX (extended load-linked) operation on given
* LLX/SCX data record.
* Returns true and stores result in given reference variable on success,
* otherwise returns false.
*/
bool TryLoadLinked(
DataRecord_t * const data_record,
/**< [IN] Pointer to data record to load */
UserData & data,
/**< [OUT] Atomic snapshot of data record */
bool & finalized
/**< [OUT] Indicating whether requested fields have been finalized */
);
/**
* Tentatively performs an LLX (extended load-linked) operation on given
* LLX/SCX data record.
* Returns true and stores result in given reference variable on success,
* otherwise returns false.
*/
bool TryLoadLinked(
DataRecord_t * const data_record,
/**< [IN] Pointer to data record to load */
UserData & data
/**< [OUT] Atomic snapshot of data record */
);
/**
* Tentatively performs a single-record Store-Conditional operation on
* given LLX/SCX data record.
* Returns true if the given value has been stored successfully, otherwise
* false.
*/
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<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.
* This class is non-public.
*/
template< typename DataRecord >
class ScxRecord {
......@@ -163,8 +327,6 @@ class ScxRecord {
}; /* class ScxRecord */
} // namespace internal
/**
* Wraps user-defined data with fields required for LLX/SCX algorithm.
* Mutable fields must each be contained in a single word.
......@@ -341,7 +503,7 @@ class LlxScx {
bool TryLoadLinked(
DataRecord_t * const data_record,
/**< [IN] Pointer to data record to load */
DataRecord_t & data,
UserData & data,
/**< [OUT] Atomic snapshot of data record */
bool & finalized
/**< [OUT] Indicating whether requested fields have been finalized */
......@@ -356,7 +518,7 @@ class LlxScx {
bool TryLoadLinked(
DataRecord_t * const data_record,
/**< [IN] Pointer to data record to load */
DataRecord_t & data
UserData & data
/**< [OUT] Atomic snapshot of data record */
);
......@@ -378,6 +540,18 @@ class LlxScx {
/**< [IN] Data records to be finalized in this store operation */
);
template< typename FieldType >
bool TryStoreConditional(
embb::base::Atomic<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.
......@@ -394,15 +568,24 @@ class LlxScx {
/**< [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.
* Returns true if the calling thread's link obtained by its most recent
* invocation of SCX is still valid.
* Before calling this method, the given LLX/SCX record must have been
* linked via \c TryLoadLinked.
*
* Precondition:
* - for each data record r in data_records, the active thread
* has performed and r.LLX linked to this VLX.
* \returns True if the calling thread's link obtained by its most recent
* invocation of SCX is still valid.
*/
bool TryValidateLink(
const DataRecord_t & data_record
......@@ -433,13 +616,16 @@ class LlxScx {
unsigned int max_threads_;
/**
* Shared table containing for each r in V, a copy of r's info
* value in this thread's local table of LLX results.
* Shared table containing for each r in V, a copy of r's info value in this
* thread's local table of LLX results.
*/
embb::containers::ObjectPool<
embb::containers::internal::FixedSizeList<ScxRecord_t *>, ValuePool >
scx_record_list_pool_;
/**
* Pool for SCX records allocated in TryStoreConditional
*/
embb::containers::ObjectPool< ScxRecord_t, ValuePool > scx_record_pool_;
/**
......@@ -463,12 +649,37 @@ class LlxScx {
*/
LlxScx & operator=(const LlxScx &);
/**
* Actual implementation of StoreConditional operating on unified fields/values
* of type cas_t.
*/
bool TryStoreConditionalCAS(
embb::base::Atomic<cas_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
#include <embb/containers/internal/primitives/llx_scx-inl.h>
#include <embb/containers/internal/llx_scx-inl.h>
#endif // EMBB_CONTAINERS_PRIMITIVES_LLX_SCX_H_
\ No newline at end of file
#endif // EMBB_CONTAINERS_INTERNAL_LLX_SCX_H_
\ No newline at end of file
......@@ -26,15 +26,15 @@
#include <llx_scx_test.h>
#include <embb/containers/internal/fixed_size_list.h>
#include <embb/containers/primitives/llx_scx.h>
#include <embb/containers/internal/llx_scx.h>
namespace embb {
namespace containers {
namespace test {
using embb::containers::internal::FixedSizeList;
using embb::containers::primitives::LlxScxRecord;
using embb::containers::primitives::LlxScx;
using embb::containers::internal::LlxScxRecord;
using embb::containers::internal::LlxScx;
LlxScxTest::LlxScxTest() :
num_threads_(
......@@ -42,7 +42,8 @@ LlxScxTest::LlxScxTest() :
llxscx_(3),
tail(0, '-'),
head(0, '-', Node::node_ptr_t(&tail)) {
CreateUnit("SerialTest").Add(&LlxScxTest::SerialTest, this);
CreateUnit("SerialArrayTest").Add(&LlxScxTest::SerialArrayTest, this);
CreateUnit("SerialListTest").Add(&LlxScxTest::SerialListTest, this);
CreateUnit("ParallelTest").Add(&LlxScxTest::ParallelTest, this);
}
......@@ -57,7 +58,39 @@ void LlxScxTest::ParallelTest() {
// Threads try to append n nodes to a linked list in parallel
}
void LlxScxTest::SerialTest() {
void LlxScxTest::SerialArrayTest() {
typedef int Payload;
typedef embb::base::Atomic<size_t> AtomicField;
// LLX/SCX with maximum of 3 active load-links in every thread:
LlxScx<Payload> llxscx(3);
// Atomic<size_t> not assignable, TryStoreConditional requires
// a specialization for atomics that uses a.Store(b.Load()).
AtomicField field(23);
// Initialize
LlxScxRecord< Payload > * my_list =
new LlxScxRecord<Payload>[10];
for (int i = 0; i != 10; ++i) {
my_list[i] = i;
}
Payload l1, l2;
PT_ASSERT(llxscx.TryLoadLinked(&my_list[0], l1));
PT_ASSERT(llxscx.TryLoadLinked(&my_list[5], l2));
FixedSizeList< LlxScxRecord<Payload> * >
links(2);
links.PushBack(&my_list[0]);
links.PushBack(&my_list[5]);
// Try to store new value depending on links:
size_t a = 42;
PT_ASSERT(llxscx.TryStoreConditional(&field, a, links));
// New value should have been changed successfully:
PT_ASSERT_EQ(field.Load(), a);
}
void LlxScxTest::SerialListTest() {
typedef LlxScxTest::Node Node;
// Global:
LlxScx<Node> llxscx(3);
......@@ -77,17 +110,17 @@ void LlxScxTest::SerialTest() {
dr2->next_.Store(&dr3);
// Thread-local:
LlxScxRecord<Node> l1, l2, l3;
Node l1, l2, l3;
bool finalized;
PT_ASSERT(llxscx.TryLoadLinked(&dr1, l1, finalized));
PT_ASSERT(!finalized);
PT_ASSERT_EQ(l1->value_, dr1->value_);
PT_ASSERT_EQ(l1.value_, dr1->value_);
PT_ASSERT(llxscx.TryLoadLinked(&dr2, l2, finalized));
PT_ASSERT(!finalized);
PT_ASSERT_EQ(l2->value_, dr2->value_);
PT_ASSERT_EQ(l2.value_, dr2->value_);
PT_ASSERT(llxscx.TryLoadLinked(&dr3, l3, finalized));
PT_ASSERT(!finalized);
PT_ASSERT_EQ(l3->value_, dr3->value_);
PT_ASSERT_EQ(l3.value_, dr3->value_);
FixedSizeList< LlxScxRecord<Node> * >
linked_deps(3);
......@@ -103,7 +136,7 @@ void LlxScxTest::SerialTest() {
LlxScxRecord<Node> new_node(n3);
PT_ASSERT(
llxscx.TryStoreConditional<LlxScxRecord<Node> *>(
llxscx.TryStoreConditional(
&n2.next_, // fld: field to update
&new_node, // new value
linked_deps, // V: dependencies, must be LL'd before
......
......@@ -28,7 +28,7 @@
#define CONTAINERS_CPP_TEST_LLX_SCX_TEST_H_
#include <partest/partest.h>
#include <embb/containers/primitives/llx_scx.h>
#include <embb/containers/internal/llx_scx.h>
namespace embb {
namespace containers {
......@@ -39,10 +39,10 @@ class LlxScxTest : public partest::TestCase {
private:
class Node {
public:
typedef primitives::LlxScxRecord<Node> * node_ptr_t;
typedef internal::LlxScxRecord<Node> * node_ptr_t;
public:
embb::base::Atomic<primitives::LlxScxRecord<Node> *> next_;
embb::base::Atomic<internal::LlxScxRecord<Node> *> next_;
embb::base::Atomic<int> count_;
char value_;
......@@ -88,11 +88,12 @@ class LlxScxTest : public partest::TestCase {
LlxScxTest();
private:
void SerialTest();
void SerialArrayTest();
void SerialListTest();
void ParallelTest();
int num_threads_;
primitives::LlxScx<Node> llxscx_;
internal::LlxScx<Node> llxscx_;
Node tail;
Node head;
};
......
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