diff --git a/cds/intrusive/details/valois_list_base.h b/cds/intrusive/details/valois_list_base.h new file mode 100644 index 000000000..2d363dcf6 --- /dev/null +++ b/cds/intrusive/details/valois_list_base.h @@ -0,0 +1,216 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * 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. +*/ + +#include +#include +#include +#include +#include +#include + +#ifndef CDSLIB_INTRUSIVE_DETAILS_VALOIS_LIST_BASE_H +#define CDSLIB_INTRUSIVE_DETAILS_VALOIS_LIST_BASE_H + +namespace cds { namespace intrusive { + + namespace valois_list{ + + template + struct node + { + typedef T value_type; ///< Value type + typedef cds::details::marked_ptr marked_data_ptr; ///< marked pointer to the value + + atomics::atomic< node* > next; ///< pointer to next node in the list + atomics::atomic< marked_data_ptr > data; ///< pointer to user data, \p nullptr if the node is free + + node() + { + next.store( nullptr, atomics::memory_order_relaxed ); + data.store( marked_data_ptr( NULL ), atomics::memory_order_release ); + } + + node( value_type * pVal ) + { + next.store( nullptr, atomics::memory_order_relaxed ); + data.store( marked_data_ptr( pVal ), atomics::memory_order_release ); + } + + }; + + + template + struct stat { + typedef EventCounter event_counter; ///< Event counter type + + event_counter m_nInsertSuccess; ///< Number of success \p insert() operations + event_counter m_nInsertFailed; ///< Number of failed \p insert() operations + event_counter m_nInsertRetry; ///< Number of attempts to insert new item + event_counter m_nUpdateNew; ///< Number of new item inserted for \p update() + event_counter m_nUpdateExisting; ///< Number of existing item updates + event_counter m_nUpdateFailed; ///< Number of failed \p update() call + event_counter m_nUpdateRetry; ///< Number of attempts to \p update() the item + event_counter m_nUpdateMarked; ///< Number of attempts to \p update() logically deleted (marked) items + event_counter m_nEraseSuccess; ///< Number of successful \p erase(), \p unlink(), \p extract() operations + event_counter m_nEraseFailed; ///< Number of failed \p erase(), \p unlink(), \p extract() operations + event_counter m_nEraseRetry; ///< Number of attempts to \p erase() an item + event_counter m_nFindSuccess; ///< Number of successful \p find() and \p get() operations + event_counter m_nFindFailed; ///< Number of failed \p find() and \p get() operations + + event_counter m_nHelpingSuccess; ///< Number of successful help attempts to remove marked item during searching + event_counter m_nHelpingFailed; ///< Number if failed help attempts to remove marked item during searching + + //@cond + void onInsertSuccess() { ++m_nInsertSuccess; } + void onInsertFailed() { ++m_nInsertFailed; } + void onInsertRetry() { ++m_nInsertRetry; } + void onUpdateNew() { ++m_nUpdateNew; } + void onUpdateExisting() { ++m_nUpdateExisting; } + void onUpdateFailed() { ++m_nUpdateFailed; } + void onUpdateRetry() { ++m_nUpdateRetry; } + void onUpdateMarked() { ++m_nUpdateMarked; } + void onEraseSuccess() { ++m_nEraseSuccess; } + void onEraseFailed() { ++m_nEraseFailed; } + void onEraseRetry() { ++m_nEraseRetry; } + void onFindSuccess() { ++m_nFindSuccess; } + void onFindFailed() { ++m_nFindFailed; } + + void onHelpingSuccess() { ++m_nHelpingSuccess; } + void onHelpingFailed() { ++m_nHelpingFailed; } + //@endcond + }; + + /// \p MichaelList empty internal statistics + struct empty_stat { + //@cond + void onInsertSuccess() const {} + void onInsertFailed() const {} + void onInsertRetry() const {} + void onUpdateNew() const {} + void onUpdateExisting() const {} + void onUpdateFailed() const {} + void onUpdateRetry() const {} + void onUpdateMarked() const {} + void onEraseSuccess() const {} + void onEraseFailed() const {} + void onEraseRetry() const {} + void onFindSuccess() const {} + void onFindFailed() const {} + + void onHelpingSuccess() const {} + void onHelpingFailed() const {} + //@endcond + }; + + //@cond + template > + struct wrapped_stat { + typedef Stat stat_type; + + wrapped_stat( stat_type& st ) + : m_stat( st ) + {} + + void onInsertSuccess() { m_stat.onInsertSuccess(); } + void onInsertFailed() { m_stat.onInsertFailed(); } + void onInsertRetry() { m_stat.onInsertRetry(); } + void onUpdateNew() { m_stat.onUpdateNew(); } + void onUpdateExisting() { m_stat.onUpdateExisting(); } + void onUpdateFailed() { m_stat.onUpdateFailed(); } + void onUpdateRetry() { m_stat.onUpdateRetry(); } + void onUpdateMarked() { m_stat.onUpdateMarked(); } + void onEraseSuccess() { m_stat.onEraseSuccess(); } + void onEraseFailed() { m_stat.onEraseFailed(); } + void onEraseRetry() { m_stat.onEraseRetry(); } + void onFindSuccess() { m_stat.onFindSuccess(); } + void onFindFailed() { m_stat.onFindFailed(); } + + void onHelpingSuccess() { m_stat.onHelpingSuccess(); } + void onHelpingFailed() { m_stat.onHelpingFailed(); } + + stat_type& m_stat; + }; + + struct traits + { + /// Key comparison functor + /** + No default functor is provided. If the option is not specified, the \p less is used. + */ + typedef opt::none compare; + + /// Specifies binary predicate used for key compare. + /** + Default is \p std::less + */ + typedef opt::none less; + + /// Node allocator + typedef CDS_DEFAULT_ALLOCATOR node_allocator; + + /// Back-off strategy + typedef cds::backoff::Default back_off; + + /// Disposer for removing items + typedef opt::v::empty_disposer disposer; + + /// Internal statistics + /** + It's disabled + */ + typedef empty_stat stat; + + /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter or \p atomicity::cache_friendly_item_counter to enable item counting + typedef atomicity::empty_item_counter item_counter; + + /// C++ memory ordering model + /** + Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) + or \p opt::v::sequential_consistent (sequentially consisnent memory model). + */ + typedef opt::v::relaxed_ordering memory_model; + }; + + template + 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 + }; + + } +}} + +#endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_VALOIS_LIST_BASE_H diff --git a/cds/intrusive/impl/valois_list.h b/cds/intrusive/impl/valois_list.h new file mode 100644 index 000000000..c809def76 --- /dev/null +++ b/cds/intrusive/impl/valois_list.h @@ -0,0 +1,395 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * 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 CDS_VALOIS_LIST_H +#define CDS_VALOIS_LIST_H + +#include +#include + +namespace cds { + namespace intrusive { + + template + class ValoisList { + + public: + typedef GC gc; ///< Garbage collector + typedef T value_type; ///< type of value stored in the list + typedef Traits traits; ///< Traits template parameter + typedef valois_list::node node_type; ///< node type + + typedef typename opt::details::make_comparator::type key_comparator; + + typedef typename traits::disposer disposer; ///< disposer for \p value_type + typedef typename traits::back_off back_off; ///< back-off strategy + typedef typename traits::memory_model memory_model; ///< Memory ordering. See \p cds::opt::memory_model option + typedef typename traits::node_allocator node_allocator; ///< Node allocator + typedef typename traits::stat stat; ///< Internal statistics + + static CDS_CONSTEXPR const size_t c_nHazardPtrCount = 4; ///< Count of hazard pointer required for the algorithm + + protected: + typedef typename atomics::atomic atomic_node_ptr; + typedef typename node_type::marked_data_ptr marked_data_ptr; + + atomic_node_ptr list_head_node; ///< Head pointer + atomic_node_ptr m_pTail; ///< Tail pointer + + public: + friend class iterator; + + protected: + + class iterator { + friend class ValoisList; + + protected: + atomic_node_ptr current_node; // Valois target - current real node + atomic_node_ptr aux_pNode; // Valois pre_aux - aux node before the real node + atomic_node_ptr prev_node; // Valois pre_cell - real node before the current real node + typename gc::Guard current_guard; + typename gc::Guard prev_guard; + typename gc::Guard aux_guard; + + bool next() { + if (current_node.load()->next.load() == nullptr) { // if tail + current_guard.clear(); + prev_guard.clear(); + aux_guard.clear(); + return false; + } + prev_guard.copy(current_guard); + prev_node = prev_guard.protect(current_node); + aux_pNode = aux_guard.protect(current_node.load()->next); + + update_iterator(); + return true; + } + + + + public: + typedef typename cds::details::make_const_type::pointer value_ptr; + typedef typename cds::details::make_const_type::reference value_ref; + + iterator() + : current_node(nullptr), aux_pNode(nullptr), prev_node(nullptr) {} + + // node - only Head + iterator( node_type * node) { + set(node); + } + + void set(node_type * node){ + prev_node = node; + aux_pNode = node->next.load(); + + update_iterator(); + } + + void update_iterator() { + if (aux_pNode.load()->next == current_node) { + return; + } + node_type * new_aux = aux_pNode.load(); + node_type * new_cur = new_aux->next.load(atomics::memory_order_acquire); + + while ( new_cur->next != nullptr && new_cur->data == nullptr) { //while not last and is aux node + + prev_node.load()->next.compare_exchange_strong( + new_aux, + new_cur, + atomics::memory_order_seq_cst, + atomics::memory_order_seq_cst + ); + + new_aux = new_cur; + + new_cur = new_aux->next.load(atomics::memory_order_acquire); + + } + + current_node.store(new_cur); + current_guard.protect(current_node); + aux_pNode.store(new_aux); + aux_pNode = aux_guard.protect(aux_pNode); + + } + + }; + + public: + ValoisList() { + init_list(); + } + + + ~ValoisList() { + destroy(); + } + + iterator begin() { + return iterator(list_head_node); + } + + /** + * try insert in the position + * @param i + * @param val + * @return + */ + + template + bool search_insert(Q* val, Compare cmp) { + typename gc::Guard m_Guard; + iterator mIter; + mIter.set(list_head_node); + bool k = false; + while (true) { + + Q * nVal = (mIter.current_guard.template get())->data.load( + atomics::memory_order_acquire + ).ptr() ; + + if(nVal == nullptr){ + // for last node; + k = try_insert(mIter, val); + if (k){ + return k; + } else{ + // find again; + mIter.set(list_head_node); + continue; + } + } + + int const nCmp = cmp(*val, *nVal); + + if (nCmp == 0) { + return true; + } else if (nCmp < 0) { + k = try_insert(mIter, val); + + if (k){ + return k; + } else{ + // find again; + mIter.set(list_head_node); + } + + } else { + mIter.next(); + } + } + } + + bool try_insert(iterator& i, value_type * val) { + + node_type * real_node = new node_type(val); + node_type * aux_node = new node_type(); + + real_node->next = aux_node; + aux_node->next.store((i.current_guard.template get())); + + node_type * cur_node = (i.current_guard.template get()); + + bool insert_status = i.aux_pNode.load()->next.compare_exchange_strong( + cur_node, + real_node, + memory_model::memory_order_seq_cst, + memory_model::memory_order_seq_cst + ); + if(!insert_status){ + delete real_node; + delete aux_node; + } + + return insert_status; + } + + + bool insert( value_type &val ){ + return search_insert(&val, key_comparator() ); + } + + + /** + * delete value from linked list; + * @param value + * @return + */ + template + bool erase(Q* val, Compare cmp) { + iterator mIter; + mIter.set( list_head_node ); + //search node + while ((mIter.current_guard.template get())->next.load() != nullptr ) { + value_type * nVal = (mIter.current_guard.template get()) + ->data.load(atomics::memory_order_acquire ).ptr(); + int const nCmp = cmp( *val , *nVal ); + + if ( nCmp == 0 ){ + + bool result = try_erase(mIter); + if (result){ + return true; + } + else{ + //run again + mIter.set( list_head_node ); + } + } + else if ( nCmp > 0 ){ + // not found + return false; + } + else{ + mIter.next(); + } + } + // not found + return false; + } + + bool erase(value_type val){ + return erase(&val, key_comparator()); + } + + bool erase(value_type * val){ + return erase(val, key_comparator()); + } + + + template + bool contains( Q* val, Compare cmp) { + typename gc::Guard m_Guard; + iterator mIter; + mIter.set(list_head_node); + while ((mIter.current_guard.template get())->next.load() != nullptr ) { + value_type * nVal = (mIter.current_guard.template get())->data.load(atomics::memory_order_acquire ).ptr(); + int const nCmp = cmp( *val , *nVal ); + + if ( nCmp == 0 ){ + return true; + } + else if ( nCmp < 0 ){ + return false; + } + else{ + mIter.next(); + } + } + return false; + } + + bool contains(value_type &val) { + return contains( &val, key_comparator() ); + } + + bool empty() { + iterator mIter; + mIter.set(list_head_node); + + if ( mIter.next() ) { + return false; + } else { + return true; + } + } + + + + private: + + void init_list() { + node_type * aux_temp = new node_type(); + list_head_node.store(new node_type); + list_head_node.load()->next.store( aux_temp, atomics::memory_order_release); //link to aux node + m_pTail.store(new node_type); + aux_temp->next.store( m_pTail, atomics::memory_order_release ); + m_pTail.load()->next.store(nullptr, atomics::memory_order_release ); //link tail to nullptr + } + + void destroy() { + + node_type * tNode = list_head_node.load()->next.load(memory_model::memory_order_relaxed); + + while (tNode->next.load(memory_model::memory_order_relaxed) != nullptr) { + value_type * pVal = tNode->data.load(memory_model::memory_order_relaxed).ptr(); + if (pVal) { + + deleted(*pVal); + + tNode = list_head_node.load( + atomics::memory_order_release) + ->next.load(memory_model::memory_order_relaxed); + } + tNode = tNode->next.load(memory_model::memory_order_relaxed); + } + } + + bool try_erase(iterator& i) { + + node_type *d = (i.current_guard.template get()); + + node_type *n = (i.current_guard.template get())->next.load(atomics::memory_order_release); + bool r = i.aux_pNode.load()->next.compare_exchange_strong(d, n->next.load(), atomics::memory_order_seq_cst); + + if (!r){ + return false; + } + gc::template retire( d ); + gc::template retire( n ); + + return true; + } + + bool deleted (const value_type & number){ + node_type * next_node = list_head_node.load(); + node_type * next_aux_node; + int ind = 0; + do{ + next_aux_node = next_node->next.load(); + next_node = next_aux_node->next.load(); + if (number == *(next_node->data.load().ptr())){ + next_aux_node->next.store(next_node->next.load()->next.load()); + return true; + } + ind++; + } while (next_node->next.load() != nullptr); + return false; + } + }; + + } +} + +#endif //CDS_VALOIS_LIST_H diff --git a/cds/intrusive/valois_list_dhp.h b/cds/intrusive/valois_list_dhp.h new file mode 100644 index 000000000..3625fc31d --- /dev/null +++ b/cds/intrusive/valois_list_dhp.h @@ -0,0 +1,37 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * 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 CDSLIB_INTRUSIVE_VALOIS_LIST_DHP_H +#define CDSLIB_INTRUSIVE_VALOIS_LIST_DHP_H + +#include +#include + +#endif //CDSLIB_INTRUSIVE_VALOIS_LIST_DHP_H diff --git a/cds/intrusive/valois_list_hp.h b/cds/intrusive/valois_list_hp.h new file mode 100644 index 000000000..300612cf0 --- /dev/null +++ b/cds/intrusive/valois_list_hp.h @@ -0,0 +1,39 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * 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 CDSLIB_INTRUSIVE_VALOIS_LIST_HP_H +#define CDSLIB_INTRUSIVE_VALOIS_LIST_HP_H + + + +#include +#include + +#endif //CDSLIB_INTRUSIVE_VALOIS_LIST_HP_H diff --git a/test/unit/intrusive-list/CMakeLists.txt b/test/unit/intrusive-list/CMakeLists.txt index 3e588164b..0b24eca65 100644 --- a/test/unit/intrusive-list/CMakeLists.txt +++ b/test/unit/intrusive-list/CMakeLists.txt @@ -47,9 +47,21 @@ target_link_libraries(${UNIT_ILIST_ITERABLE} ${CDS_TEST_LIBRARIES}) add_test(NAME ${UNIT_ILIST_ITERABLE} COMMAND ${UNIT_ILIST_ITERABLE} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +# intrusive::ValoisList +set(UNIT_ILIST_VALOIS unit-ilist-valois) +set(UNIT_ILIST_VALOIS_SOURCES + ../main.cpp + intrusive_valois_hp.cpp + intrusive_valois_dhp.cpp + ) +add_executable(${UNIT_ILIST_VALOIS} ${UNIT_ILIST_VALOIS_SOURCES}) +target_link_libraries(${UNIT_ILIST_VALOIS} ${CDS_TEST_LIBRARIES}) +add_test(NAME ${UNIT_ILIST_VALOIS} COMMAND ${UNIT_ILIST_VALOIS} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) + add_custom_target( unit-ilist - DEPENDS + DEPENDS ${UNIT_ILIST_ITERABLE} ${UNIT_ILIST_LAZY} ${UNIT_ILIST_MICHAEL} -) + ${UNIT_ILIST_VALOIS} + ) diff --git a/test/unit/intrusive-list/intrusive_valois_dhp.cpp b/test/unit/intrusive-list/intrusive_valois_dhp.cpp new file mode 100644 index 000000000..8d8de3774 --- /dev/null +++ b/test/unit/intrusive-list/intrusive_valois_dhp.cpp @@ -0,0 +1,39 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * 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. +*/ + +#include "test_intrusive_iterable_list_hp.h" +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::DHP gc_type; + + +} // namespace diff --git a/test/unit/intrusive-list/intrusive_valois_hp.cpp b/test/unit/intrusive-list/intrusive_valois_hp.cpp new file mode 100644 index 000000000..19f5d309e --- /dev/null +++ b/test/unit/intrusive-list/intrusive_valois_hp.cpp @@ -0,0 +1,252 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * 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. +*/ + +#include "test_intrusive_list_hp.h" +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::HP gc_type; + + class IntrusiveValoisList_HP : public cds_test::intrusive_list_hp + { + public: + typedef cds_test::intrusive_list_hp::base_item< ci::valois_list::node< gc_type>> base_item; + typedef cds_test::intrusive_list_hp::member_item< ci::valois_list::node< gc_type>> member_item; + + protected: + void SetUp() + { + struct traits: public ci::valois_list::traits{}; + + typedef ci::ValoisList< gc_type, base_item, traits > list_type; + + // +1 - for guarded_ptr + cds::gc::hp::GarbageCollector::Construct( list_type::c_nHazardPtrCount + 1, 1, 16 ); + + cds::threading::Manager::attachThread(); + + } + + void TearDown() + { + + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + template + void test_simple_list(List& list) + { + + list.append_in_first(3); + list.append_in_position(10000,10); + + list.search(10000); + + list.deleted(50); + + list.print_all_by_iterator(); + } + + template + void test_list(List& list) + { + /** + * 1) testing method is empty + * 2) test adding 10 element in the container + * 3) test comtainig 10 element on the container + * 4) test deleting element in the containing + */ + + // test empty method(); + ASSERT_TRUE( list.empty()); + + static const size_t nSize = 20; + typedef typename List::value_type value_type; + + // insert and contains method + for ( int i = 0; i <= nSize; i++ ) { + int * index = new int32_t(i); + ASSERT_FALSE(list.contains(*index)); + list.insert(*index); + ASSERT_TRUE( list.contains(*index)); + ASSERT_FALSE( list.empty()); + + } + + // test adding in + for ( int i = 0; i <= nSize; i++ ) { + ASSERT_TRUE( list.contains(i)); + } + + // delete and contains method + for ( int i = 0; i <= nSize; i++ ) { + ASSERT_TRUE( list.contains(i)); + list.erase(i); + ASSERT_FALSE(list.contains(i)); + } + // test empty method(); + ASSERT_TRUE( list.empty()); + + } + + template + void revert_test_list(List& list){ + ASSERT_TRUE( list.empty()); + + static const size_t nSize = 20; + typedef typename List::value_type value_type; + + // insert and contains method + for ( int i = nSize; i >= 0 ; i-- ) { + int * index = new int32_t(i); + ASSERT_FALSE(list.contains(*index)); + list.insert(*index); + ASSERT_TRUE( list.contains(*index)); + ASSERT_FALSE( list.empty()); + } + + // test adding in + for ( int i = 0; i <= nSize ; i++ ) { + ASSERT_TRUE( list.contains(i)); + } + + // delete and contains method + for ( int i = 0; i <= nSize ; i++ ) { + ASSERT_TRUE( list.contains(i)); + list.erase(i); + ASSERT_FALSE(list.contains(i)); + } + // test empty method(); + ASSERT_TRUE( list.empty()); + } + + + template + void random_test_list(List& list){ + int items[10] = {4,7,6,8,2,9,3,1,0,5}; + + //insert + for(auto i : items){ + int * index = new int32_t(i); + ASSERT_FALSE( list.contains(i)); + ASSERT_TRUE(list.insert(*index)); + ASSERT_TRUE( list.contains(i)); + } + + //contains + for(auto i : items){ + ASSERT_TRUE(list.contains(i)); + } + + //delete + for ( int i = 0; i <= 9 ; i++ ) { + ASSERT_TRUE( list.contains(i)); + list.erase(i); + ASSERT_FALSE( list.contains(i)); + } + } + + struct traits: public ci::valois_list::traits{}; + /*typedef ci::ValoisList< gc_type, int, traits > list_stress_type;*/ + typedef ci::ValoisList< gc_type, int, traits > list_type; + + + struct val_and_id{ + std::thread::id thread_id; + int value; + + val_and_id(const std::thread::id & thread__id, const int & number){ + thread_id = thread__id; + value = number; + } + + bool operator == (const val_and_id & rhs)const { + return rhs.value == this->value && rhs.thread_id == this->thread_id; + } + + bool operator < (const val_and_id & rhs)const { + return rhs.value < this->value; + } + }; + typedef ci::ValoisList< gc_type, val_and_id, traits > list_stress_type; + + template + void complex_insdel_thread(List& list){ + cds::threading::Manager::attachThread(); + std::thread::id this_id = std::this_thread::get_id(); + for (int key=0; key < 10000; ++key ) { + val_and_id * input = new val_and_id(this_id,key); + list.insert(* input); + list.erase(* input); + } + cds::threading::Manager::detachThread(); + } + void complex_insdel_test(){ + list_stress_type lst; + + std::thread::id this_id = std::this_thread::get_id(); + + + int numThreads = 10; + std::vector threads; + + + for(int i = 0; i < numThreads; i++) + threads.push_back(std::thread(complex_insdel_thread, std::ref(lst))); + + for(int i = 0; i < numThreads; i++) + threads[i].join(); + + } + + TEST_F( IntrusiveValoisList_HP, base_hook ) + { + + struct traits: public ci::valois_list::traits{}; + + + list_type l2; + test_list(l2); + + list_type l3; + revert_test_list(l3); + + list_type l4; + random_test_list(l4); + + complex_insdel_test(); + + } + +} // namespace