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

Conversation

Rextuz
Copy link

@Rextuz Rextuz commented Jan 27, 2018

Implementation of SkipListSet based on fine-grained locks. The algorithm is based on the imlementation from the paper [2007] Herlihy, Lev, Luchangco, Shavit A Provably Correct Scalable Concurrent Skip List.

Contributors:
Max Kanushin,
Oleg Kurishev,
Efim Cherepanov.

@Rextuz
Copy link
Author

Rextuz commented Jan 28, 2018

Code in Java
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

public class LazySkipListSet {

    public static final int max_height = 4;
    private static int randomSeed = new Random().nextInt() | 0x0100;

    private Node lSentinel = new Node(Integer.MIN_VALUE, max_height);
    private Node rSentinel = new Node(Integer.MAX_VALUE, max_height);

    public LazySkipListSet() {
        for (int layer = 0; layer < max_height; layer++)
            lSentinel.nexts[layer] = rSentinel;
    }

    private int findNode(int v, Node preds[], Node succs[]) {
        int lFound = -1;
        Node pred = lSentinel;

        for (int layer = max_height - 1; layer >= 0; layer--) {
            Node curr = pred.nexts[layer];

            while (v > curr.key) {
                pred = curr;
                curr = pred.nexts[layer];
            }

            if (lFound == -1 && v == curr.key)
                lFound = layer;

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

        return lFound;
    }

    private int _randomLevel() {
        int x = randomSeed;
        x ^= x << 13;
        x ^= x >>> 17;
        randomSeed = x ^= x << 5;
        if ((x & 0x8001) != 0) // test highest and lowest bits
            return 0;
        int level = 1;
        while (((x >>>= 1) & 1) != 0) ++level;
        return level;
    }

    public int randomLevel() {
        // FIXME
        int level = _randomLevel();

        if (level == max_height - 1) {
            int a = new Random().nextInt(2);
            if (a == 0)
                return max_height - 2;
        }

        if (level >= max_height)
            return max_height - 1;

        return level;
    }

    public boolean add(int v) {
        int topLayer = randomLevel();
        Node preds[] = new Node[max_height];
        Node succs[] = new Node[max_height];

        while (true) {
            int lFound = findNode(v, preds, succs);

            if (lFound != -1) {
                Node nodeFound = succs[lFound];
                if (!nodeFound.marked) {
                    while (!nodeFound.fullyLinked) {
                    }
                    return false;
                }
                continue;
            }

            int highestLocked = -1;
            try {
                Node pred, succ, prevPred = null;
                boolean valid = true;

                for (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.nexts[layer] == succ;
                }

                if (!valid)
                    continue;

                Node newNode = new Node(v, topLayer);
                for (int layer = 0; layer <= topLayer; layer++) {
                    newNode.nexts[layer] = succs[layer];
                    preds[layer].nexts[layer] = newNode;
                }

                newNode.fullyLinked = true;
                return true;
            } finally {
                unlock(preds, highestLocked);
            }
        }
    }

    private void unlock(Node preds[], int highestLocked) {
        for (int layer = 0; layer <= highestLocked; layer++)
            if (preds[layer].lock.isHeldByCurrentThread())
                preds[layer].lock.unlock();
    }

    public boolean remove(int v) {
        Node nodeToDelete = null;
        boolean isMarked = false;
        int topLayer = -1;
        Node preds[] = new Node[max_height];
        Node succs[] = new Node[max_height];

        while (true) {
            int lFound = findNode(v, preds, succs);

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

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

                    nodeToDelete.marked = true;
                    isMarked = true;
                }

                int highestLocked = -1;
                try {
                    Node pred, succ, prevPred = null;
                    boolean valid = true;

                    for (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.nexts[layer] == succ;
                    }

                    if (!valid)
                        continue;

                    for (int layer = topLayer; layer >= 0; layer--)
                        preds[layer].nexts[layer] = nodeToDelete.nexts[layer];

                    nodeToDelete.lock.unlock();
                    return true;
                } finally {
                    unlock(preds, highestLocked);
                }
            } else
                return false;
        }
    }

    private boolean okToDelete(Node candidate, int lFound) {
        return (candidate.fullyLinked && candidate.topLayer == lFound && !candidate.marked);
    }

    public boolean contains(int v) {
        Node preds[] = new Node[max_height];
        Node succs[] = new Node[max_height];
        int lFound = findNode(v, preds, succs);

        return (lFound != -1 && succs[lFound].fullyLinked && !succs[lFound].marked);
    }

    public boolean isEmpty() {
        Node succ = lSentinel.nexts[0];

        while (true) {
            if (lSentinel.nexts[0] == rSentinel)
                return true;

            if (succ.marked)
                succ = lSentinel.nexts[0];
            else
                return false;
        }
    }

    class Node {
        int key;
        int topLayer;
        Node nexts[];
        volatile boolean marked;
        volatile boolean fullyLinked; // false
        ReentrantLock lock = new ReentrantLock();

        Node(int v, int topLayer) {
            this.key = v;
            this.topLayer = topLayer;

            nexts = new Node[topLayer + 1];
        }
    }
}

