Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Lazy Skip List Set #109

Open
wants to merge 6 commits into
base: integration
Choose a base branch
from
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
151 changes: 151 additions & 0 deletions cds/container/details/lazy_skip_list_set_base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#ifndef CDSLIB_LAZY_SKIP_LIST_BASE_H
#define CDSLIB_LAZY_SKIP_LIST_BASE_H

#include <cds/intrusive/details/skip_list_base.h>
#include <cds/gc/nogc.h>

namespace cds { namespace container {

namespace lazy_skip_list_set {

static size_t const c_nMaxHeight = 32;

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

template <
typename GC,
typename T,
typename Lock = std::recursive_mutex
>
class node
{
public:
typedef cds::gc::nogc gc;
typedef T value_type;
typedef Lock lock_type;

typedef std::size_t key_type;

typedef node* node_ptr;
typedef std::numeric_limits<key_type> limits;

typedef cds::details::Allocator<node> node_allocator;
typedef cds::details::Allocator<node_ptr> tower_allocator;

protected:
static tower_allocator towerAllocator;
static node_allocator nodeAllocator;

value_type value;
key_type key;
unsigned int m_nHeight;
node_ptr * m_arrNext;
atomics::atomic<bool> _marked;
atomics::atomic<bool> _fully_linked;

key_type hash() {
return std::hash<value_type>{}(value);
}

public:
lock_type lock;

node() : _marked(false), _fully_linked(false) {

}

~node(){
dispose_tower(this);
}

bool marked() {
return _marked.load();
}

bool mark() {
_marked.store(true);
}

bool fully_linked() {
return _fully_linked.load();
}

void set_fully_linked(bool value) {
_fully_linked.store(value);
}

key_type node_key() {
return key;
}

node_ptr& next(unsigned int nLevel) {
return m_arrNext[nLevel];
}

unsigned int height() {
return m_nHeight;
}

void allocate_tower(int nHeight) {
m_arrNext = towerAllocator.NewArray(nHeight, nullptr);
}

static void dispose_node(node * pNode) {
nodeAllocator.Delete(pNode);
}

static void dispose_tower(node * pNode) {
unsigned int topLayer = pNode->height();
if (topLayer > 0)
towerAllocator.Delete(pNode->release_tower(), topLayer + 1);
}

node_ptr * release_tower() {
node_ptr * pTower = m_arrNext;
m_arrNext = nullptr;
m_nHeight = 0;

return pTower;
}

static node_ptr allocate_node(key_type key) {
node_ptr new_node = nodeAllocator.New();
new_node->key = key;
new_node->m_nHeight = cds::container::lazy_skip_list_set::c_nMaxHeight;
new_node->allocate_tower(new_node->m_nHeight);
new_node->_fully_linked = false;

return new_node;
}

static node_ptr allocate_node(value_type v, unsigned int topLayer) {
node_ptr new_node = nodeAllocator.New();

new_node->value = v;
new_node->key = new_node->hash();
new_node->m_nHeight = topLayer;
new_node->_fully_linked = false;

new_node->allocate_tower(topLayer + 1);

return new_node;
}

static node * min_key() {
node_ptr new_node = allocate_node(limits::min());

return new_node;
}

static node * max_key() {
node_ptr new_node = allocate_node(limits::max());

return new_node;
}

};
} // namespace lazy_skip_list_set

}}

#endif //CDSLIB_LAZY_SKIP_LIST_BASE_H
245 changes: 245 additions & 0 deletions cds/container/lazy_skip_list_set.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
#ifndef CDSLIB_CONTAINER_SKIP_LIST_SET_H
#define CDSLIB_CONTAINER_SKIP_LIST_SET_H

#include <mutex>

#include <cds/container/details/lazy_skip_list_set_base.h>

