diff --git a/containers_cpp/include/embb/containers/internal/multiset-inl.h b/containers_cpp/include/embb/containers/internal/multiset-inl.h new file mode 100644 index 0000000..9eb28bf --- /dev/null +++ b/containers_cpp/include/embb/containers/internal/multiset-inl.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2014-2015, 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_MULTISET_INL_H_ +#define EMBB_CONTAINERS_INTERNAL_MULTISET_INL_H_ + +#include +#include +#include + +namespace embb { +namespace containers { + +namespace internal { + +template< typename Type > +MultisetNode::MultisetNode(const Type & key, size_t count, node_ptr_t next) : + key_(key), count_(count), next_(next) { +} + +template< typename Type > +inline Type & MultisetNode::Key() const { + return key_; +} + +template< typename Type > +inline embb::base::Atomic & MultisetNode::Count() { + return count_; +} + +template< typename Type > +inline embb::base::Atomic & MultisetNode::Next() { + return next_; +} + +} // namespace internal + +template< typename Type, Type UndefinedKey, class ValuePool > +Multiset::Multiset(size_t capacity) : + llx_scx_(NUM_LLX_SCX_LINKS) { + internal::MultisetNode sentinel_node(UndefinedKey, 0, UNDEFINED_POINTER); + head_ = node_pool_.Allocate(sentinel_node); + tail_ = node_pool_.Allocate(sentinel_node); + head_->Next().Store(tail_); + tail_->Next().Store(UNDEFINED_POINTER); +} + +template< typename Type, Type UndefinedKey, class ValuePool > +bool Multiset::Search( + const Type key, + node_ptr_t & node, + node_ptr_t & next) { + node = head_; + next = tail_; + while (key > next->Key() && next->Key() != UndefinedKey) { + node = next; + next = next->Next(); + } + return (next->Key() != UndefinedKey); +} + +template< typename Type, Type UndefinedKey, class ValuePool > +size_t Multiset::Get(Type key) { + node_ptr_t node, next; + if (Search(key, node, next)) { + return next->Count().Load(); + } + return 0; +} + +template< typename Type, Type UndefinedKey, class ValuePool > +void Multiset::Insert(Type key, size_t count) { + if (count == 0) { + return; + } + while (true) { + node_ptr_t node, next, local_next; + Search(key, node, next); + // Key value is stored in node->next as head node + // is sentinel. + if (key == next->Key()) { + node_ptr_t local_next; + // Key already present in multiset, increment count of its node: + // LLX(r:next) + if (llx_scx_.TryLoadLinked(next, local_next)) { + internal::FixedSizeList linked_deps(1); + linked_deps.PushBack(next); + // SCX(fld:next->count, value:next->count + count, V:); + if (llx_scx_.TryStoreConditional( + next->Count(), + local_next->Count()->Load() + count, + linked_deps)) { + return; + } + } + } + else { + // Key not present in multiset yet, add node: + node_ptr_t local_node; + if (llx_scx_.TryLoadLinked(node, local_node) && next == local_node->Next()) { + internal::FixedSizeList linked_deps(1); + linked_deps.PushBack(node); + node_t new_node(internal::MultisetNode(key, count, next)); + node_ptr_t new_node_ptr = node_pool_.Allocate(new_node); + if (llx_scx_.TryStoreConditional(node->Next(), new_node_ptr, linked_deps)) { + return; + } + else { + // Insert failed, return new node object to pool: + node_pool_.Free(new_node_ptr); + } + } + } + } +} + +template< typename Type, Type UndefinedKey, class ValuePool > +bool Multiset::TryDelete(Type key, size_t count) { + if (count == 0) { + return false; + } + while (true) { + node_ptr_t node, next, local_node, local_next; + Search(key, node, next); + if (llx_scx_.TryLoadLinked(node, local_node) && + llx_scx_.TryLoadLinked(next, local_next) && + next == local_node->Next()) { + if (key != next->Key() || local_next->Count() < count) { + // Key not present in multiset or existing count less + // than amount of elements requested for deletion: + return false; + } + else if (local_next->Count() > count) { + // Inserting a new node with decremented count instead + // of decrementing the exising node's count to reduce + // number of linked dependencies in SCX. + internal::MultisetNode new_node(key, count, next); + node_ptr_t new_node_ptr = node_pool_.Allocate(new_node); + internal::FixedSizeList linked_deps(1); + linked_deps.PushBack(node); + internal::FixedSizeList finalize_deps(1); + finalize_deps.PushBack(next); + if (llx_scx_.TryStoreConditional( + node->Next(), new_node_ptr, linked_deps, finalize_deps)) { + return true; + } + } + else { + assert(local_next->Count() == count); + node_ptr_t local_next_next; + if (llx_scx_.TryLoadLinked(next->Next(), local_next_next)) { + node_ptr_t new_next_next_ptr = node_pool_.Allocate(*local_next_next); + internal::FixedSizeList linked_deps(3); + linked_deps.PushBack(node); + linked_deps.PushBack(next); + linked_deps.PushBack(local_next_next); + internal::FixedSizeList finalize_deps(2); + finalize_deps.PushBack(next); + finalize_deps.PushBack(local_next_next); + if (llx_scx_.TryStoreConditional( + node->Next(), new_next_next_ptr, linked_deps, finalize_deps) { + return true; + } + else { + // Delete failed, return new node object to pool and retry: + node_pool_.Free(new_next_next_ptr); + } + } + } + } + } +} + +} // namespace containers +} // namespace embb + +#endif // EMBB_CONTAINERS_INTERNAL_MULTISET_INL_H_ \ No newline at end of file diff --git a/containers_cpp/include/embb/containers/multiset.h b/containers_cpp/include/embb/containers/multiset.h new file mode 100644 index 0000000..fff7cc3 --- /dev/null +++ b/containers_cpp/include/embb/containers/multiset.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014-2015, 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_MULTISET_H_ +#define EMBB_CONTAINERS_MULTISET_H_ + +#include +#include +#include +#include +#include +#include + +namespace embb { +namespace containers { + +namespace internal { + +template< typename Type > +class MultisetNode { + private: + typedef primitives::LlxScxRecord< internal::MultisetNode > * node_ptr_t; + + public: + MultisetNode(const Type & key, size_t count, node_ptr_t next); + + inline Type & Key(); + + inline embb::base::Atomic * Count() const; + + inline embb::base::Atomic * Next(); + + private: + Type key_; + embb::base::Atomic count_; + embb::base::Atomic next_; +}; + +} // namespace internal + +template< + typename Type, + Type UndefinedKey, + typename ValuePool = embb::containers::LockFreeTreeValuePool< bool, false > +> +class Multiset { + + private: + typedef primitives::LlxScxRecord< internal::MultisetNode > node_t; + typedef primitives::LlxScxRecord< internal::MultisetNode > * node_ptr_t; + static const size_t UNDEFINED_POINTER = static_cast(0); + static const size_t NUM_LLX_SCX_LINKS = 3; + + public: + Multiset(size_t capacity); + + size_t Get(Type key); + + void Insert(Type key, size_t count); + + bool TryDelete(Type key, size_t count); + + private: + void Search( + const Type key, + ///< [IN] The element key to search in the multiset + node_ptr_t & node, + ///< [OUT] The node in the multiset representing the given key + node_ptr_t & next + ///< [OUT] The next pointer of the node in the multiset representing the given key + ); + + private: + ObjectPool< node_t, ValuePool > node_pool_; + node_ptr_t head_; + node_ptr_t tail_; + primitives::LlxScx< MultisetNode > llx_scx_; +}; + +} // namespace containers +} // namespace embb + +// #include + +#endif // EMBB_CONTAINERS_MULTISET_H_ \ No newline at end of file diff --git a/containers_cpp/test/multiset_test.cc b/containers_cpp/test/multiset_test.cc new file mode 100644 index 0000000..c0a19fa --- /dev/null +++ b/containers_cpp/test/multiset_test.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014-2015, 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. + */ + +#if 0 + +#include +#include + +namespace embb { +namespace containers { +namespace test { + +MultisetTest::MultisetTest() : + num_threads_(static_cast( + partest::TestSuite::GetDefaultNumThreads())) { + CreateUnit("SerialTest").Add(&MultisetTest::SerialTest, this); +} + +void MultisetTest::SerialTest() { + Multiset multiset(3); + multiset.Insert('a', 5); + multiset.Insert('b', 5); + multiset.Insert('c', 5); + PT_ASSERT(multiset.TryDelete('a', 4)); + PT_ASSERT(multiset.TryDelete('b', 4)); + PT_ASSERT(multiset.TryDelete('c', 4)); + PT_ASSERT(multiset.Get('a') == 1); + PT_ASSERT(multiset.Get('b') == 1); + PT_ASSERT(multiset.Get('c') == 1); + PT_ASSERT(multiset.TryDelete('a', 1)); + PT_ASSERT(multiset.TryDelete('b', 1)); + PT_ASSERT(multiset.TryDelete('c', 1)); +} + +} // namespace test +} // namespace containers +} // namespace embb + +#endif diff --git a/containers_cpp/test/multiset_test.h b/containers_cpp/test/multiset_test.h new file mode 100644 index 0000000..385b90f --- /dev/null +++ b/containers_cpp/test/multiset_test.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2015, 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 CONTAINERS_CPP_TEST_MULTISET_TEST_H_ +#define CONTAINERS_CPP_TEST_MULTISET_TEST_H_ + +#include +#include + +namespace embb { +namespace containers { +namespace test { + +class MultisetTest : public partest::TestCase { + + public: + /** + * Adds test methods. + */ + MultisetTest(); + + private: + void SerialTest(); + + int num_threads_; + +}; +} // namespace test +} // namespace containers +} // namespace embb + +#endif // CONTAINERS_CPP_TEST_MULTISET_TEST_H_