Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 185 additions & 7 deletions cds/container/details/lazy_skip_list_set_base.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#ifndef CDSLIB_LAZY_SKIP_LIST_BASE_H
#define CDSLIB_LAZY_SKIP_LIST_BASE_H

#include <cds/intrusive/details/skip_list_base.h>
#include <cds/intrusive/details/base.h>
#include <cds/details/marked_ptr.h>
#include <cds/os/timer.h>
#include <cds/gc/dhp.h>

namespace cds { namespace container {

namespace lazy_skip_list_set {
namespace skip_list {

static size_t const c_nMaxHeight = 32;

typedef cds::intrusive::skip_list::traits traits;

template <
typename GC,
typename T,
Expand All @@ -19,7 +20,7 @@ namespace cds { namespace container {
class node
{
public:
typedef GC gc;
typedef cds::gc::DHP gc;
typedef T value_type;
typedef Lock lock_type;

Expand Down Expand Up @@ -119,7 +120,7 @@ namespace cds { namespace container {
node_allocator alloc;
node_ptr new_node = alloc.New();
new_node->key = key;
new_node->m_nHeight = cds::container::lazy_skip_list_set::c_nMaxHeight;
new_node->m_nHeight = cds::container::skip_list::c_nMaxHeight;
new_node->allocate_tower(new_node->m_nHeight);
new_node->fullyLinked = false;

Expand Down Expand Up @@ -153,7 +154,184 @@ namespace cds { namespace container {
}

};
} // namespace lazy_skip_list_set

/// Turbo-pascal random level generator
/**
This uses a cheap pseudo-random function that was used in Turbo Pascal.

The random generator should return numbers from range [0..31].

From Doug Lea's ConcurrentSkipListMap.java.
*/
template <unsigned MaxHeight>
class turbo
{
//@cond
atomics::atomic<unsigned int> m_nSeed;

static_assert( MaxHeight > 1, "MaxHeight" );
static_assert( MaxHeight <= c_nMaxHeight, "MaxHeight is too large" );
static unsigned int const c_nBitMask = (1u << ( MaxHeight - 1 )) - 1;
//@endcond
public:
/// The upper bound of generator's return value. The generator produces random number in range <tt>[0..c_nUpperBound)</tt>
static unsigned int const c_nUpperBound = MaxHeight;

/// Initializes the generator instance
turbo()
{
m_nSeed.store( (unsigned int) cds::OS::Timer::random_seed(), atomics::memory_order_relaxed );
}

/// Main generator function
unsigned int operator()()
{
/*
private int randomLevel() {
int level = 0;
int r = randomSeed;
randomSeed = r * 134775813 + 1;
if (r < 0) {
while ((r <<= 1) > 0)
++level;
}
return level;
}
*/
/*
The low bits are apparently not very random (the original used only
upper 16 bits) so we traverse from highest bit down (i.e., test
sign), thus hardly ever use lower bits.
*/
unsigned int x = m_nSeed.load( atomics::memory_order_relaxed ) * 134775813 + 1;
m_nSeed.store( x, atomics::memory_order_relaxed );
unsigned int nLevel = ( x & 0x80000000 ) ? ( c_nUpperBound - 1 - cds::bitop::MSBnz( (x & c_nBitMask ) | 1 )) : 0;

assert( nLevel < c_nUpperBound );
return nLevel;
}
};
/// Turbo-Pascal random level generator, max height 32
typedef turbo<c_nMaxHeight> turbo32;

template <typename EventCounter = cds::atomicity::event_counter>
struct stat {
typedef EventCounter event_counter ; ///< Event counter type

event_counter m_nNodeHeightAdd[c_nMaxHeight] ; ///< Count of added node of each height
event_counter m_nNodeHeightDel[c_nMaxHeight] ; ///< Count of deleted node of each height
event_counter m_nInsertSuccess ; ///< Count of success insertion
event_counter m_nInsertFailed ; ///< Count of failed insertion
event_counter m_nInsertRetries ; ///< Count of unsuccessful retries of insertion
event_counter m_nUpdateExist ; ///< Count of \p update() call for existed node
event_counter m_nUpdateNew ; ///< Count of \p update() call for new node
event_counter m_nUnlinkSuccess ; ///< Count of successful call of \p unlink
event_counter m_nUnlinkFailed ; ///< Count of failed call of \p unlink
event_counter m_nEraseSuccess ; ///< Count of successful call of \p erase
event_counter m_nEraseFailed ; ///< Count of failed call of \p erase
event_counter m_nEraseRetry ; ///< Count of retries while erasing node
event_counter m_nFindFastSuccess ; ///< Count of successful call of \p find and all derivatives (via fast-path)
event_counter m_nFindFastFailed ; ///< Count of failed call of \p find and all derivatives (via fast-path)
event_counter m_nFindSlowSuccess ; ///< Count of successful call of \p find and all derivatives (via slow-path)
event_counter m_nFindSlowFailed ; ///< Count of failed call of \p find and all derivatives (via slow-path)
event_counter m_nRenewInsertPosition ; ///< Count of renewing position events while inserting
event_counter m_nLogicDeleteWhileInsert; ///< Count of events "The node has been logically deleted while inserting"
event_counter m_nRemoveWhileInsert ; ///< Count of evnts "The node is removing while inserting"
event_counter m_nFastErase ; ///< Fast erase event counter
event_counter m_nFastExtract ; ///< Fast extract event counter
event_counter m_nSlowErase ; ///< Slow erase event counter
event_counter m_nSlowExtract ; ///< Slow extract event counter
event_counter m_nExtractSuccess ; ///< Count of successful call of \p extract
event_counter m_nExtractFailed ; ///< Count of failed call of \p extract
event_counter m_nExtractRetries ; ///< Count of retries of \p extract call
event_counter m_nExtractMinSuccess ; ///< Count of successful call of \p extract_min
event_counter m_nExtractMinFailed ; ///< Count of failed call of \p extract_min
event_counter m_nExtractMinRetries ; ///< Count of retries of \p extract_min call
event_counter m_nExtractMaxSuccess ; ///< Count of successful call of \p extract_max
event_counter m_nExtractMaxFailed ; ///< Count of failed call of \p extract_max
event_counter m_nExtractMaxRetries ; ///< Count of retries of \p extract_max call
event_counter m_nEraseWhileFind ; ///< Count of erased item while searching
event_counter m_nExtractWhileFind ; ///< Count of extracted item while searching (RCU only)
event_counter m_nMarkFailed ; ///< Count of failed node marking (logical deletion mark)
event_counter m_nEraseContention ; ///< Count of key erasing contention encountered

//@cond
void onAddNode( unsigned int nHeight )
{
assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightAdd) / sizeof(m_nNodeHeightAdd[0]));
++m_nNodeHeightAdd[nHeight - 1];
}
void onRemoveNode( unsigned int nHeight )
{
assert( nHeight > 0 && nHeight <= sizeof(m_nNodeHeightDel) / sizeof(m_nNodeHeightDel[0]));
++m_nNodeHeightDel[nHeight - 1];
}

void onInsertSuccess() { ++m_nInsertSuccess ; }
void onInsertFailed() { ++m_nInsertFailed ; }
void onInsertRetry() { ++m_nInsertRetries ; }
void onUpdateExist() { ++m_nUpdateExist ; }
void onUpdateNew() { ++m_nUpdateNew ; }
void onUnlinkSuccess() { ++m_nUnlinkSuccess ; }
void onUnlinkFailed() { ++m_nUnlinkFailed ; }
void onEraseSuccess() { ++m_nEraseSuccess ; }
void onEraseFailed() { ++m_nEraseFailed ; }
void onEraseRetry() { ++m_nEraseRetry; }
void onFindFastSuccess() { ++m_nFindFastSuccess ; }
void onFindFastFailed() { ++m_nFindFastFailed ; }
void onFindSlowSuccess() { ++m_nFindSlowSuccess ; }
void onFindSlowFailed() { ++m_nFindSlowFailed ; }
void onEraseWhileFind() { ++m_nEraseWhileFind ; }
void onExtractWhileFind() { ++m_nExtractWhileFind ; }
void onRenewInsertPosition() { ++m_nRenewInsertPosition; }
void onLogicDeleteWhileInsert() { ++m_nLogicDeleteWhileInsert; }
void onRemoveWhileInsert() { ++m_nRemoveWhileInsert; }
void onFastErase() { ++m_nFastErase; }
void onFastExtract() { ++m_nFastExtract; }
void onSlowErase() { ++m_nSlowErase; }
void onSlowExtract() { ++m_nSlowExtract; }
void onExtractSuccess() { ++m_nExtractSuccess; }
void onExtractFailed() { ++m_nExtractFailed; }
void onExtractRetry() { ++m_nExtractRetries; }
void onExtractMinSuccess() { ++m_nExtractMinSuccess; }
void onExtractMinFailed() { ++m_nExtractMinFailed; }
void onExtractMinRetry() { ++m_nExtractMinRetries; }
void onExtractMaxSuccess() { ++m_nExtractMaxSuccess; }
void onExtractMaxFailed() { ++m_nExtractMaxFailed; }
void onExtractMaxRetry() { ++m_nExtractMaxRetries; }
void onMarkFailed() { ++m_nMarkFailed; }
void onEraseContention() { ++m_nEraseContention; }
//@endcond
};

// typedef cds::intrusive::skip_list::traits traits;
struct traits {
typedef opt::v::relaxed_ordering memory_model;
typedef turbo32 random_level_generator;
};

template <typename... Options>
struct make_traits {
# ifdef CDS_DOXYGEN_INVOKED
typedef implementation_defined type ; ///< Metafunction result
# else
typedef typename cds::opt::make_options<
typename cds::opt::find_type_traits< traits, Options... >::type
,Options...
>::type type;
# endif
};

namespace details {
template <class GC, typename T>
class iterator {
typedef node<GC, T> node_type;

node_type * m_pNode;
};
}

} // namespace skip_list

}}

Expand Down
43 changes: 32 additions & 11 deletions cds/container/lazy_skip_list_set_dhp.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,56 @@ namespace cds { namespace container {
template <
typename GC,
typename T,
typename Traits = lazy_skip_list_set::traits
typename Traits = skip_list::traits
>
class LazySkipListSet
class SkipListSet
{
public:
typedef GC gc;
typedef cds::gc::DHP gc;
typedef T value_type;
typedef Traits traits;

static size_t const c_nMaxHeight = cds::container::lazy_skip_list_set::c_nMaxHeight;
// static size_t const c_nHazardPtrCount = c_nMaxHeight * 2 + 3;
static size_t const c_nMaxHeight = cds::container::skip_list::c_nMaxHeight;
static size_t const c_nHazardPtrCount = 0;

typedef typename traits::random_level_generator rand_height;

protected:
typedef cds::container::lazy_skip_list_set::node<gc, value_type> node_type;
typedef cds::container::skip_list::node<gc, value_type> node_type;
typedef typename node_type::key_type key_type;
typedef skip_list::details::iterator<GC, T> iterator;

typedef cds::details::marked_ptr<node_type, 1> marked_ptr;

node_type *m_Head;
node_type *m_Tail;

public:
LazySkipListSet() {
SkipListSet() {
m_Head = node_type::min_key();
m_Tail = node_type::max_key();

for (unsigned int layer = 0; layer < c_nMaxHeight; layer++)
m_Head->next(layer).store(marked_ptr(m_Tail), traits::memory_model::memory_order_relaxed);
}

~LazySkipListSet() {
~SkipListSet() {
destroy();
}

iterator begin() {
return iterator(m_Head);
}

iterator end() {
key_type key = m_Tail->node_key();
node_type *preds[c_nMaxHeight];
node_type *succs[c_nMaxHeight];
int lFound = find(key, preds, succs);

return iterator(preds[0]);
}

bool insert(value_type v) {
key_type key = std::hash<value_type>{}(v);
unsigned int topLayer = randomLevel();
Expand Down Expand Up @@ -168,19 +182,22 @@ namespace cds { namespace container {
}
}

bool contains(value_type v) {
value_type contains(value_type &v) {
key_type key = std::hash<value_type>{}(v);
node_type *preds[c_nMaxHeight];
node_type *succs[c_nMaxHeight];
int lFound = find(key, preds, succs);

if (lFound == -1)
return false;
return nullptr;

bool linked = succs[lFound]->fully_linked();
bool marked = succs[lFound]->marked();

return (linked && !marked);
if (linked && !marked)
return v;

return nullptr;
}

bool empty() {
Expand All @@ -198,6 +215,10 @@ namespace cds { namespace container {
}
}

size_t size() {
return 0;
}

protected:
void destroy() {
node_type *p = m_Head; //->next(0).load(atomics::memory_order_relaxed).ptr();
Expand Down
10 changes: 10 additions & 0 deletions test/unit/set/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ add_executable(${UNIT_SET_SKIP} ${UNIT_SET_SKIP_SOURCES})
target_link_libraries(${UNIT_SET_SKIP} ${CDS_TEST_LIBRARIES})
add_test(NAME ${UNIT_SET_SKIP} COMMAND ${UNIT_SET_SKIP} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})

# LazySkipListSet
set(UNIT_SET_LAZY_SKIP unit-set-lazy-skip)
set(UNIT_SET_LAZY_SKIP_SOURCES
../main.cpp
lazy_skiplist.cpp
)
add_executable(${UNIT_SET_LAZY_SKIP} ${UNIT_SET_LAZY_SKIP_SOURCES})
target_link_libraries(${UNIT_SET_LAZY_SKIP} ${CDS_TEST_LIBRARIES})
add_test(NAME ${UNIT_SET_LAZY_SKIP} COMMAND ${UNIT_SET_LAZY_SKIP} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})

# SplitListSet<MichaelList>
set(UNIT_SET_SPLIT_MICHAEL unit-set-split-michael)
set(UNIT_SET_SPLIT_MICHAEL_SOURCES
Expand Down
Loading