namespace cds { namespace container {

template <
typename GC,
typename T,
typename Traits = lazy_skip_list_set::traits
>
class LazySkipListSet
{
public:
typedef cds::gc::nogc 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 = 0;

typedef typename traits::random_level_generator rand_height;

protected:
typedef cds::container::lazy_skip_list_set::node<gc, value_type> node_type;
typedef typename node_type::key_type key_type;

node_type *m_Head;
node_type *m_Tail;

public:
LazySkipListSet() {
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) = m_Tail;
}

~LazySkipListSet() {
destroy();
}

bool insert(value_type v) {
key_type key = std::hash<value_type>{}(v);
unsigned int topLayer = randomLevel();
node_type * preds[c_nMaxHeight];
node_type * succs[c_nMaxHeight];

while (true) {
int lFound = find(key, preds, succs);

if (lFound != -1) {
node_type *nodeFound = succs[lFound];
if (!nodeFound->marked()) {
while (!nodeFound->fully_linked()) {
}
return false;
}
continue;
}

int highestLocked = -1;
try {
node_type *pred, *succ, *prevPred = nullptr;
bool valid = true;

for (unsigned int layer = 0; valid && (layer <= topLayer); layer++) {
pred = preds[layer];
succ = succs[layer];

if (pred != prevPred) {
pred->lock.lock();
highestLocked = layer;
prevPred = pred;
}

valid = !pred->marked() && !succ->marked() && pred->next(layer) == succ;
}

if (!valid)
continue;

node_type *new_node = node_type::allocate_node(v, topLayer);
for (unsigned int layer = 0; layer <= topLayer; layer++) {
new_node->next(layer) = succs[layer];
preds[layer]->next(layer) = new_node;
}

new_node->set_fully_linked(true);
unlock(preds, highestLocked);

return true;
} catch (int e) {
unlock(preds, highestLocked);
}
}
}

bool remove(value_type v) {
key_type key = std::hash<value_type>{}(v);
node_type *nodeToDelete = nullptr;
bool isMarked = false;
unsigned int topLayer = 0;
node_type *preds[c_nMaxHeight];
node_type *succs[c_nMaxHeight];

while (true) {
int lFound = find(key, preds, succs);

if (isMarked || (lFound != -1 && okToDelete(succs[lFound], lFound))) {
if (!isMarked) {
nodeToDelete = succs[lFound];
topLayer = nodeToDelete->height();
nodeToDelete->lock.lock();

if (nodeToDelete->marked()) {
nodeToDelete->lock.unlock();
return false;
}

nodeToDelete->mark();
isMarked = true;
}

int highestLocked = -1;
try {
node_type *pred, *succ, *prevPred = nullptr;
bool valid = true;

for (unsigned int layer = 0; valid && (layer <= topLayer); layer++) {
pred = preds[layer];
succ = succs[layer];

if (pred != prevPred) {
pred->lock.lock();
highestLocked = layer;
prevPred = pred;
}

valid = !pred->marked() && pred->next(layer) == succ;
}

if (!valid)
continue;

for (unsigned int layer = topLayer; layer >= 0 && layer < c_nMaxHeight; layer--)
preds[layer]->next(layer) = nodeToDelete->next(layer);

nodeToDelete->lock.unlock();
node_type::dispose_node(nodeToDelete);
unlock(preds, highestLocked);
return true;
} catch (int e) {
unlock(preds, highestLocked);
}
} else
return false;
}
}

bool 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;

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

return (linked && !marked);
}

bool empty() {
node_type *succ = m_Head->next(0);

while (true) {
if (m_Head->next(0) == m_Tail)
return true;

if (succ->marked())
succ = m_Head->next(0);
else
return false;
}
}

protected:
void destroy() {
node_type *p = m_Head;
while (p) {
node_type *pNext = p->next(0);
node_type::dispose_node(p);
p = pNext;
}
}

unsigned int randomLevel() {
rand_height gen;

return gen();
}

bool okToDelete(node_type *candidate, int lFound) {
return (candidate->fully_linked() && candidate->height() == lFound && !candidate->marked());
}

int find(key_type key, node_type **preds, node_type **succs) {
int lFound = -1;
node_type *pred = m_Head;

for (unsigned int layer = c_nMaxHeight - 1; layer >= 0 && layer < c_nMaxHeight; layer--) {
node_type *curr = pred->next(layer);

while (key > curr->node_key()) {
pred = curr;
curr = pred->next(layer);
}

if (lFound == -1 && key == curr->node_key())
lFound = layer;

preds[layer] = pred;
succs[layer] = curr;
}

return lFound;
}

void unlock(node_type **preds, int highestLocked) {
for (int layer = 0; layer <= highestLocked; layer++)
preds[layer]->lock.unlock();
}

};

}} // namespace cds::container

#endif // #ifndef CDSLIB_CONTAINER_SKIP_LIST_SET_H
Loading