@Rextuz
Copy link
Author

Rextuz commented Jan 28, 2018

Добрый день, возникли проблемы с запуском тестов для нашего контейнера.

Ссылка на юнит-тесты: Rextuz#1
Ссылка на стресс-тесты: Rextuz#2
В обоих случаях происходит ошибка сборки, когда в конструктор std::hash передается нестандартный тип данных:

In file included from /home/rextuz/Documents/git/libcds/test/stress/set/insdel_find/../set_type_lazy_skip_list.h:36:0,
                 from /home/rextuz/Documents/git/libcds/test/stress/set/insdel_find/set_insdelfind_lazy_skip.cpp:32:
/home/rextuz/Documents/git/libcds/cds/container/lazy_skip_list_set_dhp.h: In instantiation of ‘bool cds::container::LazySkipListSet<GC, T, Traits>::insert(cds::container::LazySkipListSet<GC, T, Traits>::value_type) [with GC = cds::gc::DHP; T = set::set_type_base<long unsigned int, long unsigned int>::key_val; Traits = set::set_type<set::tag_SkipListSet, long unsigned int, long unsigned int>::traits_LazySkipListSet_turbo32; cds::container::LazySkipListSet<GC, T, Traits>::value_type = set::set_type_base<long unsigned int, long unsigned int>::key_val]’:
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_find/set_insdelfind.h:154:21:   required from ‘void set::Set_InsDelFind::do_test(Set&) [with Set = set::SkipListSet<cds::gc::DHP, set::set_type_base<long unsigned int, long unsigned int>::key_val, set::set_type<set::tag_SkipListSet, long unsigned int, long unsigned int>::traits_LazySkipListSet_turbo32>]’
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_find/set_insdelfind.h:214:13:   required from ‘void set::Set_InsDelFind::run_test() [with Set = set::SkipListSet<cds::gc::DHP, set::set_type_base<long unsigned int, long unsigned int>::key_val, set::set_type<set::tag_SkipListSet, long unsigned int, long unsigned int>::traits_LazySkipListSet_turbo32>]’
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_find/set_insdelfind_lazy_skip.cpp:36:5:   required from here
/home/rextuz/Documents/git/libcds/cds/container/lazy_skip_list_set_dhp.h:50:51: error: use of deleted function ‘std::hash<set::set_type_base<long unsigned int, long unsigned int>::key_val>::hash()’
             key_type key = std::hash<value_type>{}(v);

Как стоит бороться с такой ситуацией?

В одном из стресс-тестов при сборке по каким-то причинам происходит вызов конструктора node с параметрами K const& k, T const& v, который у нас не реализован (и не должен быть?). Почему подобное может происходить?

                 from /home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/../set_type_lazy_skip_list.h:36,
                 from /home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/set_insdel_string_lazy_skip.cpp:32:
/home/rextuz/Documents/git/libcds/cds/container/details/lazy_skip_list_set_base.h: In instantiation of ‘cds::container::lazy_skip_list_set::node<GC, T, Lock>::node() [with GC = cds::gc::DHP; T = set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val; Lock = std::recursive_mutex]’:
/home/rextuz/Documents/git/libcds/cds/details/allocator.h:153:35:   required from ‘cds::details::Allocator<T, Alloc>::value_type* cds::details::Allocator<T, Alloc>::Construct(void*, const S& ...) [with S = {}; T = cds::container::lazy_skip_list_set::node<cds::gc::DHP, set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val, std::recursive_mutex>; Alloc = std::allocator<int>; cds::details::Allocator<T, Alloc>::value_type = cds::container::lazy_skip_list_set::node<cds::gc::DHP, set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val, std::recursive_mutex>]’
/home/rextuz/Documents/git/libcds/cds/details/allocator.h:73:33:   required from ‘cds::details::Allocator<T, Alloc>::value_type* cds::details::Allocator<T, Alloc>::New(const S& ...) [with S = {}; T = cds::container::lazy_skip_list_set::node<cds::gc::DHP, set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val, std::recursive_mutex>; Alloc = std::allocator<int>; cds::details::Allocator<T, Alloc>::value_type = cds::container::lazy_skip_list_set::node<cds::gc::DHP, set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val, std::recursive_mutex>]’
/home/rextuz/Documents/git/libcds/cds/container/details/lazy_skip_list_set_base.h:120:26:   required from ‘static cds::container::lazy_skip_list_set::node<GC, T, Lock>* cds::container::lazy_skip_list_set::node<GC, T, Lock>::allocate_node(cds::container::lazy_skip_list_set::node<GC, T, Lock>::key_type) [with GC = cds::gc::DHP; T = set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val; Lock = std::recursive_mutex; cds::container::lazy_skip_list_set::node<GC, T, Lock>::node_ptr = cds::container::lazy_skip_list_set::node<cds::gc::DHP, set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val, std::recursive_mutex>*; cds::container::lazy_skip_list_set::node<GC, T, Lock>::key_type = long unsigned int]’
/home/rextuz/Documents/git/libcds/cds/container/details/lazy_skip_list_set_base.h:144:50:   required from ‘static cds::container::lazy_skip_list_set::node<GC, T, Lock>* cds::container::lazy_skip_list_set::node<GC, T, Lock>::min_key() [with GC = cds::gc::DHP; T = set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val; Lock = std::recursive_mutex]’
/home/rextuz/Documents/git/libcds/cds/container/lazy_skip_list_set_dhp.h:38:40:   required from ‘cds::container::LazySkipListSet<GC, T, Traits>::LazySkipListSet() [with GC = cds::gc::DHP; T = set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val; Traits = set::set_type<set::tag_SkipListSet, std::__cxx11::basic_string<char>, long unsigned int>::traits_LazySkipListSet_turbo32]’
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/../set_type_lazy_skip_list.h:50:9:   required from ‘set::SkipListSet<GC, T, Traits>::SkipListSet(const Config&) [with Config = set::Set_InsDel_string; GC = cds::gc::DHP; T = set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val; Traits = set::set_type<set::tag_SkipListSet, std::__cxx11::basic_string<char>, long unsigned int>::traits_LazySkipListSet_turbo32]’
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/set_insdel_string.h:484:17:   required from ‘void set::Set_InsDel_string::run_test() [with Set = set::SkipListSet<cds::gc::DHP, set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val, set::set_type<set::tag_SkipListSet, std::__cxx11::basic_string<char>, long unsigned int>::traits_LazySkipListSet_turbo32>]’
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/set_insdel_string_lazy_skip.cpp:36:5:   required from here
/home/rextuz/Documents/git/libcds/cds/container/details/lazy_skip_list_set_base.h:52:55: error: no matching function for call to ‘set::set_type_base<std::__cxx11::basic_string<char>, long unsigned int>::key_val::key_val()’
             node() : _marked(false), fullyLinked(false) {
                                                       ^
In file included from /home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/set_insdel_string.h:31:0,
                 from /home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/set_insdel_string_lazy_skip.cpp:31:
/home/rextuz/Documents/git/libcds/test/stress/set/insdel_string/../set_type.h:190:13: note: candidate: template<class K, class T> set::set_type_base<Key, Value>::key_val::key_val(const K&, const T&)
             key_val( K const& k, T const& v ): key(k), val(v) {}```

@Rextuz Rextuz force-pushed the lazy_skip_list_integration branch from 570616f to ca80b94 Compare January 28, 2018 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant