From: khizmax Date: Mon, 1 Aug 2016 08:46:02 +0000 (+0300) Subject: Merge branch 'dev' into integration X-Git-Tag: v2.2.0~159^2~3 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=a4d098afe1cfb4cfb6359c4bb4e22d8d9d75f0b5;hp=be79f320440993ee4a9f504e3c623315f88a1102;p=libcds.git Merge branch 'dev' into integration --- diff --git a/cds/container/details/lazy_list_base.h b/cds/container/details/lazy_list_base.h index 5c1521ae..f21b4594 100644 --- a/cds/container/details/lazy_list_base.h +++ b/cds/container/details/lazy_list_base.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_DETAILS_LAZY_LIST_BASE_H @@ -37,10 +37,23 @@ namespace cds { namespace container { - /// LazyList ordered list related definitions + /// \p LazyList ordered list related definitions /** @ingroup cds_nonintrusive_helper */ namespace lazy_list { + + /// \p LazyList internal statistics, see \p cds::intrusive::lazy_list::stat + template + using stat = cds::intrusive::lazy_list::stat< EventCounter >; + + /// \p LazyList empty internal statistics, see \p cds::intrusive::lazy_list::empty_stat + typedef cds::intrusive::lazy_list::empty_stat empty_stat; + + //@cond + template > + using wrapped_stat = cds::intrusive::lazy_list::wrapped_stat< Stat >; + //@endif + /// LazyList traits /** Either \p compare or \p less or both must be specified. @@ -88,10 +101,17 @@ namespace cds { namespace container { /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting typedef atomicity::empty_item_counter item_counter; + /// Internal statistics + /** + By default, internal statistics is disabled (\p lazy_list::empty_stat). + Use \p lazy_list::stat to enable it. + */ + typedef empty_stat stat; + /// 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). + or \p opt::v::sequential_consistent (sequentially consistent memory model). */ typedef opt::v::relaxed_ordering memory_model; @@ -125,9 +145,11 @@ namespace cds { namespace container { - \p opt::back_off - back-off strategy used. If the option is not specified, \p cds::backoff::Default is used. - \p opt::item_counter - the type of item counting feature. Default is disabled (\p atomicity::empty_item_counter). To enable item counting use \p atomicity::item_counter. + - \p opt::stat - internal statistics. By default, it is disabled (\p lazy_list::empty_stat). + To enable it use \p lazy_list::stat - \p opt::allocator - the allocator used for creating and freeing list's item. Default is \ref CDS_DEFAULT_ALLOCATOR macro. - \p opt::memory_model - 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). + or \p opt::v::sequential_consistent (sequentially consistent memory model). */ template struct make_traits { @@ -153,7 +175,7 @@ namespace cds { namespace container { // Tag for selecting lazy list implementation /** - This struct is empty and it is used only as a tag for selecting LazyList + This empty struct is used only as a tag for selecting \p LazyList as ordered list implementation in declaration of some classes. See \p split_list::traits::ordered_list as an example. diff --git a/cds/container/details/make_michael_kvlist.h b/cds/container/details/make_michael_kvlist.h index b1513061..385a2c93 100644 --- a/cds/container/details/make_michael_kvlist.h +++ b/cds/container/details/make_michael_kvlist.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_DETAILS_MAKE_MICHAEL_KVLIST_H diff --git a/cds/container/details/make_michael_list.h b/cds/container/details/make_michael_list.h index 20fea0dc..e779c607 100644 --- a/cds/container/details/make_michael_list.h +++ b/cds/container/details/make_michael_list.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_DETAILS_MAKE_MICHAEL_LIST_H diff --git a/cds/container/details/michael_list_base.h b/cds/container/details/michael_list_base.h index 7169c353..2a64ba44 100644 --- a/cds/container/details/michael_list_base.h +++ b/cds/container/details/michael_list_base.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_DETAILS_MICHAEL_LIST_BASE_H @@ -41,10 +41,23 @@ namespace cds { namespace container { /** @ingroup cds_nonintrusive_helper */ namespace michael_list { + + /// \p MichaelList internal statistics, see \p cds::intrusive::michael_list::stat + template + using stat = cds::intrusive::michael_list::stat< EventCounter >; + + /// \p MichaelList empty internal statistics, see \p cds::intrusive::michael_list::empty_stat + typedef cds::intrusive::michael_list::empty_stat empty_stat; + + //@cond + template > + using wrapped_stat = cds::intrusive::michael_list::wrapped_stat< Stat >; + //@endif + /// MichaelList traits struct traits { - typedef CDS_DEFAULT_ALLOCATOR allocator ; ///< allocator used to allocate new node + typedef CDS_DEFAULT_ALLOCATOR allocator; ///< allocator used to allocate new node /// Key comparison functor /** @@ -64,6 +77,13 @@ namespace cds { namespace container { /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting typedef atomicity::empty_item_counter item_counter; + /// Internal statistics + /** + By default, internal statistics is disabled (\p michael_list::empty_stat). + Use \p michael_list::stat to enable it. + */ + typedef empty_stat stat; + /// C++ memory ordering model /** Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) @@ -86,6 +106,20 @@ namespace cds { namespace container { /// Metafunction converting option list to \p michael_list::traits /** + Supported \p Options are: + - \p opt::compare - key comparison functor. No default functor is provided. + If the option is not specified, the \p opt::less is used. + - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less. + - \p opt::allocator - an allocator, default is \p CDS_DEFAULT_ALLOCATOR + - \p opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used. + - \p opt::item_counter - the type of item counting feature. Default is disabled (\p atomicity::empty_item_counter). + To enable item counting use \p atomicity::item_counter. + - \p opt::stat - internal statistics. By default, it is disabled (\p michael_list::empty_stat). + To enable it use \p michael_list::stat + - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) + or \p opt::v::sequential_consistent (sequentially consistent memory model). + - \p opt::rcu_check_deadlock - a deadlock checking policy for \ref cds_intrusive_MichaelList_rcu "RCU-based MichaelList" + Default is \p opt::v::rcu_throw_deadlock */ template struct make_traits { diff --git a/cds/container/impl/lazy_kvlist.h b/cds/container/impl/lazy_kvlist.h index 5e449387..59947bb9 100644 --- a/cds/container/impl/lazy_kvlist.h +++ b/cds/container/impl/lazy_kvlist.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_IMPL_LAZY_KVLIST_H @@ -133,6 +133,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counter type typedef typename maker::key_comparator key_comparator; ///< key comparing functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See \p cds::opt::memory_model + typedef typename base_class::stat stat; ///< Internal statistics static CDS_CONSTEXPR const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm @@ -371,6 +372,13 @@ namespace cds { namespace container { LazyKVList() {} + //@cond + template >::value >> + explicit LazyKVList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// Destructor clears the list ~LazyKVList() { @@ -753,6 +761,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/impl/lazy_list.h b/cds/container/impl/lazy_list.h index f0d4bbf1..6b7efb57 100644 --- a/cds/container/impl/lazy_list.h +++ b/cds/container/impl/lazy_list.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_IMPL_LAZY_LIST_H @@ -140,6 +140,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics static CDS_CONSTEXPR const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm @@ -356,6 +357,13 @@ namespace cds { namespace container { LazyList() {} + //@cond + template >::value >> + explicit LazyList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// Destructor clears the list ~LazyList() { @@ -746,6 +754,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/impl/michael_kvlist.h b/cds/container/impl/michael_kvlist.h index 28f18ef6..37f7a499 100644 --- a/cds/container/impl/michael_kvlist.h +++ b/cds/container/impl/michael_kvlist.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_IMPL_MICHAEL_KVLIST_H @@ -136,6 +136,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics static CDS_CONSTEXPR const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm @@ -357,7 +358,14 @@ namespace cds { namespace container { MichaelKVList() {} - /// List desctructor + //@cond + template >::value >> + explicit MichaelKVList( Stat& st ) + : base_class( st ) + {} + //@endcond + + /// List destructor /** Clears the list */ @@ -766,6 +774,12 @@ namespace cds { namespace container { base_class::clear(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + protected: //@cond bool insert_node_at( head_type& refHead, node_type * pNode ) diff --git a/cds/container/impl/michael_list.h b/cds/container/impl/michael_list.h index 514dbfd8..27c87b90 100644 --- a/cds/container/impl/michael_list.h +++ b/cds/container/impl/michael_list.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_IMPL_MICHAEL_LIST_H @@ -136,6 +136,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See \p cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics static CDS_CONSTEXPR const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm @@ -335,6 +336,13 @@ namespace cds { namespace container { MichaelList() {} + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// List destructor /** Clears the list @@ -733,6 +741,12 @@ namespace cds { namespace container { base_class::clear(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + protected: //@cond bool insert_node( node_type * pNode ) diff --git a/cds/container/lazy_kvlist_nogc.h b/cds/container/lazy_kvlist_nogc.h index 3f0e9319..c2150836 100644 --- a/cds/container/lazy_kvlist_nogc.h +++ b/cds/container/lazy_kvlist_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_LAZY_KVLIST_NOGC_H @@ -85,6 +85,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics static CDS_CONSTEXPR bool const c_bSort = base_class::c_bSort; ///< List type: ordered (\p true) or unordered (\p false) protected: @@ -330,6 +331,13 @@ namespace cds { namespace container { LazyKVList() {} + //@cond + template >::value >> + explicit LazyKVList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// Desctructor clears the list ~LazyKVList() { @@ -522,6 +530,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list /** Post-condition: the list is empty diff --git a/cds/container/lazy_kvlist_rcu.h b/cds/container/lazy_kvlist_rcu.h index 1a3f60ec..155237c4 100644 --- a/cds/container/lazy_kvlist_rcu.h +++ b/cds/container/lazy_kvlist_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_LAZY_KVLIST_RCU_H @@ -132,6 +132,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics typedef typename base_class::rcu_check_deadlock rcu_check_deadlock ; ///< RCU deadlock checking policy typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock @@ -357,6 +358,13 @@ namespace cds { namespace container { LazyKVList() {} + //@cond + template >::value >> + explicit LazyKVList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// Destructor clears the list ~LazyKVList() { @@ -764,6 +772,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/lazy_list_nogc.h b/cds/container/lazy_list_nogc.h index 469159d6..4227d8b3 100644 --- a/cds/container/lazy_list_nogc.h +++ b/cds/container/lazy_list_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_LAZY_LIST_NOGC_H @@ -77,11 +77,13 @@ namespace cds { namespace container { typedef T value_type; ///< Type of value stored in the list typedef Traits traits; ///< List traits - typedef typename base_class::back_off back_off; ///< Back-off strategy used - typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes - typedef typename base_class::item_counter item_counter; ///< Item counting policy used - typedef typename maker::key_comparator key_comparator; ///< key comparing functor - typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::back_off back_off; ///< Back-off strategy used + typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes + typedef typename base_class::item_counter item_counter; ///< Item counting policy used + typedef typename maker::key_comparator key_comparator; ///< key comparing functor + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics + static CDS_CONSTEXPR bool const c_bSort = base_class::c_bSort; ///< List type: ordered (\p true) or unordered (\p false) protected: @@ -303,6 +305,13 @@ namespace cds { namespace container { LazyList() {} + //@cond + template >::value >> + explicit LazyList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// Desctructor clears the list ~LazyList() { @@ -438,6 +447,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/lazy_list_rcu.h b/cds/container/lazy_list_rcu.h index 49bbaaad..0c5ed541 100644 --- a/cds/container/lazy_list_rcu.h +++ b/cds/container/lazy_list_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_LAZY_LIST_RCU_H @@ -136,11 +136,12 @@ namespace cds { namespace container { typedef T value_type; ///< Type of value stored in the list typedef Traits traits; ///< List traits - typedef typename base_class::back_off back_off; ///< Back-off strategy - typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes - typedef typename base_class::item_counter item_counter; ///< Item counting policy used - typedef typename maker::key_comparator key_comparator; ///< key compare functor - typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::back_off back_off; ///< Back-off strategy + typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes + typedef typename base_class::item_counter item_counter; ///< Item counting policy used + typedef typename maker::key_comparator key_comparator; ///< key compare functor + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics typedef typename base_class::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock @@ -351,6 +352,13 @@ namespace cds { namespace container { LazyList() {} + //@cond + template >::value >> + explicit LazyList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// Desctructor clears the list ~LazyList() { @@ -762,6 +770,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/michael_kvlist_nogc.h b/cds/container/michael_kvlist_nogc.h index 53f71b0b..89974bf5 100644 --- a/cds/container/michael_kvlist_nogc.h +++ b/cds/container/michael_kvlist_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_MICHAEL_KVLIST_NOGC_H @@ -112,6 +112,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics protected: //@cond @@ -346,7 +347,14 @@ namespace cds { namespace container { MichaelKVList() {} - /// List desctructor + //@cond + template >::value >> + explicit MichaelKVList( Stat& st ) + : base_class( st ) + {} + //@endcond + + /// List destructor /** Clears the list */ @@ -519,6 +527,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/michael_kvlist_rcu.h b/cds/container/michael_kvlist_rcu.h index 175cc234..2dbe9ffe 100644 --- a/cds/container/michael_kvlist_rcu.h +++ b/cds/container/michael_kvlist_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_MICHAEL_KVLIST_RCU_H @@ -138,6 +138,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See \p michael_list::traits::memory_model + typedef typename base_class::stat stat; ///< Internal statistics typedef typename base_class::rcu_check_deadlock rcu_check_deadlock ; ///< RCU deadlock checking policy typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock @@ -367,7 +368,14 @@ namespace cds { namespace container { MichaelKVList() {} - /// List desctructor + //@cond + template >::value >> + explicit MichaelKVList( Stat& st ) + : base_class( st ) + {} + //@endcond + + /// List destructor /** Clears the list */ @@ -817,6 +825,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list /** Post-condition: the list is empty diff --git a/cds/container/michael_list_nogc.h b/cds/container/michael_list_nogc.h index b7fbbf6e..1bf897c7 100644 --- a/cds/container/michael_list_nogc.h +++ b/cds/container/michael_list_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_MICHAEL_LIST_NOGC_H @@ -98,6 +98,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics protected: //@cond @@ -301,7 +302,14 @@ namespace cds { namespace container { MichaelList() {} - /// List desctructor + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : base_class( st ) + {} + //@endcond + + /// List destructor /** Clears the list */ @@ -418,6 +426,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/michael_list_rcu.h b/cds/container/michael_list_rcu.h index 61d16235..861da797 100644 --- a/cds/container/michael_list_rcu.h +++ b/cds/container/michael_list_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_MICHAEL_LIST_RCU_H @@ -148,6 +148,7 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy used typedef typename maker::key_comparator key_comparator; ///< key comparison functor typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::stat stat; ///< Internal statistics typedef typename base_class::rcu_check_deadlock rcu_check_deadlock ; ///< RCU deadlock checking policy typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock @@ -361,6 +362,13 @@ namespace cds { namespace container { MichaelList() {} + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : base_class( st ) + {} + //@endcond + /// List destructor /** Clears the list @@ -777,6 +785,12 @@ namespace cds { namespace container { return base_class::size(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return base_class::statistics(); + } + /// Clears the list void clear() { diff --git a/cds/container/mspriority_queue.h b/cds/container/mspriority_queue.h index 3ebcb3c7..77e7ea00 100644 --- a/cds/container/mspriority_queue.h +++ b/cds/container/mspriority_queue.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_CONTAINER_MSPRIORITY_QUEUE_H @@ -43,14 +43,18 @@ namespace cds { namespace container { namespace mspriority_queue { #ifdef CDS_DOXYGEN_INVOKED - /// Synonym for cds::intrusive::mspriority_queue::stat + /// Synonym for \p cds::intrusive::mspriority_queue::stat typedef cds::intrusive::mspriority_queue::stat<> stat; - /// Synonym for cds::intrusive::mspriority_queue::empty_stat + /// Synonym for \p cds::intrusive::mspriority_queue::empty_stat typedef cds::intrusive::mspriority_queue::empty_stat empty_stat; + + /// Synonym for \p cds::intrusive::mspriority_queue::monotonic_counter + typedef cds::intrusive::mspriority_queue::monotonic_counter monotonic_counter; #else using cds::intrusive::mspriority_queue::stat; using cds::intrusive::mspriority_queue::empty_stat; + using cds::intrusive::mspriority_queue::monotonic_counter; #endif /// MSPriorityQueue traits @@ -91,7 +95,9 @@ namespace cds { namespace container { If the compiler supports move semantics it would be better to specify the move policy based on the move semantics for type \p T. - \p opt::stat - internal statistics. Available types: \p mspriority_queue::stat, \p mspriority_queue::empty_stat (the default, no overhead) - */ + - \p opt::item_counter - an item counter type for \p MSPriorityQueue. + Available type: \p cds::bitop::bit_reverse_counter, \p mspriority_queue::monotonic_counter. See \p cds::intrusive::mspriority_queue::traits::item_counter for details. + */ template struct make_traits { # ifdef CDS_DOXYGEN_INVOKED @@ -142,11 +148,12 @@ namespace cds { namespace container { typedef Traits traits ; ///< Traits template parameter typedef typename base_class::key_comparator key_comparator; ///< priority comparing functor based on opt::compare and opt::less option setter. - typedef typename base_class::lock_type lock_type; ///< heap's size lock type - typedef typename base_class::back_off back_off ; ///< Back-off strategy - typedef typename base_class::stat stat ; ///< internal statistics type + typedef typename base_class::lock_type lock_type; ///< heap's size lock type + typedef typename base_class::back_off back_off ; ///< Back-off strategy + typedef typename traits::stat stat; ///< internal statistics type, see \p intrusive::mspriority_queue::traits::stat + typedef typename traits::item_counter item_counter;///< Item counter type, see \p intrusive::mspriority_queue::traits::item_counter typedef typename traits::allocator::template rebind::other allocator_type; ///< Value allocator - typedef typename traits::move_policy move_policy; ///< Move policy for type \p T + typedef typename traits::move_policy move_policy; ///< Move policy for type \p T protected: //@cond diff --git a/cds/details/bit_reverse_counter.h b/cds/details/bit_reverse_counter.h index 6bcadd66..90c9d0ad 100644 --- a/cds/details/bit_reverse_counter.h +++ b/cds/details/bit_reverse_counter.h @@ -71,6 +71,7 @@ namespace cds { namespace bitop { counter_type dec() { + counter_type ret = m_nReversed; --m_nCounter; int nBit; for ( nBit = m_nHighBit - 1; nBit >= 0; --nBit ) { @@ -81,7 +82,7 @@ namespace cds { namespace bitop { m_nReversed = m_nCounter; --m_nHighBit; } - return m_nReversed; + return ret; } counter_type value() const @@ -93,6 +94,11 @@ namespace cds { namespace bitop { { return m_nReversed; } + + int high_bit() const + { + return m_nHighBit; + } }; }} // namespace cds::bitop diff --git a/cds/intrusive/details/iterable_list_base.h b/cds/intrusive/details/iterable_list_base.h new file mode 100644 index 00000000..3ddaa07a --- /dev/null +++ b/cds/intrusive/details/iterable_list_base.h @@ -0,0 +1,293 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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_DETAILS_ITERABLE_LIST_BASE_H +#define CDSLIB_INTRUSIVE_DETAILS_ITERABLE_LIST_BASE_H + +#include +#include +#include +#include +#include +#include + +namespace cds { namespace intrusive { + + /// \p IterableList ordered list related definitions + /** @ingroup cds_intrusive_helper + */ + namespace iterable_list { + + /// Node type + template + struct node + { + typedef T value_type; ///< Value type + + atomics::atomic< node* > next; ///< pointer to next node in the list + atomics::atomic< value_type* > data; ///< pointer to user data, \p nullptr if the node is free + + //@cond + node() + : next( nullptr ) + , data( nullptr ) + {} + + node( value_type * pVal ) + : next( nullptr ) + , data( pVal ) + {} + //@endcond + }; + + /// \p IterableList internal statistics + 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 update the item + 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_nNodeCreated; ///< Number of created internal nodes + event_counter m_nNodeRemoved; ///< Number of removed internal nodes + + //@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 onEraseSuccess() { ++m_nEraseSuccess; } + void onEraseFailed() { ++m_nEraseFailed; } + void onEraseRetry() { ++m_nEraseRetry; } + void onFindSuccess() { ++m_nFindSuccess; } + void onFindFailed() { ++m_nFindFailed; } + + void onNodeCreated() { ++m_nNodeCreated; } + void onNodeRemoved() { ++m_nNodeRemoved; } + //@endcond + }; + + /// \p IterableList 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 onEraseSuccess() const {} + void onEraseFailed() const {} + void onEraseRetry() const {} + void onFindSuccess() const {} + void onFindFailed() const {} + + void onNodeCreated() const {} + void onNodeRemoved() 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 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 onNodeCreated() { m_stat.onNodeCreated(); } + void onNodeRemoved() { m_stat.onNodeRemoved(); } + + stat_type& m_stat; + }; + //@endcond + + + /// \p IterableList traits + 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 + /** + By default, internal statistics is disabled (\p iterable_list::empty_stat). + Use \p iterable_list::stat to enable it. + */ + typedef empty_stat stat; + + /// Item counting feature; by default, disabled. Use \p cds::atomicity::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; + + /// RCU deadlock checking policy (only for \ref cds_intrusive_IterableList_rcu "RCU-based IterableList") + /** + List of available policy see \p opt::rcu_check_deadlock + */ + typedef opt::v::rcu_throw_deadlock rcu_check_deadlock; + }; + + /// Metafunction converting option list to \p iterable_list::traits + /** + Supported \p Options are: + - \p opt::compare - key comparison functor. No default functor is provided. + If the option is not specified, the \p opt::less is used. + - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less. + - \p opt::node_allocator - node allocator, default is \p std::allocator. + - \p opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used. + - \p opt::disposer - the functor used for disposing removed items. Default is \p opt::v::empty_disposer. Due the nature + of GC schema the disposer may be called asynchronously. + - \p opt::item_counter - the type of item counting feature. Default is disabled (\p atomicity::empty_item_counter). + To enable item counting use \p atomicity::item_counter. + - \p opt::stat - internal statistics. By default, it is disabled (\p iterable_list::empty_stat). + To enable it use \p iterable_list::stat + - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) + or \p opt::v::sequential_consistent (sequentially consistent memory model). + - \p opt::rcu_check_deadlock - a deadlock checking policy for \ref cds_intrusive_IterableList_rcu "RCU-based IterableList" + Default is \p opt::v::rcu_throw_deadlock + */ + 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 + }; + + + //@cond + template + struct select_stat_wrapper + { + typedef Stat stat; + typedef iterable_list::wrapped_stat wrapped_stat; + enum { + empty = false + }; + }; + + template <> + struct select_stat_wrapper< empty_stat > + { + typedef empty_stat stat; + typedef empty_stat wrapped_stat; + enum { + empty = true + }; + }; + + template + struct select_stat_wrapper< iterable_list::wrapped_stat>: public select_stat_wrapper + {}; + //@endcond + + } // namespace iterable_list + + //@cond + // Forward declaration + template < class GC, typename T, class Traits = iterable_list::traits > + class IterableList; + //@endcond + + //@cond + template + struct is_iterable_list { + enum { + value = false + }; + }; + + template + struct is_iterable_list< IterableList< GC, T, Traits >> { + enum { + value = true + }; + }; + //@endcond + +}} // namespace cds::intrusive + +#endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_ITERABLE_LIST_BASE_H diff --git a/cds/intrusive/details/lazy_list_base.h b/cds/intrusive/details/lazy_list_base.h index 2a8e79b8..441a106b 100644 --- a/cds/intrusive/details/lazy_list_base.h +++ b/cds/intrusive/details/lazy_list_base.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_DETAILS_LAZY_LIST_BASE_H @@ -215,6 +215,101 @@ namespace cds { namespace intrusive { //@endcond }; + /// \p LazyList internal statistics + 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_nValidationSuccess; ///< Number of successful validating of search result + event_counter m_nValidationFailed; ///< Number of failed validating of search result + + //@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 onValidationSuccess() { ++m_nValidationSuccess; } + void onValidationFailed() { ++m_nValidationFailed; } + //@endcond + }; + + /// \p LazyList 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 onValidationSuccess() const {} + void onValidationFailed() 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 onValidationSuccess() { m_stat.onValidationSuccess(); } + void onValidationFailed() { m_stat.onValidationFailed(); } + + stat_type& m_stat; + }; + //@endcond + + /// LazyList traits struct traits { @@ -260,6 +355,13 @@ namespace cds { namespace intrusive { /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting typedef atomicity::empty_item_counter item_counter; + /// Internal statistics + /** + By default, internal statistics is disabled (\p lazy_list::empty_stat). + Use \p lazy_list::stat to enable it. + */ + typedef empty_stat stat; + /// Link fields checking feature /** Default is \p opt::debug_check_link @@ -298,6 +400,8 @@ namespace cds { namespace intrusive { - \p opt::link_checker - the type of node's link fields checking. Default is \p opt::debug_check_link - \p opt::item_counter - the type of item counting feature. Default is disabled (\p atomicity::empty_item_counter). To enable item counting use \p atomicity::item_counter. + - \p opt::stat - internal statistics. By default, it is disabled (\p lazy_list::empty_stat). + To enable it use \p lazy_list::stat - \p opt::memory_model - 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). - \p opt::rcu_check_deadlock - a deadlock checking policy for \ref cds_intrusive_MichaelList_rcu "RCU-based MichaelList" @@ -315,6 +419,32 @@ namespace cds { namespace intrusive { # endif }; + //@cond + template + struct select_stat_wrapper + { + typedef Stat stat; + typedef lazy_list::wrapped_stat wrapped_stat; + enum { + empty = false + }; + }; + + template <> + struct select_stat_wrapper< empty_stat > + { + typedef empty_stat stat; + typedef empty_stat wrapped_stat; + enum { + empty = true + }; + }; + + template + struct select_stat_wrapper< lazy_list::wrapped_stat>: public select_stat_wrapper< Stat > + {}; + //@endcond + } // namespace lazy_list //@cond @@ -323,6 +453,22 @@ namespace cds { namespace intrusive { class LazyList; //@endcond + //@cond + template + struct is_lazy_list { + enum { + value = false + }; + }; + + template + struct is_lazy_list< LazyList< GC, T, Traits >> { + enum { + value = true + }; + }; + //@endcond + }} // namespace cds::intrusive #endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_LAZY_LIST_BASE_H diff --git a/cds/intrusive/details/michael_list_base.h b/cds/intrusive/details/michael_list_base.h index 2b72b262..a51275a5 100644 --- a/cds/intrusive/details/michael_list_base.h +++ b/cds/intrusive/details/michael_list_base.h @@ -196,6 +196,101 @@ namespace cds { namespace intrusive { //@endcond }; + + /// \p MichaelList internal statistics + 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; + }; + //@endcond + /// MichaelList traits struct traits { @@ -224,7 +319,14 @@ namespace cds { namespace intrusive { typedef opt::v::empty_disposer disposer; /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting - typedef atomicity::empty_item_counter item_counter; + typedef atomicity::empty_item_counter item_counter; + + /// Internal statistics + /** + By default, internal statistics is disabled (\p michael_list::empty_stat). + Use \p michael_list::stat to enable it. + */ + typedef empty_stat stat; /// Link fields checking feature /** @@ -260,6 +362,8 @@ namespace cds { namespace intrusive { - \p opt::link_checker - the type of node's link fields checking. Default is \p opt::debug_check_link - \p opt::item_counter - the type of item counting feature. Default is disabled (\p atomicity::empty_item_counter). To enable item counting use \p atomicity::item_counter. + - \p opt::stat - internal statistics. By default, it is disabled (\p michael_list::empty_stat). + To enable it use \p michael_list::stat - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) or \p opt::v::sequential_consistent (sequentially consistent memory model). - \p opt::rcu_check_deadlock - a deadlock checking policy for \ref cds_intrusive_MichaelList_rcu "RCU-based MichaelList" @@ -277,6 +381,34 @@ namespace cds { namespace intrusive { # endif }; + + //@cond + template + struct select_stat_wrapper + { + typedef Stat stat; + typedef michael_list::wrapped_stat wrapped_stat; + enum { + empty = false + }; + }; + + template <> + struct select_stat_wrapper< empty_stat > + { + typedef empty_stat stat; + typedef empty_stat wrapped_stat; + enum { + empty = true + }; + }; + + template + struct select_stat_wrapper< michael_list::wrapped_stat>: public select_stat_wrapper< Stat > + {}; + + //@endcond + } // namespace michael_list //@cond @@ -286,8 +418,21 @@ namespace cds { namespace intrusive { //@endcond - /// Tag for selecting Michael list - //class michael_list_tag; + //@cond + template + struct is_michael_list { + enum { + value = false + }; + }; + + template + struct is_michael_list< MichaelList< GC, T, Traits >> { + enum { + value = true + }; + }; + //@endcond }} // namespace cds::intrusive diff --git a/cds/intrusive/details/michael_set_base.h b/cds/intrusive/details/michael_set_base.h index 2e7eef9b..bc8490e7 100644 --- a/cds/intrusive/details/michael_set_base.h +++ b/cds/intrusive/details/michael_set_base.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_DETAILS_MICHAEL_SET_BASE_H @@ -120,7 +120,8 @@ namespace cds { namespace intrusive { template class iterator { - friend class iterator < OrderedList, !IsConst >; + friend class iterator< OrderedList, !IsConst >; + protected: typedef OrderedList bucket_type; typedef typename list_iterator_selector< bucket_type, IsConst>::bucket_ptr bucket_ptr; diff --git a/cds/intrusive/impl/iterable_list.h b/cds/intrusive/impl/iterable_list.h new file mode 100644 index 00000000..723b6b25 --- /dev/null +++ b/cds/intrusive/impl/iterable_list.h @@ -0,0 +1,1211 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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_IMPL_ITERABLE_LIST_H +#define CDSLIB_INTRUSIVE_IMPL_ITERABLE_LIST_H + +#include +#include + +namespace cds { namespace intrusive { + + /// Iterable lock-free ordered single-linked list + /** @ingroup cds_intrusive_list + \anchor cds_intrusive_IterableList_hp + + This lock-free list implementation supports thread-safe iterators. + Unlike \p cds::intrusive::MichaelList the iterable list does not require + any hook in \p T to be stored in the list. + + Usually, ordered single-linked list is used as a building block for the hash table implementation. + Iterable list is suitable for almost append-only hash table because the list doesn't delete + its internal node when erasing a key but it is marked them as empty to be reused in the future. + However, plenty of empty nodes degrades performance. + Separation of internal nodes and user data implies the need for an allocator for internal node + so the iterable list is not fully intrusive. Nevertheless, if you need thread-safe iterator, + the iterable list is good choice. + + The complexity of searching is O(N). + + Template arguments: + - \p GC - Garbage collector used. + - \p T - type to be stored in the list. + - \p Traits - type traits, default is \p iterable_list::traits. It is possible to declare option-based + list with \p cds::intrusive::iterable_list::make_traits metafunction: + For example, the following traits-based declaration of \p gc::HP iterable list + \code + #include + // Declare item stored in your list + struct foo + { + int nKey; + // .... other data + }; + + // Declare comparator for the item + struct my_compare { + int operator()( foo const& i1, foo const& i2 ) const + { + return i1.nKey - i2.nKey; + } + }; + + // Declare traits + struct my_traits: public cds::intrusive::iterable_list::traits + { + typedef my_compare compare; + }; + + // Declare list + typedef cds::intrusive::IterableList< cds::gc::HP, foo, my_traits > list_type; + \endcode + is equivalent for the following option-based list + \code + #include + + // foo struct and my_compare are the same + + // Declare option-based list + typedef cds::intrusive::IterableList< cds::gc::HP, foo, + typename cds::intrusive::iterable_list::make_traits< + cds::intrusive::opt::compare< my_compare > // item comparator option + >::type + > option_list_type; + \endcode + + \par Usage + There are different specializations of this template for each garbage collecting schema. + You should select GC you want and include appropriate .h-file: + - for \p gc::HP: + - for \p gc::DHP: + - for \ref cds_urcu_gc "RCU type" - see \ref cds_intrusive_IterableList_rcu "RCU-based IterableList" + */ + template < + class GC + ,typename T +#ifdef CDS_DOXYGEN_INVOKED + ,class Traits = iterable_list::traits +#else + ,class Traits +#endif + > + class IterableList + { + public: + typedef T value_type; ///< type of value stored in the list + typedef Traits traits; ///< Traits template parameter + + typedef iterable_list::node< value_type > node_type; ///< node type + +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined key_comparator ; ///< key comparison functor based on opt::compare and opt::less option setter. +# else + typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator; +# endif + + typedef typename traits::disposer disposer; ///< disposer for \p value_type + + typedef GC gc; ///< Garbage collector + typedef typename traits::back_off back_off; ///< back-off strategy + typedef typename traits::item_counter item_counter; ///< Item counting policy used + 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 + + typedef typename gc::template guarded_ptr< value_type > guarded_ptr; ///< Guarded pointer + + static CDS_CONSTEXPR const size_t c_nHazardPtrCount = 2; ///< Count of hazard pointer required for the algorithm + + //@cond + // Rebind traits (split-list support) + template + struct rebind_traits { + typedef IterableList< + gc + , value_type + , typename cds::opt::make_options< traits, Options...>::type + > type; + }; + + // Stat selector + template + using select_stat_wrapper = iterable_list::select_stat_wrapper< Stat >; + //@endcond + + protected: + typedef atomics::atomic< node_type* > atomic_node_ptr; ///< Atomic node pointer + typedef atomic_node_ptr auxiliary_head; ///< Auxiliary head type (for split-list support) + + atomic_node_ptr m_pHead; ///< Head pointer + item_counter m_ItemCounter; ///< Item counter + mutable stat m_Stat; ///< Internal statistics + + //@cond + typedef cds::details::Allocator< node_type, node_allocator > cxx_node_allocator; + + /// Position pointer for item search + struct position { + atomic_node_ptr * pHead; ///< Previous node (pointer to pPrev->next or to m_pHead) + node_type * pPrev; ///< Previous node + node_type * pCur; ///< Current node + + value_type * pFound; ///< Value of \p pCur->data, valid only if data found + typename gc::Guard guard; ///< guard for \p pFound + }; + //@endcond + + protected: + //@cond + template + class iterator_type + { + friend class IterableList; + + protected: + node_type* m_pNode; + value_type* m_pVal; + typename gc::Guard m_Guard; // for m_pVal + + void next() + { + while ( m_pNode ) { + m_pNode = m_pNode->next.load( memory_model::memory_order_relaxed ); + if ( !m_pNode ) + break; + m_pVal = m_Guard.protect( m_pNode->data ); + if ( m_pVal ) + break; + } + } + + explicit iterator_type( atomic_node_ptr const& pNode ) + : m_pNode( pNode.load( memory_model::memory_order_relaxed )) + , m_pVal( nullptr ) + { + if ( m_pNode ) { + m_pVal = m_Guard.protect( m_pNode->data ); + if ( !m_pVal ) + next(); + } + } + + iterator_type( node_type* pNode, value_type* pVal ) + : m_pNode( pNode ) + , m_pVal( pVal ) + { + if ( m_pNode ) { + assert( pVal != nullptr ); + m_Guard.assign( pVal ); + } + } + + public: + typedef typename cds::details::make_const_type::pointer value_ptr; + typedef typename cds::details::make_const_type::reference value_ref; + + iterator_type() + : m_pNode( nullptr ) + , m_pVal( nullptr ) + {} + + iterator_type( iterator_type const& src ) + : m_pNode( src.m_pNode ) + , m_pVal( src.m_pVal ) + { + m_Guard.assign( m_pVal ); + } + + value_ptr operator ->() const + { + return m_pVal; + } + + value_ref operator *() const + { + assert( m_pVal != nullptr ); + return *m_pVal; + } + + /// Pre-increment + iterator_type& operator ++() + { + next(); + return *this; + } + + iterator_type& operator = (iterator_type const& src) + { + m_pNode = src.m_pNode; + m_pVal = src.m_pVal; + m_Guard.assign( m_pVal ); + return *this; + } + + template + bool operator ==(iterator_type const& i ) const + { + return m_pNode == i.m_pNode; + } + template + bool operator !=(iterator_type const& i ) const + { + return m_pNode != i.m_pNode; + } + }; + //@endcond + + public: + ///@name Thread-safe forward iterators + //@{ + /// Forward iterator + /** + The forward iterator for iterable list has some features: + - it has no post-increment operator + - to protect the value, the iterator contains a GC-specific guard. + For some GC (like as \p gc::HP), a guard is a limited resource per thread, so an exception (or assertion) "no free guard" + may be thrown if the limit of guard count per thread is exceeded. + - The iterator cannot be moved across thread boundary since it contains thread-private GC's guard. + - Iterator is thread-safe: event if the element the iterator points to is removed, the iterator stays valid because + it contains the guard keeping the value from to be recycled. + + The iterator interface: + \code + class iterator { + public: + // Default constructor + iterator(); + + // Copy construtor + iterator( iterator const& src ); + + // Dereference operator + value_type * operator ->() const; + + // Dereference operator + value_type& operator *() const; + + // Preincrement operator + iterator& operator ++(); + + // Assignment operator + iterator& operator = (iterator const& src); + + // Equality operators + bool operator ==(iterator const& i ) const; + bool operator !=(iterator const& i ) const; + }; + \endcode + + @note For two iterators pointed to the same element the value can be different; + this code + \code + if ( it1 == it2 ) + assert( &(*it1) == &(*it2) ); + \endcode + can throw assertion. The point is that the iterator stores the value of element which can be modified later by other thread. + The guard inside the iterator prevents recycling that value so the iterator's value remains valid even after such changing. + Other iterator can observe modified value of the element. + */ + typedef iterator_type iterator; + /// Const forward iterator + /** + For iterator's features and requirements see \ref iterator + */ + typedef iterator_type const_iterator; + + /// Returns a forward iterator addressing the first element in a list + /** + For empty list \code begin() == end() \endcode + */ + iterator begin() + { + return iterator( m_pHead ); + } + + /// Returns an iterator that addresses the location succeeding the last element in a list + /** + Do not use the value returned by end function to access any item. + Internally, end returning value equals to \p nullptr. + + The returned value can be used only to control reaching the end of the list. + For empty list begin() == end() + */ + iterator end() + { + return iterator(); + } + + /// Returns a forward const iterator addressing the first element in a list + const_iterator cbegin() const + { + return const_iterator( m_pHead ); + } + + /// Returns a forward const iterator addressing the first element in a list + const_iterator begin() const + { + return const_iterator( m_pHead ); + } + + /// Returns an const iterator that addresses the location succeeding the last element in a list + const_iterator end() const + { + return const_iterator(); + } + + /// Returns an const iterator that addresses the location succeeding the last element in a list + const_iterator cend() const + { + return const_iterator(); + } + //@} + + public: + /// Default constructor initializes empty list + IterableList() + : m_pHead( nullptr ) + {} + + //@cond + template >::value >> + explicit IterableList( Stat& st ) + : m_pHead( nullptr ) + , m_Stat( st ) + {} + //@endcond + + /// Destroys the list object + ~IterableList() + { + destroy(); + } + + /// Inserts new node + /** + The function inserts \p val into the list if the list does not contain + an item with key equal to \p val. + + Returns \p true if \p val has been linked to the list, \p false otherwise. + */ + bool insert( value_type& val ) + { + return insert_at( m_pHead, val ); + } + + /// Inserts new node + /** + This function is intended for derived non-intrusive containers. + + The function allows to split new item creating into two part: + - create item with key only + - insert new item into the list + - if inserting is success, calls \p f functor to initialize value-field of \p val. + + The functor signature is: + \code + void func( value_type& val ); + \endcode + where \p val is the item inserted. User-defined functor \p f should guarantee that during changing + \p val no any other changes could be made on this list's item by concurrent threads. + The user-defined functor is called only if the inserting is success. + + @warning See \ref cds_intrusive_item_creating "insert item troubleshooting" + */ + template + bool insert( value_type& val, Func f ) + { + return insert_at( m_pHead, val, f ); + } + + /// Updates the node + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the list, then \p val is inserted + iff \p bInsert is \p true. + Otherwise, the current element is changed to \p val, the element will be retired later + by call \p Traits::disposer. + The functor \p func is called after inserting or replacing, it signature is: + \code + void func( value_type& val, value_type * old ); + \endcode + where + - \p val - argument \p val passed into the \p %update() function + - \p old - old value that will be retired. If new item has been inserted then \p old is \p nullptr. + + Returns std::pair where \p first is \p true if operation is successful, + \p second is \p true if \p val has been added or \p false if the item with that key + already in the list. + */ + template + std::pair update( value_type& val, Func func, bool bInsert = true ) + { + return update_at( m_pHead, val, func, bInsert ); + } + + /// Insert or update + /** + The operation performs inserting or updating data with lock-free manner. + + If the item \p val is not found in the list, then \p val is inserted + iff \p bInsert is \p true. + Otherwise, the current element is changed to \p val, the old element will be retired later + by call \p Traits::disposer. + + Returns std::pair where \p first is \p true if operation is successful, + \p second is \p true if \p val has been added or \p false if the item with that key + already in the list. + */ + std::pair upsert( value_type& val, bool bInsert = true ) + { + return update_at( m_pHead, val, []( value_type&, value_type* ) {}, bInsert ); + } + + /// Unlinks the item \p val from the list + /** + The function searches the item \p val in the list and unlinks it from the list + if it is found and it is equal to \p val. + + Difference between \p erase() and \p %unlink(): \p %erase() finds a key + and deletes the item found. \p %unlink() finds an item by key and deletes it + only if \p val is an item of the list, i.e. the pointer to item found + is equal to &val . + + \p disposer specified in \p Traits is called for deleted item. + + The function returns \p true if success and \p false otherwise. + */ + bool unlink( value_type& val ) + { + return unlink_at( m_pHead, val ); + } + + /// Deletes the item from the list + /** \anchor cds_intrusive_IterableList_hp_erase_val + The function searches an item with key equal to \p key in the list, + unlinks it from the list, and returns \p true. + If \p key is not found the function return \p false. + + \p disposer specified in \p Traits is called for deleted item. + */ + template + bool erase( Q const& key ) + { + return erase_at( m_pHead, key, key_comparator()); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_intrusive_IterableList_hp_erase_val "erase(Q const&)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + + \p disposer specified in \p Traits is called for deleted item. + */ + template + bool erase_with( Q const& key, Less pred ) + { + CDS_UNUSED( pred ); + return erase_at( m_pHead, key, cds::opt::details::make_comparator_from_less()); + } + + /// Deletes the item from the list + /** \anchor cds_intrusive_IterableList_hp_erase_func + The function searches an item with key equal to \p key in the list, + call \p func functor with item found, unlinks it from the list, and returns \p true. + The \p Func interface is + \code + struct functor { + void operator()( value_type const& item ); + }; + \endcode + If \p key is not found the function return \p false, \p func is not called. + + \p disposer specified in \p Traits is called for deleted item. + */ + template + bool erase( Q const& key, Func func ) + { + return erase_at( m_pHead, key, key_comparator(), func ); + } + + /// Deletes the item from the list using \p pred predicate for searching + /** + The function is an analog of \ref cds_intrusive_IterableList_hp_erase_func "erase(Q const&, Func)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + + \p disposer specified in \p Traits is called for deleted item. + */ + template + bool erase_with( Q const& key, Less pred, Func f ) + { + CDS_UNUSED( pred ); + return erase_at( m_pHead, key, cds::opt::details::make_comparator_from_less(), f ); + } + + /// Extracts the item from the list with specified \p key + /** \anchor cds_intrusive_IterableList_hp_extract + The function searches an item with key equal to \p key, + unlinks it from the list, and returns it as \p guarded_ptr. + If \p key is not found returns an empty guarded pointer. + + Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type. + + The \ref disposer specified in \p Traits class template parameter is called automatically + by garbage collector \p GC when returned \ref guarded_ptr object will be destroyed or released. + @note Each \p guarded_ptr object uses the GC's guard that can be limited resource. + + Usage: + \code + typedef cds::intrusive::IterableList< cds::gc::HP, foo, my_traits > ord_list; + ord_list theList; + // ... + { + ord_list::guarded_ptr gp(theList.extract( 5 )); + if ( gp ) { + // Deal with gp + // ... + } + // Destructor of gp releases internal HP guard + } + \endcode + */ + template + guarded_ptr extract( Q const& key ) + { + guarded_ptr gp; + extract_at( m_pHead, gp.guard(), key, key_comparator()); + return gp; + } + + /// Extracts the item using compare functor \p pred + /** + The function is an analog of \ref cds_intrusive_IterableList_hp_extract "extract(Q const&)" + but \p pred predicate is used for key comparing. + + \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q + in any order. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + guarded_ptr extract_with( Q const& key, Less pred ) + { + CDS_UNUSED( pred ); + guarded_ptr gp; + extract_at( m_pHead, gp.guard(), key, cds::opt::details::make_comparator_from_less()); + return gp; + } + + /// Finds \p key in the list + /** \anchor cds_intrusive_IterableList_hp_find_func + The function searches the item with key equal to \p key and calls the functor \p f for item found. + The interface of \p Func functor is: + \code + struct functor { + void operator()( value_type& item, Q& key ); + }; + \endcode + where \p item is the item found, \p key is the \p %find() function argument. + + The functor may change non-key fields of \p item. Note that the function is only guarantee + that \p item cannot be disposed during functor is executing. + The function does not serialize simultaneous access to the \p item. If such access is + possible you must provide your own synchronization schema to keep out unsafe item modifications. + + The function returns \p true if \p val is found, \p false otherwise. + */ + template + bool find( Q& key, Func f ) const + { + return find_at( m_pHead, key, key_comparator(), f ); + } + //@cond + template + bool find( Q const& key, Func f ) const + { + return find_at( m_pHead, key, key_comparator(), f ); + } + //@endcond + + /// Finds \p key in the list and returns iterator pointed to the item found + /** + If \p key is not found the function returns \p end(). + */ + template + iterator find( Q& key ) const + { + return find_iterator_at( m_pHead, key, key_comparator()); + } + //@cond + template + iterator find( Q const& key ) const + { + return find_iterator_at( m_pHead, key, key_comparator() ); + } + //@endcond + + /// Finds the \p key using \p pred predicate for searching + /** + The function is an analog of \ref cds_intrusive_IterableList_hp_find_func "find(Q&, Func)" + but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + bool find_with( Q& key, Less pred, Func f ) const + { + CDS_UNUSED( pred ); + return find_at( m_pHead, key, cds::opt::details::make_comparator_from_less(), f ); + } + //@cond + template + bool find_with( Q const& key, Less pred, Func f ) const + { + CDS_UNUSED( pred ); + return find_at( m_pHead, key, cds::opt::details::make_comparator_from_less(), f ); + } + //@endcond + + /// Finds \p key in the list using \p pred predicate for searching and returns iterator pointed to the item found + /** + The function is an analog of \p find(Q&) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the list. + + If \p key is not found the function returns \p end(). + */ + template + iterator find_with( Q& key, Less pred ) const + { + CDS_UNUSED( pred ); + return find_iterator_at( m_pHead, key, cds::opt::details::make_comparator_from_less()); + } + //@cond + template + iterator find_with( Q const& key, Less pred ) const + { + CDS_UNUSED( pred ); + return find_iterator_at( m_pHead, key, cds::opt::details::make_comparator_from_less()); + } + //@endcond + + /// Checks whether the list contains \p key + /** + The function searches the item with key equal to \p key + and returns \p true if it is found, and \p false otherwise. + */ + template + bool contains( Q const& key ) const + { + return find_at( m_pHead, key, key_comparator()); + } + + /// Checks whether the list contains \p key using \p pred predicate for searching + /** + The function is an analog of contains( key ) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p Less must imply the same element order as the comparator used for building the list. + */ + template + bool contains( Q const& key, Less pred ) const + { + CDS_UNUSED( pred ); + return find_at( m_pHead, key, cds::opt::details::make_comparator_from_less()); + } + + /// Finds the \p key and return the item found + /** \anchor cds_intrusive_IterableList_hp_get + The function searches the item with key equal to \p key + and returns it as \p guarded_ptr. + If \p key is not found the function returns an empty guarded pointer. + + The \ref disposer specified in \p Traits class template parameter is called + by garbage collector \p GC automatically when returned \ref guarded_ptr object + will be destroyed or released. + @note Each \p guarded_ptr object uses one GC's guard which can be limited resource. + + Usage: + \code + typedef cds::intrusive::IterableList< cds::gc::HP, foo, my_traits > ord_list; + ord_list theList; + // ... + { + ord_list::guarded_ptr gp(theList.get( 5 )); + if ( gp ) { + // Deal with gp + //... + } + // Destructor of guarded_ptr releases internal HP guard + } + \endcode + + Note the compare functor specified for \p Traits template parameter + should accept a parameter of type \p Q that can be not the same as \p value_type. + */ + template + guarded_ptr get( Q const& key ) const + { + guarded_ptr gp; + get_at( m_pHead, gp.guard(), key, key_comparator()); + return gp; + } + + /// Finds the \p key and return the item found + /** + The function is an analog of \ref cds_intrusive_IterableList_hp_get "get( Q const&)" + but \p pred is used for comparing the keys. + + \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q + in any order. + \p pred must imply the same element order as the comparator used for building the list. + */ + template + guarded_ptr get_with( Q const& key, Less pred ) const + { + CDS_UNUSED( pred ); + guarded_ptr gp; + get_at( m_pHead, gp.guard(), key, cds::opt::details::make_comparator_from_less()); + return gp; + } + + /// Clears the list (thread safe, not atomic) + void clear() + { + position pos; + for ( pos.pCur = m_pHead.load( memory_model::memory_order_relaxed ); pos.pCur; pos.pCur = pos.pCur->next.load( memory_model::memory_order_relaxed )) { + while ( true ) { + pos.pFound = pos.guard.protect( pos.pCur->data ); + if ( !pos.pFound ) + break; + if ( cds_likely( unlink_node( pos ))) { + --m_ItemCounter; + break; + } + } + } + } + + /// Checks if the list is empty + /** + Emptiness is checked by item counting: if item count is zero then the set is empty. + Thus, if you need to use \p %empty() you should provide appropriate (non-empty) \p iterable_list::traits::item_counter + feature. + */ + bool empty() const + { + return size() == 0; + } + + /// Returns list's item count + /** + The value returned depends on item counter provided by \p iterable_list::traits::item_counter. For \p atomicity::empty_item_counter, + this function always returns 0. + */ + size_t size() const + { + return m_ItemCounter.value(); + } + + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + + protected: + //@cond +#if 0 + // split-list support + bool insert_aux_node( node_type * pNode ) + { + return insert_aux_node( m_pHead, pNode ); + } + + // split-list support + bool insert_aux_node( atomic_node_ptr& refHead, node_type * pNode ) + { + assert( pNode != nullptr ); + + // Hack: convert node_type to value_type. + // In principle, auxiliary node can be non-reducible to value_type + // We assume that comparator can correctly distinguish aux and regular node. + return insert_at( refHead, *node_traits::to_value_ptr( pNode ) ); + } +#endif + + bool insert_at( atomic_node_ptr& refHead, value_type& val ) + { + position pos; + + while ( true ) { + if ( search( refHead, val, pos, key_comparator() )) { + m_Stat.onInsertFailed(); + return false; + } + + if ( link_node( &val, pos ) ) { + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; + } + + m_Stat.onInsertRetry(); + } + } + + template + bool insert_at( atomic_node_ptr& refHead, value_type& val, Func f ) + { + position pos; + + typename gc::Guard guard; + guard.assign( &val ); + + while ( true ) { + if ( search( refHead, val, pos, key_comparator() ) ) { + m_Stat.onInsertFailed(); + return false; + } + + if ( link_node( &val, pos ) ) { + f( val ); + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; + } + + m_Stat.onInsertRetry(); + } + } + + template + std::pair update_at( atomic_node_ptr& refHead, value_type& val, Func func, bool bInsert ) + { + position pos; + + typename gc::Guard guard; + guard.assign( &val ); + + while ( true ) { + if ( search( refHead, val, pos, key_comparator() ) ) { + // try to replace pCur->data with val + assert( pos.pFound != nullptr ); + assert( key_comparator()(*pos.pFound, val) == 0 ); + + if ( cds_likely( pos.pCur->data.compare_exchange_strong( pos.pFound, &val, memory_model::memory_order_release, atomics::memory_order_relaxed ))) { + if ( pos.pFound != &val ) { + retire_data( pos.pFound ); + func( val, pos.pFound ); + } + m_Stat.onUpdateExisting(); + return std::make_pair( true, false ); + } + } + else { + if ( !bInsert ) { + m_Stat.onUpdateFailed(); + return std::make_pair( false, false ); + } + + if ( link_node( &val, pos )) { + func( val, static_cast( nullptr )); + ++m_ItemCounter; + m_Stat.onUpdateNew(); + return std::make_pair( true, true ); + } + } + + m_Stat.onUpdateRetry(); + } + } + + bool unlink_at( atomic_node_ptr& refHead, value_type& val ) + { + position pos; + + back_off bkoff; + while ( search( refHead, val, pos, key_comparator())) { + if ( pos.pFound == &val ) { + if ( unlink_node( pos )) { + --m_ItemCounter; + m_Stat.onEraseSuccess(); + return true; + } + else + bkoff(); + } + else + break; + + m_Stat.onEraseRetry(); + } + + m_Stat.onEraseFailed(); + return false; + } + + template + bool erase_at( atomic_node_ptr& refHead, const Q& val, Compare cmp, Func f, position& pos ) + { + back_off bkoff; + while ( search( refHead, val, pos, cmp )) { + if ( unlink_node( pos )) { + f( *pos.pFound ); + --m_ItemCounter; + m_Stat.onEraseSuccess(); + return true; + } + else + bkoff(); + + m_Stat.onEraseRetry(); + } + + m_Stat.onEraseFailed(); + return false; + } + + template + bool erase_at( atomic_node_ptr& refHead, const Q& val, Compare cmp, Func f ) + { + position pos; + return erase_at( refHead, val, cmp, f, pos ); + } + + template + bool erase_at( atomic_node_ptr& refHead, Q const& val, Compare cmp ) + { + position pos; + return erase_at( refHead, val, cmp, [](value_type const&){}, pos ); + } + + template + bool extract_at( atomic_node_ptr& refHead, typename guarded_ptr::native_guard& dest, Q const& val, Compare cmp ) + { + position pos; + back_off bkoff; + while ( search( refHead, val, pos, cmp )) { + if ( unlink_node( pos )) { + dest.set( pos.pFound ); + --m_ItemCounter; + m_Stat.onEraseSuccess(); + return true; + } + else + bkoff(); + + m_Stat.onEraseRetry(); + } + + m_Stat.onEraseFailed(); + return false; + } + + template + bool find_at( atomic_node_ptr const& refHead, Q const& val, Compare cmp ) const + { + position pos; + if ( search( refHead, val, pos, cmp ) ) { + m_Stat.onFindSuccess(); + return true; + } + + m_Stat.onFindFailed(); + return false; + } + + template + bool find_at( atomic_node_ptr const& refHead, Q& val, Compare cmp, Func f ) const + { + position pos; + if ( search( refHead, val, pos, cmp )) { + assert( pos.pFound != nullptr ); + f( *pos.pFound, val ); + m_Stat.onFindSuccess(); + return true; + } + + m_Stat.onFindFailed(); + return false; + } + + template + iterator find_iterator_at( atomic_node_ptr const& refHead, Q const& val, Compare cmp ) const + { + position pos; + if ( search( refHead, val, pos, cmp )) { + assert( pos.pCur != nullptr ); + assert( pos.pFound != nullptr ); + m_Stat.onFindSuccess(); + return iterator( pos.pCur, pos.pFound ); + } + + m_Stat.onFindFailed(); + return iterator{}; + } + + template + bool get_at( atomic_node_ptr const& refHead, typename guarded_ptr::native_guard& guard, Q const& val, Compare cmp ) const + { + position pos; + if ( search( refHead, val, pos, cmp )) { + guard.set( pos.pFound ); + m_Stat.onFindSuccess(); + return true; + } + + m_Stat.onFindFailed(); + return false; + } + //@endcond + + protected: + + //@cond + template + bool search( atomic_node_ptr const& refHead, const Q& val, position& pos, Compare cmp ) const + { + atomic_node_ptr* pHead = const_cast( &refHead ); + node_type * pPrev = nullptr; + + while ( true ) { + node_type * pCur = pHead->load( memory_model::memory_order_relaxed ); + + if ( pCur == nullptr ) { + // end-of-list + pos.pHead = pHead; + pos.pPrev = pPrev; + pos.pCur = nullptr; + pos.pFound = nullptr; + return false; + } + + value_type * pVal = pos.guard.protect( pCur->data ); + + if ( pVal ) { + int nCmp = cmp( *pVal, val ); + if ( nCmp >= 0 ) { + pos.pHead = pHead; + pos.pPrev = pPrev; + pos.pCur = pCur; + pos.pFound = pVal; + return nCmp == 0; + } + } + + pPrev = pCur; + pHead = &( pCur->next ); + } + } + //@endcond + + private: + //@cond + node_type * alloc_node( value_type * pVal ) + { + m_Stat.onNodeCreated(); + return cxx_node_allocator().New( pVal ); + } + + void delete_node( node_type * pNode ) + { + m_Stat.onNodeRemoved(); + cxx_node_allocator().Delete( pNode ); + } + + static void retire_data( value_type * pVal ) + { + assert( pVal != nullptr ); + gc::template retire( pVal ); + } + + void destroy() + { + node_type * pNode = m_pHead.load( memory_model::memory_order_relaxed ); + while ( pNode ) { + value_type * pVal = pNode->data.load( memory_model::memory_order_relaxed ); + if ( pVal ) + retire_data( pVal ); + node_type * pNext = pNode->next.load( memory_model::memory_order_relaxed ); + delete_node( pNode ); + pNode = pNext; + } + } + + bool link_node( value_type * pVal, position& pos ) + { + if ( pos.pPrev ) { + if ( pos.pPrev->data.load( memory_model::memory_order_relaxed ) == nullptr ) { + // reuse pPrev + value_type * p = nullptr; + return pos.pPrev->data.compare_exchange_strong( p, pVal, memory_model::memory_order_release, atomics::memory_order_relaxed ); + } + else { + // insert new node between pos.pPrev and pos.pCur + node_type * pNode = alloc_node( pVal ); + pNode->next.store( pos.pCur, memory_model::memory_order_relaxed ); + + if ( cds_likely( pos.pPrev->next.compare_exchange_strong( pos.pCur, pNode, memory_model::memory_order_release, atomics::memory_order_relaxed ))) + return true; + + delete_node( pNode ); + } + } + else { + node_type * pNode = alloc_node( pVal ); + pNode->next.store( pos.pCur, memory_model::memory_order_relaxed ); + if ( cds_likely( pos.pHead->compare_exchange_strong( pos.pCur, pNode, memory_model::memory_order_release, atomics::memory_order_relaxed ) ) ) + return true; + + delete_node( pNode ); + } + return false; + } + + static bool unlink_node( position& pos ) + { + assert( pos.pCur != nullptr ); + assert( pos.pFound != nullptr ); + + if ( pos.pCur->data.compare_exchange_strong( pos.pFound, nullptr, memory_model::memory_order_acquire, atomics::memory_order_relaxed ) ) { + retire_data( pos.pFound ); + return true; + } + return false; + } + + //@endcond + }; +}} // namespace cds::intrusive + +#endif // #ifndef CDSLIB_INTRUSIVE_IMPL_ITERABLE_LIST_H diff --git a/cds/intrusive/impl/lazy_list.h b/cds/intrusive/impl/lazy_list.h index 7033bde0..dc0b54fd 100644 --- a/cds/intrusive/impl/lazy_list.h +++ b/cds/intrusive/impl/lazy_list.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_IMPL_LAZY_LIST_H @@ -57,7 +57,7 @@ namespace cds { namespace intrusive { - \p T - type to be stored in the list. The type must be based on lazy_list::node (for lazy_list::base_hook) or it must have a member of type lazy_list::node (for lazy_list::member_hook). - \p Traits - type traits. See lazy_list::traits for explanation. - It is possible to declare option-based list with cds::intrusive::lazy_list::make_traits metafunction istead of \p Traits template + It is possible to declare option-based list with cds::intrusive::lazy_list::make_traits metafunction instead of \p Traits template argument. For example, the following traits-based declaration of \p gc::HP lazy list \code #include @@ -201,9 +201,12 @@ namespace cds { namespace intrusive { typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker - typedef typename traits::back_off back_off; ///< back-off strategy + typedef typename traits::back_off back_off; ///< back-off strategy typedef typename traits::item_counter item_counter; ///< Item counting policy used - typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model) + typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model) + typedef typename traits::stat stat; ///< Internal statistics + + static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type"); typedef typename gc::template guarded_ptr< value_type > guarded_ptr; ///< Guarded pointer @@ -219,6 +222,10 @@ namespace cds { namespace intrusive { , typename cds::opt::make_options< traits, Options...>::type > type; }; + + // Stat selector + template + using select_stat_wrapper = lazy_list::select_stat_wrapper< Stat >; //@endcond protected: @@ -231,8 +238,8 @@ namespace cds { namespace intrusive { node_type m_Tail; item_counter m_ItemCounter; + stat m_Stat; ///< Internal statistics - //@cond struct clean_disposer { void operator()( value_type * p ) { @@ -498,10 +505,18 @@ namespace cds { namespace intrusive { /// Default constructor initializes empty list LazyList() { - static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" ); m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed ); } + //@cond + template >::value >> + explicit LazyList( Stat& st ) + : m_Stat( st ) + { + m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed ); + } + //@endcond + /// Destroys the list object ~LazyList() { @@ -910,13 +925,19 @@ namespace cds { namespace intrusive { this function always returns 0. @note Even if you use real item counter and it returns 0, this fact does not mean that the list - is empty. To check list emptyness use \p empty() method. + is empty. To check list emptiness use \p empty() method. */ size_t size() const { return m_ItemCounter.value(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + protected: //@cond // split-list support @@ -948,16 +969,22 @@ namespace cds { namespace intrusive { if ( validate( pos.pPred, pos.pCur )) { if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { // failed: key already in list + m_Stat.onInsertFailed(); return false; } else { link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); - ++m_ItemCounter; - return true; + break; } } } + + m_Stat.onInsertRetry(); } + + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; } template @@ -973,17 +1000,23 @@ namespace cds { namespace intrusive { if ( validate( pos.pPred, pos.pCur )) { if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { // failed: key already in list + m_Stat.onInsertFailed(); return false; } else { link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); f( val ); - ++m_ItemCounter; - return true; + break; } } } + + m_Stat.onInsertRetry(); } + + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; } template @@ -1001,21 +1034,29 @@ namespace cds { namespace intrusive { // key already in the list func( false, *node_traits::to_value_ptr( *pos.pCur ) , val ); + m_Stat.onUpdateExisting(); return std::make_pair( true, false ); } else { // new key - if ( !bAllowInsert ) + if ( !bAllowInsert ) { + m_Stat.onUpdateFailed(); return std::make_pair( false, false ); + } link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); func( true, val, val ); - ++m_ItemCounter; - return std::make_pair( true, true ); + break; } } } + + m_Stat.onUpdateRetry(); } + + ++m_ItemCounter; + m_Stat.onUpdateNew(); + return std::make_pair( true, true ); } bool unlink_at( node_type * pHead, value_type& val ) @@ -1036,21 +1077,27 @@ namespace cds { namespace intrusive { { // item found unlink_node( pos.pPred, pos.pCur, pHead ); - --m_ItemCounter; nResult = 1; } else nResult = -1; } } + if ( nResult ) { if ( nResult > 0 ) { + --m_ItemCounter; retire_node( pos.pCur ); + m_Stat.onEraseSuccess(); return true; } + + m_Stat.onEraseFailed(); return false; } } + + m_Stat.onEraseRetry(); } } @@ -1068,7 +1115,6 @@ namespace cds { namespace intrusive { // key found unlink_node( pos.pPred, pos.pCur, pHead ); f( *node_traits::to_value_ptr( *pos.pCur )); - --m_ItemCounter; nResult = 1; } else { @@ -1078,12 +1124,18 @@ namespace cds { namespace intrusive { } if ( nResult ) { if ( nResult > 0 ) { + --m_ItemCounter; retire_node( pos.pCur ); + m_Stat.onEraseSuccess(); return true; } + + m_Stat.onEraseFailed(); return false; } } + + m_Stat.onEraseRetry(); } } @@ -1124,9 +1176,12 @@ namespace cds { namespace intrusive { && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { f( *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onFindSuccess(); return true; } } + + m_Stat.onFindFailed(); return false; } @@ -1136,9 +1191,13 @@ namespace cds { namespace intrusive { position pos; search( pHead, val, pos, cmp ); - return pos.pCur != &m_Tail - && !pos.pCur->is_marked() - && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0; + if ( pos.pCur != &m_Tail && !pos.pCur->is_marked() && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { + m_Stat.onFindSuccess(); + return true; + } + + m_Stat.onFindFailed(); + return false; } template @@ -1152,8 +1211,11 @@ namespace cds { namespace intrusive { && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { gp.set( pos.guards.template get( position::guard_current_item )); + m_Stat.onFindSuccess(); return true; } + + m_Stat.onFindFailed(); return false; } @@ -1190,7 +1252,18 @@ namespace cds { namespace intrusive { pos.pPred = pPrev.ptr(); } - static bool validate( node_type * pPred, node_type * pCur ) + bool validate( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT + { + if ( validate_link( pPred, pCur )) { + m_Stat.onValidationSuccess(); + return true; + } + + m_Stat.onValidationFailed(); + return true; + } + + static bool validate_link( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT { return !pPred->is_marked() && !pCur->is_marked() diff --git a/cds/intrusive/impl/michael_list.h b/cds/intrusive/impl/michael_list.h index 37be7ebb..8e58bf4a 100644 --- a/cds/intrusive/impl/michael_list.h +++ b/cds/intrusive/impl/michael_list.h @@ -201,6 +201,7 @@ namespace cds { namespace intrusive { # endif typedef typename traits::disposer disposer; ///< disposer used + typedef typename traits::stat stat; ///< Internal statistics typedef typename get_node_traits< value_type, node_type, hook>::type node_traits ; ///< node traits typedef typename michael_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker @@ -223,6 +224,10 @@ namespace cds { namespace intrusive { , typename cds::opt::make_options< traits, Options...>::type > type; }; + + // Stat selector + template + using select_stat_wrapper = michael_list::select_stat_wrapper< Stat >; //@endcond protected: @@ -231,8 +236,9 @@ namespace cds { namespace intrusive { typedef atomic_node_ptr auxiliary_head; ///< Auxiliary head type (for split-list support) - atomic_node_ptr m_pHead; ///< Head pointer - item_counter m_ItemCounter; ///< Item counter + atomic_node_ptr m_pHead; ///< Head pointer + item_counter m_ItemCounter; ///< Item counter + stat m_Stat; ///< Internal statistics //@cond /// Position pointer for item search @@ -518,6 +524,14 @@ namespace cds { namespace intrusive { static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" ); } + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : m_pHead( nullptr ) + , m_Stat( st ) + {} + //@endcond + /// Destroys the list object ~MichaelList() { @@ -583,8 +597,8 @@ namespace cds { namespace intrusive { that during changing no any other modifications could be made on this item by concurrent threads. Returns std::pair where \p first is \p true if operation is successful, - \p second is \p true if new item has been added or \p false if the item with \p key - already is in the list. + \p second is \p true if new item has been added or \p false if the item with that key + already in the list. @warning See \ref cds_intrusive_item_creating "insert item troubleshooting" */ @@ -925,13 +939,19 @@ namespace cds { namespace intrusive { this function always returns 0. @note Even if you use real item counter and it returns 0, this fact does not mean that the list - is empty. To check list emptyness use \p empty() method. + is empty. To check list emptiness use \p empty() method. */ size_t size() const { return m_ItemCounter.value(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + protected: //@cond // split-list support @@ -957,16 +977,18 @@ namespace cds { namespace intrusive { position pos; while ( true ) { - if ( search( refHead, val, pos, key_comparator())) + if ( search( refHead, val, pos, key_comparator())) { + m_Stat.onInsertFailed(); return false; + } if ( link_node( pNode, pos )) { ++m_ItemCounter; + m_Stat.onInsertSuccess(); return true; } - // clear next field - pNode->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed ); + m_Stat.onInsertRetry(); } } @@ -977,19 +999,21 @@ namespace cds { namespace intrusive { position pos; while ( true ) { - if ( search( refHead, val, pos, key_comparator())) + if ( search( refHead, val, pos, key_comparator())) { + m_Stat.onInsertFailed(); return false; + } typename gc::Guard guard; guard.assign( &val ); if ( link_node( pNode, pos )) { f( val ); ++m_ItemCounter; + m_Stat.onInsertSuccess(); return true; } - // clear next field - pNode->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed ); + m_Stat.onInsertRetry(); } } @@ -1003,27 +1027,32 @@ namespace cds { namespace intrusive { if ( search( refHead, val, pos, key_comparator())) { if ( cds_unlikely( pos.pCur->m_pNext.load(memory_model::memory_order_acquire).bits())) { back_off()(); + m_Stat.onUpdateMarked(); continue; // the node found is marked as deleted } assert( key_comparator()( val, *node_traits::to_value_ptr( *pos.pCur )) == 0 ); func( false, *node_traits::to_value_ptr( *pos.pCur ) , val ); + m_Stat.onUpdateExisting(); return std::make_pair( true, false ); } else { - if ( !bInsert ) + if ( !bInsert ) { + m_Stat.onUpdateFailed(); return std::make_pair( false, false ); + } typename gc::Guard guard; guard.assign( &val ); if ( link_node( pNode, pos )) { ++m_ItemCounter; func( true, val, val ); + m_Stat.onUpdateNew(); return std::make_pair( true, true ); } - // clear next field - pNode->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed ); } + + m_Stat.onUpdateRetry(); } } @@ -1036,14 +1065,21 @@ namespace cds { namespace intrusive { if ( node_traits::to_value_ptr( *pos.pCur ) == &val ) { if ( unlink_node( pos )) { --m_ItemCounter; + m_Stat.onEraseSuccess(); return true; } else bkoff(); } - else + else { + m_Stat.onUpdateFailed(); break; + } + + m_Stat.onEraseRetry(); } + + m_Stat.onEraseFailed(); return false; } @@ -1055,11 +1091,16 @@ namespace cds { namespace intrusive { if ( unlink_node( pos )) { f( *node_traits::to_value_ptr( *pos.pCur )); --m_ItemCounter; + m_Stat.onEraseSuccess(); return true; } else bkoff(); + + m_Stat.onEraseRetry(); } + + m_Stat.onEraseFailed(); return false; } @@ -1086,11 +1127,15 @@ namespace cds { namespace intrusive { if ( unlink_node( pos )) { dest.set( pos.guards.template get( position::guard_current_item )); --m_ItemCounter; + m_Stat.onEraseSuccess(); return true; } else bkoff(); + m_Stat.onEraseRetry(); } + + m_Stat.onEraseFailed(); return false; } @@ -1098,7 +1143,13 @@ namespace cds { namespace intrusive { bool find_at( atomic_node_ptr& refHead, Q const& val, Compare cmp ) { position pos; - return search( refHead, val, pos, cmp ); + if ( search( refHead, val, pos, cmp ) ) { + m_Stat.onFindSuccess(); + return true; + } + + m_Stat.onFindFailed(); + return false; } template @@ -1107,8 +1158,11 @@ namespace cds { namespace intrusive { position pos; if ( search( refHead, val, pos, cmp )) { f( *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onFindSuccess(); return true; } + + m_Stat.onFindFailed(); return false; } @@ -1118,8 +1172,11 @@ namespace cds { namespace intrusive { position pos; if ( search( refHead, val, pos, cmp )) { guard.set( pos.guards.template get( position::guard_current_item )); + m_Stat.onFindSuccess(); return true; } + + m_Stat.onFindFailed(); return false; } @@ -1171,9 +1228,11 @@ namespace cds { namespace intrusive { marked_node_ptr cur( pCur.ptr()); if ( cds_unlikely( pPrev->compare_exchange_strong( cur, marked_node_ptr( pNext.ptr()), memory_model::memory_order_acquire, atomics::memory_order_relaxed ))) { retire_node( pCur.ptr()); + m_Stat.onHelpingSuccess(); } else { bkoff(); + m_Stat.onHelpingFailed(); goto try_again; } } diff --git a/cds/intrusive/iterable_list_dhp.h b/cds/intrusive/iterable_list_dhp.h new file mode 100644 index 00000000..5685f1d7 --- /dev/null +++ b/cds/intrusive/iterable_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-2016 + + 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_ITERABLE_LIST_DHP_H +#define CDSLIB_INTRUSIVE_ITERABLE_LIST_DHP_H + +#include +#include + +#endif // #ifndef CDSLIB_INTRUSIVE_ITERABLE_LIST_DHP_H diff --git a/cds/intrusive/iterable_list_hp.h b/cds/intrusive/iterable_list_hp.h new file mode 100644 index 00000000..c2718b38 --- /dev/null +++ b/cds/intrusive/iterable_list_hp.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-2016 + + 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_ITERABLE_LIST_HP_H +#define CDSLIB_INTRUSIVE_ITERABLE_LIST_HP_H + +#include +#include + +#endif // #ifndef CDSLIB_INTRUSIVE_ITERABLE_LIST_HP_H diff --git a/cds/intrusive/lazy_list_nogc.h b/cds/intrusive/lazy_list_nogc.h index 38384ee9..40183627 100644 --- a/cds/intrusive/lazy_list_nogc.h +++ b/cds/intrusive/lazy_list_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_LAZY_LIST_NOGC_H @@ -119,10 +119,13 @@ namespace cds { namespace intrusive { typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker - typedef typename traits::item_counter item_counter; ///< Item counting policy used - typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see lazy_list::traits::memory_model) + typedef typename traits::item_counter item_counter; ///< Item counting policy used + typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model) + typedef typename traits::stat stat; ///< Internal statistics //@cond + static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type"); + // Rebind traits (split-list support) template struct rebind_traits { @@ -132,6 +135,10 @@ namespace cds { namespace intrusive { , typename cds::opt::make_options< traits, Options...>::type > type; }; + + // Stat selector + template + using select_stat_wrapper = lazy_list::select_stat_wrapper< Stat >; //@endcond protected: @@ -141,6 +148,7 @@ namespace cds { namespace intrusive { node_type m_Head; ///< List head (dummy node) node_type m_Tail; ///< List tail (dummy node) item_counter m_ItemCounter; ///< Item counter + mutable stat m_Stat; ///< Internal statistics //@cond @@ -348,10 +356,18 @@ namespace cds { namespace intrusive { /// Default constructor initializes empty list LazyList() { - static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" ); m_Head.m_pNext.store( &m_Tail, memory_model::memory_order_relaxed ); } + //@cond + template >::value >> + explicit LazyList( Stat& st ) + : m_Stat( st ) + { + m_Head.m_pNext.store( &m_Tail, memory_model::memory_order_relaxed ); + } + //@endcond + /// Destroys the list object ~LazyList() { @@ -592,6 +608,12 @@ namespace cds { namespace intrusive { return m_ItemCounter.value(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + protected: //@cond // split-list support @@ -624,16 +646,22 @@ namespace cds { namespace intrusive { if ( validate( pos.pPred, pos.pCur )) { if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) { // failed: key already in list + m_Stat.onInsertFailed(); return false; } else { link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); - ++m_ItemCounter; - return true; + break; } } } + + m_Stat.onInsertRetry(); } + + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; } iterator insert_at_( node_type * pHead, value_type& val ) @@ -659,21 +687,29 @@ namespace cds { namespace intrusive { // key already in the list func( false, *node_traits::to_value_ptr( *pos.pCur ) , val ); + m_Stat.onUpdateExisting(); return std::make_pair( iterator( pos.pCur ), false ); } else { // new key - if ( !bAllowInsert ) + if ( !bAllowInsert ) { + m_Stat.onUpdateFailed(); return std::make_pair( end(), false ); + } link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); func( true, val, val ); - ++m_ItemCounter; - return std::make_pair( iterator( node_traits::to_node_ptr( val )), true ); + break; } } + + m_Stat.onUpdateRetry(); } } + + ++m_ItemCounter; + m_Stat.onUpdateNew(); + return std::make_pair( iterator( node_traits::to_node_ptr( val ) ), true ); } template @@ -694,9 +730,12 @@ namespace cds { namespace intrusive { if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) { f( *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onFindSuccess(); return true; } } + + m_Stat.onFindFailed(); return false; } @@ -716,9 +755,13 @@ namespace cds { namespace intrusive { search( pHead, val, pos, pred ); if ( pos.pCur != &m_Tail ) { - if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred )) + if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred )) { + m_Stat.onFindSuccess(); return iterator( pos.pCur ); + } } + + m_Stat.onFindFailed(); return end(); } @@ -772,9 +815,15 @@ namespace cds { namespace intrusive { return cmp(l, r) == 0; } - static bool validate( node_type * pPred, node_type * pCur ) + bool validate( node_type * pPred, node_type * pCur ) { - return pPred->m_pNext.load(memory_model::memory_order_acquire) == pCur; + if ( pPred->m_pNext.load(memory_model::memory_order_acquire) == pCur ) { + m_Stat.onValidationSuccess(); + return true; + } + + m_Stat.onValidationFailed(); + return false; } // for split-list diff --git a/cds/intrusive/lazy_list_rcu.h b/cds/intrusive/lazy_list_rcu.h index d22474a2..935c294c 100644 --- a/cds/intrusive/lazy_list_rcu.h +++ b/cds/intrusive/lazy_list_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_LAZY_LIST_RCU_H @@ -88,20 +88,8 @@ namespace cds { namespace intrusive { - \p RCU - one of \ref cds_urcu_gc "RCU type" - \p T - type to be stored in the list - \p Traits - type traits. See \p lazy_list::traits for explanation. - - It is possible to declare option-based list with \p %cds::intrusive::lazy_list::make_traits metafunction istead of \p Traits template - argument. Template argument list \p Options of cds::intrusive::lazy_list::make_traits metafunction are: - - opt::hook - hook used. Possible values are: lazy_list::base_hook, lazy_list::member_hook, lazy_list::traits_hook. - If the option is not specified, lazy_list::base_hook<> is used. - - opt::compare - key comparison functor. No default functor is provided. - If the option is not specified, the opt::less is used. - - opt::less - specifies binary predicate used for key comparison. Default is \p std::less. - - opt::back_off - back-off strategy used. If the option is not specified, the cds::backoff::empty is used. - - opt::disposer - the functor used for dispose removed items. Default is opt::v::empty_disposer - - opt::rcu_check_deadlock - a deadlock checking policy. Default is opt::v::rcu_throw_deadlock - - opt::item_counter - the type of item counting feature. Default is \ref atomicity::empty_item_counter - - opt::memory_model - C++ memory ordering model. Can be opt::v::relaxed_ordering (relaxed memory model, the default) - or opt::v::sequential_consistent (sequentially consisnent memory model). + It is possible to declare option-based list with \p %cds::intrusive::lazy_list::make_traits metafunction instead of \p Traits template + argument. \par Usage Before including you should include appropriate RCU header file, @@ -145,14 +133,17 @@ namespace cds { namespace intrusive { typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker - typedef typename traits::back_off back_off; ///< back-off strategy (not used) - typedef typename traits::item_counter item_counter; ///< Item counting policy used - typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model) - typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy + typedef typename traits::back_off back_off; ///< back-off strategy (not used) + typedef typename traits::item_counter item_counter; ///< Item counting policy used + typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model) + typedef typename traits::stat stat; ///< Internal statistics + typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock static CDS_CONSTEXPR const bool c_bExtractLockExternal = true; ///< Group of \p extract_xxx functions require external locking + static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type"); + //@cond // Rebind traits (split-list support) template @@ -163,16 +154,23 @@ namespace cds { namespace intrusive { , typename cds::opt::make_options< traits, Options...>::type > type; }; + + // Stat selector + template + using select_stat_wrapper = lazy_list::select_stat_wrapper< Stat >; //@endcond protected: - typedef typename node_type::marked_ptr marked_node_ptr; ///< Node marked pointer - typedef node_type * auxiliary_head; ///< Auxiliary head type (for split-list support) + //@cond + typedef typename node_type::marked_ptr marked_node_ptr; ///< Node marked pointer + typedef node_type * auxiliary_head; ///< Auxiliary head type (for split-list support) + //@endcond protected: node_type m_Head; ///< List head (dummy node) node_type m_Tail; ///< List tail (dummy node) item_counter m_ItemCounter; ///< Item counter + mutable stat m_Stat; ///< Internal statistics //@cond @@ -430,10 +428,18 @@ namespace cds { namespace intrusive { /// Default constructor initializes empty list LazyList() { - static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" ); m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed ); } + //@cond + template >::value >> + explicit LazyList( Stat& st ) + : m_Stat( st ) + { + m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed ); + } + //@endcond + /// Destroys the list object ~LazyList() { @@ -879,13 +885,19 @@ namespace cds { namespace intrusive { this function always returns 0. Warning: even if you use real item counter and it returns 0, this fact is not mean that the list - is empty. To check list emptyness use \ref empty() method. + is empty. To check list emptiness use \ref empty() method. */ size_t size() const { return m_ItemCounter.value(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + protected: //@cond // split-list support @@ -926,16 +938,22 @@ namespace cds { namespace intrusive { if ( validate( pos.pPred, pos.pCur )) { if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { // failed: key already in list + m_Stat.onInsertFailed(); return false; } f( val ); link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); - ++m_ItemCounter; - return true; + break; } } + + m_Stat.onInsertRetry(); } + + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; } iterator insert_at_( node_type * pHead, value_type& val ) @@ -982,7 +1000,6 @@ namespace cds { namespace intrusive { { // item found unlink_node( pos.pPred, pos.pCur, pHead ); - --m_ItemCounter; nResult = 1; } else @@ -993,11 +1010,17 @@ namespace cds { namespace intrusive { if ( nResult ) { if ( nResult > 0 ) { + --m_ItemCounter; dispose_node( pos.pCur ); + m_Stat.onEraseSuccess(); return true; } + + m_Stat.onEraseFailed(); return false; } + + m_Stat.onEraseRetry(); } } @@ -1018,7 +1041,6 @@ namespace cds { namespace intrusive { // key found unlink_node( pos.pPred, pos.pCur, pHead ); f( *node_traits::to_value_ptr( *pos.pCur )); - --m_ItemCounter; nResult = 1; } else @@ -1029,11 +1051,17 @@ namespace cds { namespace intrusive { if ( nResult ) { if ( nResult > 0 ) { + --m_ItemCounter; dispose_node( pos.pCur ); + m_Stat.onEraseSuccess(); return true; } + + m_Stat.onEraseFailed(); return false; } + + m_Stat.onEraseRetry(); } } @@ -1066,7 +1094,6 @@ namespace cds { namespace intrusive { if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { // key found unlink_node( pos.pPred, pos.pCur, pHead ); - --m_ItemCounter; nResult = 1; } else { @@ -1076,10 +1103,17 @@ namespace cds { namespace intrusive { } if ( nResult ) { - if ( nResult > 0 ) + if ( nResult > 0 ) { + --m_ItemCounter; + m_Stat.onEraseSuccess(); return node_traits::to_value_ptr( pos.pCur ); + } + + m_Stat.onEraseFailed(); return nullptr; } + + m_Stat.onEraseRetry(); } } @@ -1092,12 +1126,14 @@ namespace cds { namespace intrusive { search( pHead, val, pos, cmp ); if ( pos.pCur != &m_Tail ) { std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock ); - if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) - { + if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { f( *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onFindSuccess(); return true; } } + + m_Stat.onFindFailed(); return false; } @@ -1117,9 +1153,13 @@ namespace cds { namespace intrusive { search( pHead, val, pos, cmp ); if ( pos.pCur != &m_Tail ) { - if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) + if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { + m_Stat.onFindSuccess(); return const_iterator( pos.pCur ); + } } + + m_Stat.onFindFailed(); return end(); } @@ -1163,7 +1203,18 @@ namespace cds { namespace intrusive { pos.pPred = pPrev.ptr(); } - static bool validate( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT + bool validate( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT + { + if ( validate_link( pPred, pCur ) ) { + m_Stat.onValidationSuccess(); + return true; + } + + m_Stat.onValidationFailed(); + return false; + } + + static bool validate_link( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT { // RCU lock should be locked assert( gc::is_locked()); @@ -1192,15 +1243,22 @@ namespace cds { namespace intrusive { if ( validate( pos.pPred, pos.pCur )) { if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) { // failed: key already in list + m_Stat.onInsertFailed(); return false; } link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); - ++m_ItemCounter; - return true; + break; } } + + m_Stat.onInsertRetry(); } + + ++m_ItemCounter; + m_Stat.onInsertSuccess(); + return true; + } template @@ -1221,21 +1279,29 @@ namespace cds { namespace intrusive { // key already in the list func( false, *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onUpdateExisting(); return std::make_pair( iterator( pos.pCur ), false ); } else { // new key - if ( !bAllowInsert ) + if ( !bAllowInsert ) { + m_Stat.onUpdateFailed(); return std::make_pair( end(), false ); + } func( true, val, val ); link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur ); - ++m_ItemCounter; - return std::make_pair( iterator( node_traits::to_node_ptr( val )), true ); + break; } } } + + m_Stat.onUpdateRetry(); } + + ++m_ItemCounter; + m_Stat.onUpdateNew(); + return std::make_pair( iterator( node_traits::to_node_ptr( val )), true ); } //@endcond }; diff --git a/cds/intrusive/michael_list_nogc.h b/cds/intrusive/michael_list_nogc.h index 3e3920ba..2fa260cf 100644 --- a/cds/intrusive/michael_list_nogc.h +++ b/cds/intrusive/michael_list_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_MICHAEL_LIST_NOGC_H @@ -35,7 +35,6 @@ #include #include - namespace cds { namespace intrusive { namespace michael_list { @@ -96,11 +95,14 @@ namespace cds { namespace intrusive { typedef typename get_node_traits< value_type, node_type, hook>::type node_traits ; ///< node traits typedef typename michael_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker - typedef typename traits::back_off back_off; ///< back-off strategy - typedef typename traits::item_counter item_counter; ///< Item counting policy used - typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::back_off back_off; ///< back-off strategy + typedef typename traits::item_counter item_counter; ///< Item counting policy used + typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::stat stat; ///< Internal statistics //@cond + static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type"); + // Rebind traits (split-list support) template struct rebind_traits { @@ -110,6 +112,10 @@ namespace cds { namespace intrusive { , typename cds::opt::make_options< traits, Options...>::type > type; }; + + // Stat selector + template + using select_stat_wrapper = michael_list::select_stat_wrapper< Stat >; //@endcond protected: @@ -118,6 +124,7 @@ namespace cds { namespace intrusive { atomic_node_ptr m_pHead; ///< Head pointer item_counter m_ItemCounter; ///< Item counter + stat m_Stat; ///< Internal statistics //@cond /// Position pointer for item search @@ -309,9 +316,15 @@ namespace cds { namespace intrusive { /// Default constructor initializes empty list MichaelList() : m_pHead( nullptr ) - { - static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" ); - } + {} + + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : m_pHead( nullptr ) + , m_Stat( st ) + {} + //@endcond /// Destroys the list objects ~MichaelList() @@ -513,6 +526,12 @@ namespace cds { namespace intrusive { return m_ItemCounter.value(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + protected: //@cond // split-list support @@ -537,13 +556,18 @@ namespace cds { namespace intrusive { position pos; while ( true ) { - if ( search( refHead, val, key_comparator(), pos ) ) + if ( search( refHead, val, key_comparator(), pos )) { + m_Stat.onInsertFailed(); return false; + } if ( link_node( node_traits::to_node_ptr( val ), pos ) ) { ++m_ItemCounter; + m_Stat.onInsertSuccess(); return true; } + + m_Stat.onInsertRetry(); } } @@ -564,18 +588,24 @@ namespace cds { namespace intrusive { assert( key_comparator()( val, *node_traits::to_value_ptr( *pos.pCur ) ) == 0 ); func( false, *node_traits::to_value_ptr( *pos.pCur ) , val ); + m_Stat.onUpdateExisting(); return std::make_pair( iterator( pos.pCur ), false ); } else { - if ( !bAllowInsert ) + if ( !bAllowInsert ) { + m_Stat.onUpdateFailed(); return std::make_pair( end(), false ); + } if ( link_node( node_traits::to_node_ptr( val ), pos ) ) { ++m_ItemCounter; func( true, val , val ); + m_Stat.onUpdateNew(); return std::make_pair( iterator( node_traits::to_node_ptr( val )), true ); } } + + m_Stat.onUpdateRetry(); } } @@ -594,8 +624,11 @@ namespace cds { namespace intrusive { if ( search( refHead, val, cmp, pos ) ) { assert( pos.pCur != nullptr ); f( *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onFindSuccess(); return true; } + + m_Stat.onFindFailed(); return false; } @@ -603,8 +636,12 @@ namespace cds { namespace intrusive { value_type * find_at( atomic_node_ptr& refHead, Q const& val, Compare cmp ) { iterator it = find_at_( refHead, val, cmp ); - if ( it != end() ) + if ( it != end() ) { + m_Stat.onFindSuccess(); return &*it; + } + + m_Stat.onFindFailed(); return nullptr; } @@ -615,8 +652,11 @@ namespace cds { namespace intrusive { if ( search( refHead, val, cmp, pos ) ) { assert( pos.pCur != nullptr ); + m_Stat.onFindSuccess(); return iterator( pos.pCur ); } + + m_Stat.onFindFailed(); return end(); } diff --git a/cds/intrusive/michael_list_rcu.h b/cds/intrusive/michael_list_rcu.h index 2b9ae89a..230b22c6 100644 --- a/cds/intrusive/michael_list_rcu.h +++ b/cds/intrusive/michael_list_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_MICHAEL_LIST_RCU_H @@ -118,11 +118,12 @@ namespace cds { namespace intrusive { typedef typename get_node_traits< value_type, node_type, hook>::type node_traits ; ///< node traits typedef typename michael_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker - typedef cds::urcu::gc gc; ///< RCU schema - typedef typename traits::back_off back_off; ///< back-off strategy - typedef typename traits::item_counter item_counter; ///< Item counting policy used - typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option - typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy + typedef cds::urcu::gc gc; ///< RCU schema + typedef typename traits::back_off back_off; ///< back-off strategy + typedef typename traits::item_counter item_counter; ///< Item counting policy used + typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy + typedef typename traits::stat stat; ///< Internal statistics typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock static CDS_CONSTEXPR const bool c_bExtractLockExternal = false; ///< Group of \p extract_xxx functions do not require external locking @@ -137,15 +138,20 @@ namespace cds { namespace intrusive { , typename cds::opt::make_options< traits, Options...>::type > type; }; + + // Stat selector + template + using select_stat_wrapper = michael_list::select_stat_wrapper< Stat >; //@endcond protected: - typedef typename node_type::marked_ptr marked_node_ptr ; ///< Marked node pointer - typedef typename node_type::atomic_marked_ptr atomic_node_ptr ; ///< Atomic node pointer - typedef atomic_node_ptr auxiliary_head ; ///< Auxiliary head type (for split-list support) + typedef typename node_type::marked_ptr marked_node_ptr; ///< Marked node pointer + typedef typename node_type::atomic_marked_ptr atomic_node_ptr; ///< Atomic node pointer + typedef atomic_node_ptr auxiliary_head; ///< Auxiliary head type (for split-list support) - atomic_node_ptr m_pHead ; ///< Head pointer - item_counter m_ItemCounter ; ///< Item counter + atomic_node_ptr m_pHead; ///< Head pointer + item_counter m_ItemCounter; ///< Item counter + stat m_Stat; ///< Internal statistics protected: //@cond @@ -440,6 +446,14 @@ namespace cds { namespace intrusive { static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" ); } + //@cond + template >::value >> + explicit MichaelList( Stat& st ) + : m_pHead( nullptr ) + , m_Stat( st ) + {} + //@endcond + /// Destroy list ~MichaelList() { @@ -897,6 +911,11 @@ namespace cds { namespace intrusive { return m_ItemCounter.value(); } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } protected: //@cond // split-list support @@ -933,17 +952,21 @@ namespace cds { namespace intrusive { { rcu_lock l; while ( true ) { - if ( search( refHead, val, pos, key_comparator())) + if ( search( refHead, val, pos, key_comparator())) { + m_Stat.onInsertFailed(); return false; + } if ( link_node( node_traits::to_node_ptr( val ), pos ) ) { f( val ); ++m_ItemCounter; + m_Stat.onInsertSuccess(); return true; } // clear next field node_traits::to_node_ptr( val )->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed ); + m_Stat.onInsertRetry(); } } @@ -987,15 +1010,19 @@ namespace cds { namespace intrusive { for (;;) { { rcu_lock l; - if ( !search( refHead, val, pos, key_comparator() ) || node_traits::to_value_ptr( *pos.pCur ) != &val ) + if ( !search( refHead, val, pos, key_comparator() ) || node_traits::to_value_ptr( *pos.pCur ) != &val ) { + m_Stat.onEraseFailed(); return false; + } if ( !unlink_node( pos, erase_mask )) { bkoff(); + m_Stat.onEraseRetry(); continue; } } --m_ItemCounter; + m_Stat.onEraseSuccess(); return true; } } @@ -1010,18 +1037,23 @@ namespace cds { namespace intrusive { for (;;) { { rcu_lock l; - if ( !search( pos.refHead, val, pos, cmp ) ) + if ( !search( pos.refHead, val, pos, cmp ) ) { + m_Stat.onEraseFailed(); return false; + } + // store pCur since it may be changed by unlink_node() slow path pDel = pos.pCur; if ( !unlink_node( pos, erase_mask )) { bkoff(); + m_Stat.onEraseRetry(); continue; } } assert( pDel ); f( *node_traits::to_value_ptr( pDel ) ); --m_ItemCounter; + m_Stat.onEraseSuccess(); return true; } } @@ -1051,18 +1083,23 @@ namespace cds { namespace intrusive { { rcu_lock l; for (;;) { - if ( !search( refHead, val, pos, cmp ) ) + if ( !search( refHead, val, pos, cmp )) { + m_Stat.onEraseFailed(); return nullptr; + } + // store pCur since it may be changed by unlink_node() slow path pExtracted = pos.pCur; if ( !unlink_node( pos, extract_mask )) { bkoff(); + m_Stat.onEraseRetry(); continue; } --m_ItemCounter; value_type * pRet = node_traits::to_value_ptr( pExtracted ); assert( pExtracted->m_pDelChain == nullptr ); + m_Stat.onEraseSuccess(); return pRet; } } @@ -1078,10 +1115,13 @@ namespace cds { namespace intrusive { if ( search( refHead, val, pos, cmp ) ) { assert( pos.pCur != nullptr ); f( *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onFindSuccess(); return true; } - return false; - } + } + + m_Stat.onFindFailed(); + return false; } template @@ -1102,8 +1142,12 @@ namespace cds { namespace intrusive { position pos( refHead ); - if ( search( refHead, val, pos, cmp )) + if ( search( refHead, val, pos, cmp )) { + m_Stat.onFindSuccess(); return raw_ptr( node_traits::to_value_ptr( pos.pCur ), raw_ptr_disposer( pos )); + } + + m_Stat.onFindFailed(); return raw_ptr( raw_ptr_disposer( pos )); } //@endcond @@ -1150,8 +1194,10 @@ namespace cds { namespace intrusive { if ( cds_likely( pPrev->compare_exchange_weak( pCur, marked_node_ptr( pNext.ptr() ), memory_model::memory_order_acquire, atomics::memory_order_relaxed ))) { if ( pNext.bits() == erase_mask ) link_to_remove_chain( pos, pCur.ptr() ); + m_Stat.onHelpingSuccess(); } + m_Stat.onHelpingFailed(); goto try_again; } @@ -1177,16 +1223,20 @@ namespace cds { namespace intrusive { assert( gc::is_locked() ); while ( true ) { - if ( search( pos.refHead, val, pos, key_comparator() ) ) + if ( search( pos.refHead, val, pos, key_comparator() )) { + m_Stat.onInsertFailed(); return false; + } if ( link_node( node_traits::to_node_ptr( val ), pos ) ) { ++m_ItemCounter; + m_Stat.onInsertSuccess(); return true; } // clear next field node_traits::to_node_ptr( val )->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed ); + m_Stat.onInsertRetry(); } } @@ -1201,20 +1251,25 @@ namespace cds { namespace intrusive { assert( key_comparator()( val, *node_traits::to_value_ptr( *pos.pCur ) ) == 0 ); func( false, *node_traits::to_value_ptr( *pos.pCur ), val ); + m_Stat.onUpdateExisting(); return std::make_pair( iterator( pos.pCur ), false ); } else { - if ( !bInsert ) + if ( !bInsert ) { + m_Stat.onUpdateFailed(); return std::make_pair( end(), false ); + } if ( link_node( node_traits::to_node_ptr( val ), pos ) ) { ++m_ItemCounter; func( true, val , val ); + m_Stat.onUpdateNew(); return std::make_pair( iterator( node_traits::to_node_ptr( val )), true ); } // clear the next field node_traits::to_node_ptr( val )->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed ); + m_Stat.onUpdateRetry(); } } } @@ -1226,8 +1281,11 @@ namespace cds { namespace intrusive { if ( search( pos.refHead, val, pos, cmp ) ) { assert( pos.pCur != nullptr ); + m_Stat.onFindSuccess(); return const_iterator( pos.pCur ); } + + m_Stat.onFindFailed(); return cend(); } //@endcond diff --git a/cds/intrusive/michael_set.h b/cds/intrusive/michael_set.h index b876b5ed..b264bb1f 100644 --- a/cds/intrusive/michael_set.h +++ b/cds/intrusive/michael_set.h @@ -25,14 +25,14 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_H #define CDSLIB_INTRUSIVE_MICHAEL_SET_H #include -#include +#include namespace cds { namespace intrusive { @@ -50,7 +50,7 @@ namespace cds { namespace intrusive { Template parameters are: - \p GC - Garbage collector used. Note the \p GC must be the same as the GC used for \p OrderedList - - \p OrderedList - ordered list implementation used as bucket for hash set, for example, \p MichaelList, \p LazyList. + - \p OrderedList - ordered list implementation used as bucket for hash set, for example, \p MichaelList, \p LazyList, \p IterableList. The intrusive ordered list implementation specifies the type \p T stored in the hash-set, the reclamation schema \p GC used by hash-set, the comparison functor for the type \p T and other features specific for the ordered list. @@ -71,7 +71,7 @@ namespace cds { namespace intrusive { \code // Our node type struct Foo { - std::string key_; // key field + std::string key_; // key field // ... other fields }; @@ -248,54 +248,50 @@ namespace cds { namespace intrusive { public: typedef GC gc; ///< Garbage collector typedef OrderedList ordered_list; ///< type of ordered list used as a bucket implementation - typedef ordered_list bucket_type; ///< bucket type - typedef Traits traits; ///< Set traits + typedef Traits traits; ///< Set traits - typedef typename ordered_list::value_type value_type ; ///< type of value to be stored in the set - typedef typename ordered_list::key_comparator key_comparator ; ///< key comparing functor - typedef typename ordered_list::disposer disposer ; ///< Node disposer functor + typedef typename ordered_list::value_type value_type ; ///< type of value to be stored in the set + typedef typename ordered_list::key_comparator key_comparator ; ///< key comparing functor + typedef typename ordered_list::disposer disposer ; ///< Node disposer functor + typedef typename ordered_list::stat stat ; ///< Internal statistics /// Hash functor for \p value_type and all its derivatives that you use typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash; typedef typename traits::item_counter item_counter; ///< Item counter type + typedef typename traits::allocator allocator; ///< Bucket table allocator typedef typename ordered_list::guarded_ptr guarded_ptr; ///< Guarded pointer - /// Bucket table allocator - typedef cds::details::Allocator< bucket_type, typename traits::allocator > bucket_table_allocator; - /// Count of hazard pointer required for the algorithm static CDS_CONSTEXPR const size_t c_nHazardPtrCount = ordered_list::c_nHazardPtrCount; - protected: - item_counter m_ItemCounter; ///< Item counter - hash m_HashFunctor; ///< Hash functor - bucket_type * m_Buckets; ///< bucket table + // GC and OrderedList::gc must be the same + static_assert(std::is_same::value, "GC and OrderedList::gc must be the same"); - private: - //@cond - const size_t m_nHashBitmask; - //@endcond + // atomicity::empty_item_counter is not allowed as a item counter + static_assert(!std::is_same::value, + "cds::atomicity::empty_item_counter is not allowed as a item counter"); protected: //@cond - /// Calculates hash value of \p key - template - size_t hash_value( const Q& key ) const - { - return m_HashFunctor( key ) & m_nHashBitmask; - } + typedef typename ordered_list::template select_stat_wrapper< typename ordered_list::stat > bucket_stat; - /// Returns the bucket (ordered list) for \p key - template - bucket_type& bucket( const Q& key ) - { - return m_Buckets[ hash_value( key ) ]; - } + typedef typename ordered_list::template rebind_traits< + cds::opt::item_counter< cds::atomicity::empty_item_counter > + , cds::opt::stat< typename bucket_stat::wrapped_stat > + >::type internal_bucket_type; + + typedef typename allocator::template rebind< internal_bucket_type >::other bucket_table_allocator; + + hash m_HashFunctor; ///< Hash functor + size_t const m_nHashBitmask; + internal_bucket_type* m_Buckets; ///< bucket table + item_counter m_ItemCounter; ///< Item counter + typename bucket_stat::stat m_Stat; ///< Internal statistics //@endcond public: - ///@name Forward iterators (only for debugging purpose) + ///@name Forward iterators //@{ /// Forward iterator /** @@ -303,19 +299,23 @@ namespace cds { namespace intrusive { - it has no post-increment operator - it iterates items in unordered fashion - The iterator cannot be moved across thread boundary because it may contain GC's guard that is thread-private GC data. - - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent - deleting operations it is no guarantee that you iterate all item in the set. - Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread. - @warning Use this iterator on the concurrent container for debugging purpose only. + Iterator thread safety depends on type of \p OrderedList: + - for \p MichaelList and \p LazyList: iterator guarantees safety even if you delete the item that iterator points to + because that item is guarded by hazard pointer. + However, in case of concurrent deleting operations it is no guarantee that you iterate all item in the set. + Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread. + Use this iterator on the concurrent container for debugging purpose only. + - for \p IterableList: iterator is thread-safe. You may use it freely in concurrent environment. + */ - typedef michael_set::details::iterator< bucket_type, false > iterator; + typedef michael_set::details::iterator< internal_bucket_type, false > iterator; /// Const forward iterator /** For iterator's features and requirements see \ref iterator */ - typedef michael_set::details::iterator< bucket_type, true > const_iterator; + typedef michael_set::details::iterator< internal_bucket_type, true > const_iterator; /// Returns a forward iterator addressing the first element in a set /** @@ -323,7 +323,7 @@ namespace cds { namespace intrusive { */ iterator begin() { - return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count() ); + return iterator( m_Buckets[0].begin(), bucket_begin(), bucket_end() ); } /// Returns an iterator that addresses the location succeeding the last element in a set @@ -334,7 +334,7 @@ namespace cds { namespace intrusive { */ iterator end() { - return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() ); + return iterator( bucket_end()[-1].end(), bucket_end() - 1, bucket_end() ); } /// Returns a forward const iterator addressing the first element in a set @@ -362,18 +362,6 @@ namespace cds { namespace intrusive { } //@} - private: - //@cond - const_iterator get_const_begin() const - { - return const_iterator( m_Buckets[0].cbegin(), m_Buckets, m_Buckets + bucket_count() ); - } - const_iterator get_const_end() const - { - return const_iterator( m_Buckets[bucket_count() - 1].cend(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() ); - } - //@endcond - public: /// Initializes hash set /** @anchor cds_intrusive_MichaelHashSet_hp_ctor @@ -387,22 +375,20 @@ namespace cds { namespace intrusive { size_t nMaxItemCount, ///< estimation of max item count in the hash set size_t nLoadFactor ///< load factor: estimation of max number of items in the bucket. Small integer up to 10. ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor )) + , m_Buckets( bucket_table_allocator().allocate( bucket_count())) { - // GC and OrderedList::gc must be the same - static_assert( std::is_same::value, "GC and OrderedList::gc must be the same"); - - // atomicity::empty_item_counter is not allowed as a item counter - static_assert( !std::is_same::value, - "cds::atomicity::empty_item_counter is not allowed as a item counter"); - - m_Buckets = bucket_table_allocator().NewArray( bucket_count() ); + for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it ) + construct_bucket( it ); } /// Clears hash set object and destroys it ~MichaelHashSet() { clear(); - bucket_table_allocator().Delete( m_Buckets, bucket_count() ); + + for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it ) + it->~internal_bucket_type(); + bucket_table_allocator().deallocate( m_Buckets, bucket_count() ); } /// Inserts new node @@ -456,28 +442,38 @@ namespace cds { namespace intrusive { If the item \p val not found in the set, then \p val is inserted iff \p bAllowInsert is \p true. Otherwise, the functor \p func is called with item found. - The functor signature is: - \code - struct functor { - void operator()( bool bNew, value_type& item, value_type& val ); - }; - \endcode - with arguments: - - \p bNew - \p true if the item has been inserted, \p false otherwise - - \p item - item of the set - - \p val - argument \p val passed into the \p %update() function - If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments - refers to the same thing. - The functor may change non-key fields of the \p item. + The functor signature depends of the type of \p OrderedList: + + for \p MichaelList, \p LazyList + \code + struct functor { + void operator()( bool bNew, value_type& item, value_type& val ); + }; + \endcode + with arguments: + - \p bNew - \p true if the item has been inserted, \p false otherwise + - \p item - item of the set + - \p val - argument \p val passed into the \p %update() function + If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments + refers to the same thing. + + The functor may change non-key fields of the \p item. + @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting". + \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level + synchronization. + + for \p IterableList + \code + void func( value_type& val, value_type * old ); + \endcode + where + - \p val - argument \p val passed into the \p %update() function + - \p old - old value that will be retired. If new item has been inserted then \p old is \p nullptr. Returns std::pair where \p first is \p true if operation is successful, \p second is \p true if new item has been added or \p false if the item with \p key already is in the set. - - @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting". - \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level - synchronization. */ template std::pair update( value_type& val, Func func, bool bAllowInsert = true ) @@ -496,6 +492,35 @@ namespace cds { namespace intrusive { } //@endcond + /// Inserts or updates the node (only for \p IterableList) + /** + The operation performs inserting or changing data with lock-free manner. + + If the item \p val is not found in the set, then \p val is inserted iff \p bAllowInsert is \p true. + Otherwise, the current element is changed to \p val, the old element will be retired later + by call \p Traits::disposer. + + Returns std::pair where \p first is \p true if operation is successful, + \p second is \p true if \p val has been added or \p false if the item with that key + already in the set. + */ +#ifdef CDS_DOXYGEN_INVOKED + std::pair upsert( value_type& val, bool bAllowInsert = true ) +#else + template + typename std::enable_if< + std::is_same< Q, value_type>::value && is_iterable_list< ordered_list >::value, + std::pair + >::type + upsert( Q& val, bool bAllowInsert = true ) +#endif + { + std::pair bRet = bucket( val ).upsert( val, bAllowInsert ); + if ( bRet.second ) + ++m_ItemCounter; + return bRet; + } + /// Unlinks the item \p val from the set /** The function searches the item \p val in the set and unlink it @@ -682,6 +707,40 @@ namespace cds { namespace intrusive { } //@endcond + /// Finds \p key and returns iterator pointed to the item found (only for \p IterableList) + /** + If \p key is not found the function returns \p end(). + + @note This function is supported only for the set based on \p IterableList + */ + template +#ifdef CDS_DOXYGEN_INVOKED + iterator +#else + typename std::enable_if< std::is_same::value && is_iterable_list< ordered_list >::value, iterator >::type +#endif + find( Q& key ) + { + internal_bucket_type& b = bucket( key ); + typename internal_bucket_type::iterator it = b.find( key ); + if ( it == b.end() ) + return end(); + return iterator( it, &b, bucket_end()); + } + //@cond + template + typename std::enable_if< std::is_same::value && is_iterable_list< ordered_list >::value, iterator >::type + find( Q const& key ) + { + internal_bucket_type& b = bucket( key ); + typename internal_bucket_type::iterator it = b.find( key ); + if ( it == b.end() ) + return end(); + return iterator( it, &b, bucket_end() ); + } + //@endcond + + /// Finds the key \p key using \p pred predicate for searching /** The function is an analog of \ref cds_intrusive_MichaelHashSet_hp_find_func "find(Q&, Func)" @@ -702,6 +761,43 @@ namespace cds { namespace intrusive { } //@endcond + /// Finds \p key using \p pred predicate and returns iterator pointed to the item found (only for \p IterableList) + /** + The function is an analog of \p find(Q&) but \p pred is used for key comparing. + \p Less functor has the interface like \p std::less. + \p pred must imply the same element order as the comparator used for building the set. + + If \p key is not found the function returns \p end(). + + @note This function is supported only for the set based on \p IterableList + */ + template +#ifdef CDS_DOXYGEN_INVOKED + iterator +#else + typename std::enable_if< std::is_same::value && is_iterable_list< ordered_list >::value, iterator >::type +#endif + find_with( Q& key, Less pred ) + { + internal_bucket_type& b = bucket( key ); + typename internal_bucket_type::iterator it = b.find_with( key, pred ); + if ( it == b.end() ) + return end(); + return iterator( it, &b, bucket_end() ); + } + //@cond + template + typename std::enable_if< std::is_same::value && is_iterable_list< ordered_list >::value, iterator >::type + find_with( Q const& key, Less pred ) + { + internal_bucket_type& b = bucket( key ); + typename internal_bucket_type::iterator it = b.find_with( key, pred ); + if ( it == b.end() ) + return end(); + return iterator( it, &b, bucket_end() ); + } + //@endcond + /// Checks whether the set contains \p key /** @@ -716,14 +812,6 @@ namespace cds { namespace intrusive { { return bucket( key ).contains( key ); } - //@cond - template - CDS_DEPRECATED("use contains()") - bool find( Q const& key ) - { - return contains( key ); - } - //@endcond /// Checks whether the set contains \p key using \p pred predicate for searching /** @@ -736,14 +824,6 @@ namespace cds { namespace intrusive { { return bucket( key ).contains( key, pred ); } - //@cond - template - CDS_DEPRECATED("use contains()") - bool find_with( Q const& key, Less pred ) - { - return contains( key, pred ); - } - //@endcond /// Finds the key \p key and return the item found /** \anchor cds_intrusive_MichaelHashSet_hp_get @@ -825,6 +905,12 @@ namespace cds { namespace intrusive { return m_ItemCounter; } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + /// Returns the size of hash table /** Since \p %MichaelHashSet cannot dynamically extend the hash table size, @@ -835,6 +921,54 @@ namespace cds { namespace intrusive { { return m_nHashBitmask + 1; } + + private: + //@cond + internal_bucket_type * bucket_begin() const + { + return m_Buckets; + } + + internal_bucket_type * bucket_end() const + { + return m_Buckets + bucket_count(); + } + + const_iterator get_const_begin() const + { + return const_iterator( m_Buckets[0].cbegin(), bucket_begin(), bucket_end() ); + } + const_iterator get_const_end() const + { + return const_iterator( bucket_end()[-1].cend(), bucket_end() - 1, bucket_end() ); + } + + template + typename std::enable_if< Stat::empty >::type construct_bucket( internal_bucket_type * bucket ) + { + new (bucket) internal_bucket_type; + } + + template + typename std::enable_if< !Stat::empty >::type construct_bucket( internal_bucket_type * bucket ) + { + new (bucket) internal_bucket_type( m_Stat ); + } + + /// Calculates hash value of \p key + template + size_t hash_value( const Q& key ) const + { + return m_HashFunctor( key ) & m_nHashBitmask; + } + + /// Returns the bucket (ordered list) for \p key + template + internal_bucket_type& bucket( const Q& key ) + { + return m_Buckets[hash_value( key )]; + } + //@endcond }; }} // namespace cds::intrusive diff --git a/cds/intrusive/michael_set_nogc.h b/cds/intrusive/michael_set_nogc.h index 88998455..51ea022c 100644 --- a/cds/intrusive/michael_set_nogc.h +++ b/cds/intrusive/michael_set_nogc.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_NOGC_H @@ -33,7 +33,6 @@ #include #include -#include namespace cds { namespace intrusive { @@ -59,29 +58,43 @@ namespace cds { namespace intrusive { class MichaelHashSet< cds::gc::nogc, OrderedList, Traits > { public: - typedef cds::gc::nogc gc; ///< Garbage collector - typedef OrderedList bucket_type; ///< Type of ordered list to be used as buckets - typedef Traits traits; ///< Set traits + typedef cds::gc::nogc gc; ///< Garbage collector + typedef OrderedList ordered_list; ///< type of ordered list used as a bucket implementation + typedef Traits traits; ///< Set traits - typedef typename bucket_type::value_type value_type; ///< type of value to be stored in the set - typedef typename bucket_type::key_comparator key_comparator; ///< key comparing functor - typedef typename bucket_type::disposer disposer; ///< Node disposer functor + typedef typename ordered_list::value_type value_type; ///< type of value to be stored in the set + typedef typename ordered_list::key_comparator key_comparator; ///< key comparing functor + typedef typename ordered_list::disposer disposer; ///< Node disposer functor + typedef typename ordered_list::stat stat; ///< Internal statistics /// Hash functor for \p value_type and all its derivatives that you use typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash; typedef typename traits::item_counter item_counter; ///< Item counter type + typedef typename traits::allocator allocator; ///< Bucket table allocator - /// Bucket table allocator - typedef cds::details::Allocator< bucket_type, typename traits::allocator > bucket_table_allocator; + // GC and OrderedList::gc must be the same + static_assert(std::is_same::value, "GC and OrderedList::gc must be the same"); - protected: - item_counter m_ItemCounter; ///< Item counter - hash m_HashFunctor; ///< Hash functor - bucket_type * m_Buckets; ///< bucket table + // atomicity::empty_item_counter is not allowed as a item counter + static_assert(!std::is_same::value, + "atomicity::empty_item_counter is not allowed as a item counter"); - private: + protected: //@cond - const size_t m_nHashBitmask; + typedef typename ordered_list::template select_stat_wrapper< typename ordered_list::stat > bucket_stat; + + typedef typename ordered_list::template rebind_traits< + cds::opt::item_counter< cds::atomicity::empty_item_counter > + , cds::opt::stat< typename bucket_stat::wrapped_stat > + >::type internal_bucket_type; + + typedef typename allocator::template rebind< internal_bucket_type >::other bucket_table_allocator; + + hash m_HashFunctor; ///< Hash functor + const size_t m_nHashBitmask; + internal_bucket_type * m_Buckets; ///< bucket table + item_counter m_ItemCounter; ///< Item counter + typename bucket_stat::stat m_Stat; ///< Internal statistics //@endcond protected: @@ -95,7 +108,7 @@ namespace cds { namespace intrusive { /// Returns the bucket (ordered list) for \p key template - bucket_type& bucket( Q const & key ) + internal_bucket_type& bucket( Q const & key ) { return m_Buckets[ hash_value( key ) ]; } @@ -138,13 +151,13 @@ namespace cds { namespace intrusive { }; \endcode */ - typedef michael_set::details::iterator< bucket_type, false > iterator; + typedef michael_set::details::iterator< internal_bucket_type, false > iterator; /// Const forward iterator /** For iterator's features and requirements see \ref iterator */ - typedef michael_set::details::iterator< bucket_type, true > const_iterator; + typedef michael_set::details::iterator< internal_bucket_type, true > const_iterator; /// Returns a forward iterator addressing the first element in a set /** @@ -202,22 +215,20 @@ namespace cds { namespace intrusive { size_t nMaxItemCount, ///< estimation of max item count in the hash set size_t nLoadFactor ///< load factor: estimation of max number of items in the bucket ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor )) + , m_Buckets( bucket_table_allocator().allocate( bucket_count() ) ) { - // GC and OrderedList::gc must be the same - static_assert( std::is_same::value, "GC and OrderedList::gc must be the same"); - - // atomicity::empty_item_counter is not allowed as a item counter - static_assert( !std::is_same::value, - "atomicity::empty_item_counter is not allowed as a item counter"); - - m_Buckets = bucket_table_allocator().NewArray( bucket_count() ); + for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it ) + construct_bucket( it ); } /// Clears hash set object and destroys it ~MichaelHashSet() { clear(); - bucket_table_allocator().Delete( m_Buckets, bucket_count() ); + + for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it ) + it->~internal_bucket_type(); + bucket_table_allocator().deallocate( m_Buckets, bucket_count() ); } /// Inserts new node @@ -425,6 +436,26 @@ namespace cds { namespace intrusive { return m_nHashBitmask + 1; } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + + private: + //@cond + template + typename std::enable_if< Stat::empty >::type construct_bucket( internal_bucket_type * bucket ) + { + new (bucket) internal_bucket_type; + } + + template + typename std::enable_if< !Stat::empty >::type construct_bucket( internal_bucket_type * bucket ) + { + new (bucket) internal_bucket_type( m_Stat ); + } + //@endcond }; }} // namespace cds::intrusive diff --git a/cds/intrusive/michael_set_rcu.h b/cds/intrusive/michael_set_rcu.h index 2a5df076..75e09217 100644 --- a/cds/intrusive/michael_set_rcu.h +++ b/cds/intrusive/michael_set_rcu.h @@ -25,14 +25,13 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_RCU_H #define CDSLIB_INTRUSIVE_MICHAEL_SET_RCU_H #include -#include namespace cds { namespace intrusive { @@ -102,57 +101,54 @@ namespace cds { namespace intrusive { class MichaelHashSet< cds::urcu::gc< RCU >, OrderedList, Traits > { public: - typedef cds::urcu::gc< RCU > gc; ///< RCU schema - typedef OrderedList bucket_type; ///< type of ordered list used as a bucket implementation - typedef Traits traits; ///< Set traits + typedef cds::urcu::gc< RCU > gc; ///< RCU schema + typedef OrderedList ordered_list; ///< type of ordered list used as a bucket implementation + typedef Traits traits; ///< Set traits - typedef typename bucket_type::value_type value_type ; ///< type of value stored in the list - typedef typename bucket_type::key_comparator key_comparator ; ///< key comparing functor - typedef typename bucket_type::disposer disposer ; ///< Node disposer functor + typedef typename ordered_list::value_type value_type; ///< type of value stored in the list + typedef typename ordered_list::key_comparator key_comparator; ///< key comparing functor + typedef typename ordered_list::disposer disposer; ///< Node disposer functor + typedef typename ordered_list::stat stat; ///< Internal statistics /// Hash functor for \ref value_type and all its derivatives that you use typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash; typedef typename traits::item_counter item_counter; ///< Item counter type + typedef typename traits::allocator allocator; ///< Bucket table allocator - /// Bucket table allocator - typedef cds::details::Allocator< bucket_type, typename traits::allocator > bucket_table_allocator; - - typedef typename bucket_type::rcu_lock rcu_lock; ///< RCU scoped lock - typedef typename bucket_type::exempt_ptr exempt_ptr; ///< pointer to extracted node - typedef typename bucket_type::raw_ptr raw_ptr; ///< Return type of \p get() member function and its derivatives + typedef typename ordered_list::rcu_lock rcu_lock; ///< RCU scoped lock /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that - static CDS_CONSTEXPR const bool c_bExtractLockExternal = bucket_type::c_bExtractLockExternal; + static CDS_CONSTEXPR const bool c_bExtractLockExternal = ordered_list::c_bExtractLockExternal; - protected: - item_counter m_ItemCounter; ///< Item counter - hash m_HashFunctor; ///< Hash functor - bucket_type * m_Buckets; ///< bucket table + // GC and OrderedList::gc must be the same + static_assert(std::is_same::value, "GC and OrderedList::gc must be the same"); - private: - //@cond - const size_t m_nHashBitmask; - //@endcond + // atomicity::empty_item_counter is not allowed as a item counter + static_assert(!std::is_same::value, + "atomicity::empty_item_counter is not allowed as a item counter"); protected: //@cond - /// Calculates hash value of \p key - template - size_t hash_value( Q const& key ) const - { - return m_HashFunctor( key ) & m_nHashBitmask; - } + typedef typename ordered_list::template select_stat_wrapper< typename ordered_list::stat > bucket_stat; - /// Returns the bucket (ordered list) for \p key - template - bucket_type& bucket( Q const& key ) - { - return m_Buckets[ hash_value( key ) ]; - } - template - bucket_type const& bucket( Q const& key ) const - { - return m_Buckets[ hash_value( key ) ]; - } + typedef typename ordered_list::template rebind_traits< + cds::opt::item_counter< cds::atomicity::empty_item_counter > + , cds::opt::stat< typename bucket_stat::wrapped_stat > + >::type internal_bucket_type; + + typedef typename allocator::template rebind< internal_bucket_type >::other bucket_table_allocator; + //@endcond + + public: + typedef typename internal_bucket_type::exempt_ptr exempt_ptr; ///< pointer to extracted node + typedef typename internal_bucket_type::raw_ptr raw_ptr; ///< Return type of \p get() member function and its derivatives + + private: + //@cond + hash m_HashFunctor; ///< Hash functor + size_t const m_nHashBitmask; + internal_bucket_type* m_Buckets; ///< bucket table + item_counter m_ItemCounter; ///< Item counter + typename bucket_stat::stat m_Stat; ///< Internal statistics //@endcond public: @@ -195,13 +191,13 @@ namespace cds { namespace intrusive { }; \endcode */ - typedef michael_set::details::iterator< bucket_type, false > iterator; + typedef michael_set::details::iterator< internal_bucket_type, false > iterator; /// Const forward iterator /** For iterator's features and requirements see \ref iterator */ - typedef michael_set::details::iterator< bucket_type, true > const_iterator; + typedef michael_set::details::iterator< internal_bucket_type, true > const_iterator; /// Returns a forward iterator addressing the first element in a set /** @@ -261,22 +257,20 @@ namespace cds { namespace intrusive { size_t nMaxItemCount, ///< estimation of max item count in the hash set size_t nLoadFactor ///< load factor: average size of the bucket ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor )) + , m_Buckets( bucket_table_allocator().allocate( bucket_count() ) ) { - // GC and OrderedList::gc must be the same - static_assert( std::is_same::value, "GC and OrderedList::gc must be the same"); - - // atomicity::empty_item_counter is not allowed as a item counter - static_assert( !std::is_same::value, - "atomicity::empty_item_counter is not allowed as a item counter"); - - m_Buckets = bucket_table_allocator().NewArray( bucket_count() ); + for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it ) + construct_bucket( it ); } /// Clear hash set and destroy it ~MichaelHashSet() { clear(); - bucket_table_allocator().Delete( m_Buckets, bucket_count() ); + + for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it ) + it->~internal_bucket_type(); + bucket_table_allocator().deallocate( m_Buckets, bucket_count() ); } /// Inserts new node @@ -470,7 +464,7 @@ namespace cds { namespace intrusive { unlinks it from the set, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the item found. If the item with the key equal to \p key is not found the function returns an empty \p exempt_ptr. - Depends on \p bucket_type you should or should not lock RCU before calling of this function: + Depends on \p ordered_list you should or should not lock RCU before calling of this function: - for the set based on \ref cds_intrusive_MichaelList_rcu "MichaelList" RCU should not be locked - for the set based on \ref cds_intrusive_LazyList_rcu "LazyList" RCU should be locked @@ -633,7 +627,7 @@ namespace cds { namespace intrusive { /** \anchor cds_intrusive_MichaelHashSet_rcu_get The function searches the item with key equal to \p key and returns the pointer to item found. If \p key is not found it returns \p nullptr. - Note the type of returned value depends on underlying \p bucket_type. + Note the type of returned value depends on underlying \p ordered_list. For details, see documentation of ordered list you use. Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type. @@ -726,9 +720,47 @@ namespace cds { namespace intrusive { return m_nHashBitmask + 1; } + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + + private: + //@cond + template + typename std::enable_if< Stat::empty >::type construct_bucket( internal_bucket_type * bucket ) + { + new (bucket) internal_bucket_type; + } + + template + typename std::enable_if< !Stat::empty >::type construct_bucket( internal_bucket_type * bucket ) + { + new (bucket) internal_bucket_type( m_Stat ); + } + + /// Calculates hash value of \p key + template + size_t hash_value( Q const& key ) const + { + return m_HashFunctor( key ) & m_nHashBitmask; + } + + /// Returns the bucket (ordered list) for \p key + template + internal_bucket_type& bucket( Q const& key ) + { + return m_Buckets[hash_value( key )]; + } + template + internal_bucket_type const& bucket( Q const& key ) const + { + return m_Buckets[hash_value( key )]; + } + //@endcond }; }} // namespace cds::intrusive #endif // #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_NOGC_H - diff --git a/cds/intrusive/mspriority_queue.h b/cds/intrusive/mspriority_queue.h index f0e04dc1..fb920a8c 100644 --- a/cds/intrusive/mspriority_queue.h +++ b/cds/intrusive/mspriority_queue.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_INTRUSIVE_MSPRIORITY_QUEUE_H @@ -53,12 +53,15 @@ namespace cds { namespace intrusive { struct stat { typedef Counter event_counter ; ///< Event counter type - event_counter m_nPushCount ; ///< Count of success push operation - event_counter m_nPopCount ; ///< Count of success pop operation - event_counter m_nPushFailCount ; ///< Count of failed ("the queue is full") push operation - event_counter m_nPopFailCount ; ///< Count of failed ("the queue is empty") pop operation - event_counter m_nPushHeapifySwapCount ; ///< Count of item swapping when heapifying in push - event_counter m_nPopHeapifySwapCount ; ///< Count of item swapping when heapifying in pop + event_counter m_nPushCount; ///< Count of success push operation + event_counter m_nPopCount; ///< Count of success pop operation + event_counter m_nPushFailCount; ///< Count of failed ("the queue is full") push operation + event_counter m_nPopFailCount; ///< Count of failed ("the queue is empty") pop operation + event_counter m_nPushHeapifySwapCount; ///< Count of item swapping when heapifying in push + event_counter m_nPopHeapifySwapCount; ///< Count of item swapping when heapifying in pop + event_counter m_nItemMovedTop; ///< Count of events when \p push() encountered that inserted item was moved to top by a concurrent \p pop() + event_counter m_nItemMovedUp; ///< Count of events when \p push() encountered that inserted item was moved upwards by a concurrent \p pop() + event_counter m_nPushEmptyPass; ///< Count of empty pass during heapify via concurrent operations //@cond void onPushSuccess() { ++m_nPushCount ;} @@ -67,21 +70,60 @@ namespace cds { namespace intrusive { void onPopFailed() { ++m_nPopFailCount ;} void onPushHeapifySwap() { ++m_nPushHeapifySwapCount ;} void onPopHeapifySwap() { ++m_nPopHeapifySwapCount ;} + + void onItemMovedTop() { ++m_nItemMovedTop ;} + void onItemMovedUp() { ++m_nItemMovedUp ;} + void onPushEmptyPass() { ++m_nPushEmptyPass ;} //@endcond }; /// MSPriorityQueue empty statistics struct empty_stat { //@cond - void onPushSuccess() {} - void onPopSuccess() {} - void onPushFailed() {} - void onPopFailed() {} - void onPushHeapifySwap() {} - void onPopHeapifySwap() {} + void onPushSuccess() const {} + void onPopSuccess() const {} + void onPushFailed() const {} + void onPopFailed() const {} + void onPushHeapifySwap() const {} + void onPopHeapifySwap() const {} + + void onItemMovedTop() const {} + void onItemMovedUp() const {} + void onPushEmptyPass() const {} //@endcond }; + /// Monotonic item counter, see \p traits::item_counter for explanation + class monotonic_counter + { + //@cond + public: + typedef size_t counter_type; + + monotonic_counter() + : m_nCounter(0) + {} + + size_t inc() + { + return ++m_nCounter; + } + + size_t dec() + { + return m_nCounter--; + } + + size_t value() const + { + return m_nCounter; + } + + private: + size_t m_nCounter; + //@endcond + }; + /// MSPriorityQueue traits struct traits { /// Storage type @@ -106,7 +148,7 @@ namespace cds { namespace intrusive { */ typedef opt::none less; - /// Type of mutual-exclusion lock + /// Type of mutual-exclusion lock. The lock is not need to be recursive. typedef cds::sync::spin lock_type; /// Back-off strategy @@ -118,6 +160,20 @@ namespace cds { namespace intrusive { or any other with interface like \p %mspriority_queue::stat */ typedef empty_stat stat; + + /// Item counter type + /** + Two type are possible: + - \p cds::bitop::bit_reverse_counter - a counter described in original paper, + which was developed for reducing lock contention. However, bit-reversing technigue requires more memory than classic heapifying algorithm + because of sparsing of elements: for priority queue of max size \p N the bit-reversing technique requires array size up to 2K + where \p K - the nearest power of two such that 2K >= N. + - \p mspriority_queue::monotonic_counter - a classic monotonic item counter. This counter can lead to false sharing under high contention. + By the other hand, for priority queue of max size \p N it requires \p N array size. + + By default, \p MSPriorityQueue uses \p %cds::bitop::bit_reverse_counter as described in original paper. + */ + typedef cds::bitop::bit_reverse_counter<> item_counter; }; /// Metafunction converting option list to traits @@ -133,6 +189,8 @@ namespace cds { namespace intrusive { - \p opt::lock_type - lock type. Default is \p cds::sync::spin - \p opt::back_off - back-off strategy. Default is \p cds::backoff::yield - \p opt::stat - internal statistics. Available types: \p mspriority_queue::stat, \p mspriority_queue::empty_stat (the default, no overhead) + - \p opt::item_counter - an item counter type for \p MSPriorityQueue. + Available type: \p cds::bitop::bit_reverse_counter, \p mspriority_queue::monotonic_counter. See \p traits::item_counter for details. */ template struct make_traits { @@ -187,9 +245,10 @@ namespace cds { namespace intrusive { typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator; # endif - typedef typename traits::lock_type lock_type; ///< heap's size lock type - typedef typename traits::back_off back_off; ///< Back-off strategy - typedef typename traits::stat stat; ///< internal statistics type + typedef typename traits::lock_type lock_type; ///< heap's size lock type + typedef typename traits::back_off back_off; ///< Back-off strategy + typedef typename traits::stat stat; ///< internal statistics type, see \p mspriority_queue::traits::stat + typedef typename traits::item_counter item_counter;///< Item counter type, see \p mspriority_queue::traits::item_counter protected: //@cond @@ -232,12 +291,11 @@ namespace cds { namespace intrusive { typedef typename traits::buffer::template rebind::other buffer_type ; ///< Heap array buffer type //@cond - typedef cds::bitop::bit_reverse_counter<> item_counter_type; - typedef typename item_counter_type::counter_type counter_type; + typedef typename item_counter::counter_type counter_type; //@endcond protected: - item_counter_type m_ItemCounter ; ///< Item counter + item_counter m_ItemCounter ; ///< Item counter mutable lock_type m_Lock ; ///< Heap's size lock buffer_type m_Heap ; ///< Heap array stat m_Stat ; ///< internal statistics accumulator @@ -285,11 +343,13 @@ namespace cds { namespace intrusive { node& refNode = m_Heap[i]; refNode.lock(); m_Lock.unlock(); + assert( refNode.m_nTag == tag_type( Empty )); + assert( refNode.m_pVal == nullptr ); refNode.m_pVal = &val; refNode.m_nTag = curId; refNode.unlock(); - // Move item towards top of the heap while it has higher priority than parent + // Move item towards top of heap while it has a higher priority than its parent heapify_after_push( i, curId ); m_Stat.onPushSuccess(); @@ -303,6 +363,8 @@ namespace cds { namespace intrusive { */ value_type * pop() { + node& refTop = m_Heap[1]; + m_Lock.lock(); if ( m_ItemCounter.value() == 0 ) { // the heap is empty @@ -310,14 +372,22 @@ namespace cds { namespace intrusive { m_Stat.onPopFailed(); return nullptr; } - counter_type nBottom = m_ItemCounter.reversed_value(); - m_ItemCounter.dec(); - // Since m_Heap[0] is not used, capacity() returns m_Heap.capacity() - 1 - // Consequently, "<=" is here - assert( nBottom <= capacity() ); + counter_type nBottom = m_ItemCounter.dec(); + assert( nBottom < m_Heap.capacity() ); assert( nBottom > 0 ); - node& refBottom = m_Heap[ nBottom ]; + refTop.lock(); + if ( nBottom == 1 ) { + refTop.m_nTag = tag_type( Empty ); + value_type * pVal = refTop.m_pVal; + refTop.m_pVal = nullptr; + refTop.unlock(); + m_Lock.unlock(); + m_Stat.onPopSuccess(); + return pVal; + } + + node& refBottom = m_Heap[nBottom]; refBottom.lock(); m_Lock.unlock(); refBottom.m_nTag = tag_type(Empty); @@ -325,8 +395,6 @@ namespace cds { namespace intrusive { refBottom.m_pVal = nullptr; refBottom.unlock(); - node& refTop = m_Heap[ 1 ]; - refTop.lock(); if ( refTop.m_nTag == tag_type(Empty) ) { // nBottom == nTop refTop.unlock(); @@ -338,7 +406,7 @@ namespace cds { namespace intrusive { refTop.m_nTag = tag_type( Available ); // refTop will be unlocked inside heapify_after_pop - heapify_after_pop( 1, &refTop ); + heapify_after_pop( &refTop ); m_Stat.onPopSuccess(); return pVal; @@ -370,11 +438,9 @@ namespace cds { namespace intrusive { template void clear_with( Func f ) { - while ( !empty() ) { - value_type * pVal = pop(); - if ( pVal ) - f( *pVal ); - } + value_type * pVal; + while (( pVal = pop()) != nullptr ) + f( *pVal ); } /// Checks is the priority queue is empty @@ -393,8 +459,7 @@ namespace cds { namespace intrusive { size_t size() const { std::unique_lock l( m_Lock ); - size_t nSize = (size_t) m_ItemCounter.value(); - return nSize; + return static_cast( m_ItemCounter.value()); } /// Return capacity of the priority queue @@ -439,12 +504,18 @@ namespace cds { namespace intrusive { i = 0; } } - else if ( refParent.m_nTag == tag_type(Empty) ) + else if ( refParent.m_nTag == tag_type( Empty ) ) { + m_Stat.onItemMovedTop(); i = 0; - else if ( refItem.m_nTag != curId ) + } + else if ( refItem.m_nTag != curId ) { + m_Stat.onItemMovedUp(); i = nParent; - else + } + else { + m_Stat.onPushEmptyPass(); bProgress = false; + } refItem.unlock(); refParent.unlock(); @@ -464,37 +535,37 @@ namespace cds { namespace intrusive { } } - void heapify_after_pop( counter_type nParent, node * pParent ) + void heapify_after_pop( node * pParent ) { key_comparator cmp; + counter_type const nCapacity = m_Heap.capacity(); + + counter_type nParent = 1; + for ( counter_type nChild = nParent * 2; nChild < nCapacity; nChild *= 2 ) { + node* pChild = &m_Heap[ nChild ]; + pChild->lock(); - while ( nParent < m_Heap.capacity() / 2 ) { - counter_type nLeft = nParent * 2; - counter_type nRight = nLeft + 1; - node& refLeft = m_Heap[nLeft]; - node& refRight = m_Heap[nRight]; - refLeft.lock(); - refRight.lock(); - - counter_type nChild; - node * pChild; - if ( refLeft.m_nTag == tag_type(Empty) ) { - refRight.unlock(); - refLeft.unlock(); + if ( pChild->m_nTag == tag_type( Empty )) { + pChild->unlock(); break; } - else if ( refRight.m_nTag == tag_type(Empty) || cmp( *refLeft.m_pVal, *refRight.m_pVal ) > 0 ) { - refRight.unlock(); - nChild = nLeft; - pChild = &refLeft; - } - else { - refLeft.unlock(); - nChild = nRight; - pChild = &refRight; + + counter_type const nRight = nChild + 1; + if ( nRight < nCapacity ) { + node& refRight = m_Heap[nRight]; + refRight.lock(); + + if ( refRight.m_nTag != tag_type( Empty ) && cmp( *refRight.m_pVal, *pChild->m_pVal ) > 0 ) { + // get right child + pChild->unlock(); + nChild = nRight; + pChild = &refRight; + } + else + refRight.unlock(); } - // If child has higher priority that parent then swap + // If child has higher priority than parent then swap // Otherwise stop if ( cmp( *pChild->m_pVal, *pParent->m_pVal ) > 0 ) { std::swap( pParent->m_nTag, pChild->m_nTag ); diff --git a/cds/opt/buffer.h b/cds/opt/buffer.h index 76dc4cf1..c54d1d5d 100644 --- a/cds/opt/buffer.h +++ b/cds/opt/buffer.h @@ -286,17 +286,18 @@ namespace cds { namespace opt { class uninitialized_dynamic_buffer { public: - typedef T value_type; ///< Value type + typedef T value_type; ///< Value type + typedef Alloc allocator; ///< Allocator type; static CDS_CONSTEXPR const bool c_bExp2 = Exp2; ///< \p Exp2 flag /// Rebind buffer for other template parameters - template + template struct rebind { typedef uninitialized_dynamic_buffer other; ///< Rebinding result type }; //@cond - typedef typename Alloc::template rebind::other allocator_type; + typedef typename allocator::template rebind::other allocator_type; //@endcond private: @@ -387,17 +388,18 @@ namespace cds { namespace opt { class initialized_dynamic_buffer { public: - typedef T value_type; ///< Value type + typedef T value_type; ///< Value type + typedef Alloc allocator; ///< Allocator type static CDS_CONSTEXPR const bool c_bExp2 = Exp2; ///< \p Exp2 flag /// Rebind buffer for other template parameters - template + template struct rebind { typedef initialized_dynamic_buffer other; ///< Rebinding result type }; //@cond - typedef cds::details::Allocator allocator_type; + typedef cds::details::Allocator allocator_type; //@endcond private: diff --git a/cds/sync/spinlock.h b/cds/sync/spinlock.h index 67430901..13938317 100644 --- a/cds/sync/spinlock.h +++ b/cds/sync/spinlock.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSLIB_SYNC_SPINLOCK_H @@ -49,18 +49,18 @@ namespace cds { [1984] L. Rudolph, Z. Segall. Dynamic Decentralized Cache Schemes for MIMD Parallel Processors. No serialization performed - any of waiting threads may owns the spin-lock. - This spin-lock is NOT recursive: the thread owned the lock cannot call lock() method withod deadlock. - The method unlock() can call any thread + This spin-lock is NOT recursive: the thread owned the lock cannot call \p lock() method without deadlock. + The method \p unlock() can call any thread DEBUG version: The spinlock stores owner thead id. Assertion is raised when: - double lock attempt encountered by same thread (deadlock) - unlock by another thread - If spin-lock is locked the Backoff algorithm is called. Predefined backoff::LockDefault class yields current + If spin-lock is locked the \p Backoff algorithm is called. Predefined \p backoff::LockDefault class yields current thread and repeats lock attempts later Template parameters: - - @p Backoff backoff strategy. Used when spin lock is locked + - \p Backoff - backoff strategy. Used when spin lock is locked */ template class spin_lock @@ -140,7 +140,7 @@ namespace cds { return !bCurrent; } - /// Try to lock the object, repeat @p nTryCount times if failed + /// Try to lock the object, repeat \p nTryCount times if failed /** Returns \p true if locking is succeeded otherwise (if the spin is already locked) returns \p false @@ -193,8 +193,8 @@ namespace cds { Allows recursive calls: the owner thread may recursive enter to critical section guarded by the spin-lock. Template parameters: - - @p Integral one of integral atomic type: unsigned int, int, and others - - @p Backoff backoff strategy. Used when spin lock is locked + - \p Integral one of integral atomic type: unsigned int, \p int, and others + - \p Backoff backoff strategy. Used when spin lock is locked */ template class reentrant_spin_lock @@ -207,7 +207,7 @@ namespace cds { private: atomics::atomic m_spin ; ///< spin-lock atomic - thread_id m_OwnerId ; ///< Owner thread id. If spin-lock is not locked it usually equals to OS::c_NullThreadId + thread_id m_OwnerId ; ///< Owner thread id. If spin-lock is not locked it usually equals to \p OS::c_NullThreadId private: //@cond @@ -301,7 +301,7 @@ namespace cds { return !( m_spin.load( atomics::memory_order_relaxed ) == 0 || is_taken( cds::OS::get_current_thread_id() )); } - /// Try to lock the spin-lock (synonym for \ref try_lock) + /// Try to lock the spin-lock (synonym for \p try_lock()) bool try_lock() CDS_NOEXCEPT { thread_id tid = OS::get_current_thread_id(); @@ -337,7 +337,7 @@ namespace cds { } } - /// Unlock the spin-lock. Return @p true if the current thread is owner of spin-lock @p false otherwise + /// Unlock the spin-lock. Return \p true if the current thread is owner of spin-lock \p false otherwise bool unlock() CDS_NOEXCEPT { if ( is_taken( OS::get_current_thread_id() ) ) { diff --git a/change.log b/change.log index fbfb9922..9eb20b9f 100644 --- a/change.log +++ b/change.log @@ -20,6 +20,7 @@ are removed. - Fixed: use-after-free bug in VyukovMPMCCycleQueue internal buffer. To prevent this bug the queue uses an uninitialized buffer now. + - Fixed: rare priority inversion bug in MSPriorityQueue - Added: for minimizing runtime of stress test the detail level for some test is added. Command line argument --detail-level=N specifies what test should be ran: each test with level not great than N will be ran. Instead of command line arg diff --git a/projects/Win/vc14/cds.vcxproj b/projects/Win/vc14/cds.vcxproj index 9694c1e0..e994ff6e 100644 --- a/projects/Win/vc14/cds.vcxproj +++ b/projects/Win/vc14/cds.vcxproj @@ -509,6 +509,7 @@ + @@ -524,10 +525,13 @@ + + + diff --git a/projects/Win/vc14/cds.vcxproj.filters b/projects/Win/vc14/cds.vcxproj.filters index b9a9ef05..73584fa3 100644 --- a/projects/Win/vc14/cds.vcxproj.filters +++ b/projects/Win/vc14/cds.vcxproj.filters @@ -102,12 +102,6 @@ {7226715d-6777-4c01-8e66-83b3885c00c1} - - {84ca9e83-f6c9-4503-a45f-14f08317fd70} - - - {4b79fe31-4f6c-4e05-8910-1151a26d51f3} - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx @@ -118,12 +112,6 @@ {560b4d4c-71e1-443c-942e-dcc5a275c7c2} - - {6530b757-5bb7-4de0-b1c9-019acc8183ba} - - - {d3f68c37-8c36-448e-9d4c-cd89a940d275} - {32754dfc-727a-42ff-b243-9a8510bf5c4e} @@ -145,9 +133,6 @@ {00a14aa8-3035-4b56-bc86-442ca9bf8f44} - - {0a2328b4-ff6f-4afb-8de0-9884ae172fa9} - {3195cce2-1710-4b79-a1cf-6c7cea085fa3} @@ -157,6 +142,21 @@ {fe703227-44ad-4ad6-bae4-b6c9f5c65355} + + {84ca9e83-f6c9-4503-a45f-14f08317fd70} + + + {4b79fe31-4f6c-4e05-8910-1151a26d51f3} + + + {6530b757-5bb7-4de0-b1c9-019acc8183ba} + + + {d3f68c37-8c36-448e-9d4c-cd89a940d275} + + + {0a2328b4-ff6f-4afb-8de0-9884ae172fa9} + @@ -1244,5 +1244,17 @@ Header Files\cds\algo\flat_combining + + Header Files\cds\intrusive\details + + + Header Files\cds\intrusive\impl + + + Header Files\cds\intrusive + + + Header Files\cds\intrusive + \ No newline at end of file diff --git a/projects/Win/vc14/gtest-intrusive-set.vcxproj b/projects/Win/vc14/gtest-intrusive-set.vcxproj index 72ddedd1..912c132c 100644 --- a/projects/Win/vc14/gtest-intrusive-set.vcxproj +++ b/projects/Win/vc14/gtest-intrusive-set.vcxproj @@ -27,6 +27,8 @@ + + @@ -199,6 +201,8 @@ + + @@ -320,6 +324,8 @@ Disabled _ENABLE_ATOMIC_ALIGNMENT_FIX;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) + 4503 Console @@ -335,6 +341,8 @@ Disabled _ENABLE_ATOMIC_ALIGNMENT_FIX;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) + 4503 Console @@ -350,6 +358,8 @@ Disabled _ENABLE_ATOMIC_ALIGNMENT_FIX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) + 4503 Console @@ -365,6 +375,8 @@ Disabled _ENABLE_ATOMIC_ALIGNMENT_FIX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) + 4503 Console @@ -382,6 +394,8 @@ true _ENABLE_ATOMIC_ALIGNMENT_FIX;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) + 4503 Console @@ -401,6 +415,8 @@ true _ENABLE_ATOMIC_ALIGNMENT_FIX;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(SolutionDir)..\..\..;$(GTEST_ROOT)/include;$(SolutionDir)..\..\..\test\include;$(BOOST_PATH);%(AdditionalIncludeDirectories) + /bigobj %(AdditionalOptions) + 4503 Console diff --git a/projects/Win/vc14/gtest-intrusive-set.vcxproj.filters b/projects/Win/vc14/gtest-intrusive-set.vcxproj.filters index b1946eca..027cb084 100644 --- a/projects/Win/vc14/gtest-intrusive-set.vcxproj.filters +++ b/projects/Win/vc14/gtest-intrusive-set.vcxproj.filters @@ -171,6 +171,12 @@ Source Files\FeldmanHashSet + + Source Files\MichaelSet + + + Source Files\MichaelSet + @@ -212,5 +218,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/projects/Win/vc14/gtest-list.vcxproj b/projects/Win/vc14/gtest-list.vcxproj index 767fc125..bbd7b1c5 100644 --- a/projects/Win/vc14/gtest-list.vcxproj +++ b/projects/Win/vc14/gtest-list.vcxproj @@ -27,6 +27,8 @@ + + @@ -47,6 +49,8 @@ + + diff --git a/projects/Win/vc14/gtest-list.vcxproj.filters b/projects/Win/vc14/gtest-list.vcxproj.filters index 2fe3b715..2b38fd61 100644 --- a/projects/Win/vc14/gtest-list.vcxproj.filters +++ b/projects/Win/vc14/gtest-list.vcxproj.filters @@ -69,6 +69,12 @@ Header Files + + Header Files + + + Header Files + @@ -218,5 +224,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/test/stress/pqueue/pop.cpp b/test/stress/pqueue/pop.cpp index df1a55ee..e939d691 100644 --- a/test/stress/pqueue/pop.cpp +++ b/test/stress/pqueue/pop.cpp @@ -40,52 +40,6 @@ namespace { typedef cds_test::stress_fixture base_class; protected: - template - class Producer: public cds_test::thread - { - typedef cds_test::thread base_class; - - public: - Producer( cds_test::thread_pool& pool, PQueue& queue ) - : base_class( pool ) - , m_Queue( queue ) - {} - - Producer( Producer& src ) - : base_class( src ) - , m_Queue( src.m_Queue ) - {} - - virtual thread * clone() - { - return new Producer( *this ); - } - - virtual void test() - { - typedef typename PQueue::value_type value_type; - for ( array_type::const_iterator it = m_arr.begin(); it != m_arr.end(); ++it ) { - if ( !m_Queue.push( value_type( *it ) )) - ++m_nPushError; - } - } - - void prepare( size_t nStart, size_t nEnd ) - { - m_arr.reserve( nEnd - nStart ); - for ( size_t i = nStart; i < nEnd; ++i ) - m_arr.push_back( i ); - shuffle( m_arr.begin(), m_arr.end() ); - } - - public: - PQueue& m_Queue; - size_t m_nPushError = 0; - - typedef std::vector array_type; - array_type m_arr; - }; - template class Consumer: public cds_test::thread { @@ -116,18 +70,27 @@ namespace { ++m_nPopSuccess; nPrevKey = val.key; - while ( !m_Queue.empty() ) { - if ( m_Queue.pop( val )) { - ++m_nPopSuccess; - if ( val.key > nPrevKey ) - ++m_nPopError; - else if ( val.key == nPrevKey ) - ++m_nPopErrorEq; - nPrevKey = val.key; + bool prevPopFailed = false; + while ( m_Queue.pop( val )) { + ++m_nPopSuccess; + if ( val.key > nPrevKey ) { + ++m_nPopError; + m_arrFailedPops.emplace_back( failed_pops{ nPrevKey, val.key, static_cast(-1) } ); + prevPopFailed = true; + } + else if ( val.key == nPrevKey ) { + ++m_nPopErrorEq; + m_arrFailedPops.emplace_back( failed_pops{ nPrevKey, val.key, static_cast(-1) } ); } - else - ++m_nPopFailed; + else { + if ( prevPopFailed ) + m_arrFailedPops.back().next_key = val.key; + prevPopFailed = false; + } + if ( nPrevKey > val.key ) + nPrevKey = val.key; } + } else ++m_nPopFailed; @@ -139,6 +102,13 @@ namespace { size_t m_nPopErrorEq = 0; size_t m_nPopSuccess = 0; size_t m_nPopFailed = 0; + + struct failed_pops { + size_t prev_key; + size_t popped_key; + size_t next_key; + }; + std::vector< failed_pops > m_arrFailedPops; }; protected: @@ -146,31 +116,30 @@ namespace { template void test( PQueue& q ) { - size_t const nThreadItemCount = s_nQueueSize / s_nThreadCount; - s_nQueueSize = nThreadItemCount * s_nThreadCount; - cds_test::thread_pool& pool = get_pool(); - propout() << std::make_pair( "thread_count", s_nThreadCount ) - << std::make_pair( "push_count", s_nQueueSize ); - // push { - pool.add( new Producer( pool, q ), s_nThreadCount ); + std::vector< size_t > arr; + arr.reserve( s_nQueueSize ); + for ( size_t i = 0; i < s_nQueueSize; ++i ) + arr.push_back( i ); + shuffle( arr.begin(), arr.end() ); - size_t nStart = 0; - for ( size_t i = 0; i < pool.size(); ++i ) { - static_cast&>( pool.get(i) ).prepare( nStart, nStart + nThreadItemCount ); - nStart += nThreadItemCount; + size_t nPushError = 0; + typedef typename PQueue::value_type value_type; + for ( auto it = arr.begin(); it != arr.end(); ++it ) { + if ( !q.push( value_type( *it ) )) + ++nPushError; } - - std::chrono::milliseconds duration = pool.run(); - propout() << std::make_pair( "producer_duration", duration ); + s_nQueueSize -= nPushError; } + propout() << std::make_pair( "thread_count", s_nThreadCount ) + << std::make_pair( "push_count", s_nQueueSize ); + // pop { - pool.clear(); pool.add( new Consumer( pool, q ), s_nThreadCount ); std::chrono::milliseconds duration = pool.run(); @@ -188,6 +157,18 @@ namespace { nTotalError += cons.m_nPopError; nTotalErrorEq += cons.m_nPopErrorEq; nTotalFailed += cons.m_nPopFailed; + + if ( !cons.m_arrFailedPops.empty() ) { + std::cerr << "Priority violations, thread " << i; + for ( size_t k = 0; k < cons.m_arrFailedPops.size(); ++k ) { + std::cerr << "\n " << "prev_key=" << cons.m_arrFailedPops[k].prev_key << " popped_key=" << cons.m_arrFailedPops[k].popped_key; + if ( cons.m_arrFailedPops[k].next_key != static_cast(-1) ) + std::cerr << " next_key=" << cons.m_arrFailedPops[k].next_key; + else + std::cerr << " next_key unspecified"; + } + std::cerr << std::endl; + } } propout() @@ -196,8 +177,8 @@ namespace { << std::make_pair( "error_priority_violation", nTotalError ); EXPECT_EQ( nTotalPopped, s_nQueueSize ); - EXPECT_EQ( nTotalError, 0 ); - EXPECT_EQ( nTotalErrorEq, 0 ); + EXPECT_EQ( nTotalError, 0 ) << "priority violations"; + EXPECT_EQ( nTotalErrorEq, 0 ) << "double key"; } propout() << q.statistics(); @@ -224,11 +205,13 @@ namespace { TEST_F( fixture_t, pqueue_t ) \ { \ typedef pqueue::Types::pqueue_t pqueue_type; \ - pqueue_type pq( s_nQueueSize ); \ + pqueue_type pq( s_nQueueSize + 1 ); \ test( pq ); \ } - CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_less ) - CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_less_stat ) + CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_bitreverse_less ) + CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_bitreverse_less_stat ) + CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_monotonic_less ) + CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_monotonic_less_stat ) CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_cmp ) //CDSSTRESS_MSPriorityQueue( pqueue_pop, MSPriorityQueue_dyn_mutex ) // too slow diff --git a/test/stress/pqueue/pqueue_type.h b/test/stress/pqueue/pqueue_type.h index 1df733d1..83dee9da 100644 --- a/test/stress/pqueue/pqueue_type.h +++ b/test/stress/pqueue/pqueue_type.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSSTRESS_PQUEUE_TYPES_H @@ -392,20 +392,35 @@ namespace pqueue { {}; typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_static_mutex > MSPriorityQueue_static_mutex; - struct traits_MSPriorityQueue_dyn_less : public - cc::mspriority_queue::make_traits< - co::buffer< co::v::initialized_dynamic_buffer< char > > - >::type - {}; - typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_dyn_less > MSPriorityQueue_dyn_less; + struct traits_MSPriorityQueue_dyn: public cc::mspriority_queue::traits + { + typedef co::v::initialized_dynamic_buffer< char > buffer; + }; + + struct traits_MSPriorityQueue_dyn_bitreverse_less : public traits_MSPriorityQueue_dyn + { + typedef cds::bitop::bit_reverse_counter<> item_counter; + }; + typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_dyn_bitreverse_less > MSPriorityQueue_dyn_bitreverse_less; + + struct traits_MSPriorityQueue_dyn_bitreverse_less_stat: public traits_MSPriorityQueue_dyn_bitreverse_less + { + typedef cc::mspriority_queue::stat<> stat; + }; + typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_dyn_bitreverse_less_stat > MSPriorityQueue_dyn_bitreverse_less_stat; + + struct traits_MSPriorityQueue_dyn_monotonic_less: public traits_MSPriorityQueue_dyn + { + typedef cds::intrusive::mspriority_queue::monotonic_counter item_counter; + }; + typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_dyn_monotonic_less > MSPriorityQueue_dyn_monotonic_less; + + struct traits_MSPriorityQueue_dyn_monotonic_less_stat: public traits_MSPriorityQueue_dyn_monotonic_less + { + typedef cc::mspriority_queue::stat<> stat; + }; + typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_dyn_monotonic_less_stat > MSPriorityQueue_dyn_monotonic_less_stat; - struct traits_MSPriorityQueue_dyn_less_stat : public - cc::mspriority_queue::make_traits < - co::buffer< co::v::initialized_dynamic_buffer< char > > - , co::stat < cc::mspriority_queue::stat<> > - > ::type - {}; - typedef cc::MSPriorityQueue< Value, traits_MSPriorityQueue_dyn_less_stat > MSPriorityQueue_dyn_less_stat; struct traits_MSPriorityQueue_dyn_cmp : public cc::mspriority_queue::make_traits < @@ -640,7 +655,10 @@ namespace cds_test { << CDSSTRESS_STAT_OUT( s, m_nPushFailCount ) << CDSSTRESS_STAT_OUT( s, m_nPopFailCount ) << CDSSTRESS_STAT_OUT( s, m_nPushHeapifySwapCount ) - << CDSSTRESS_STAT_OUT( s, m_nPopHeapifySwapCount ); + << CDSSTRESS_STAT_OUT( s, m_nPopHeapifySwapCount ) + << CDSSTRESS_STAT_OUT( s, m_nItemMovedTop ) + << CDSSTRESS_STAT_OUT( s, m_nItemMovedUp ) + << CDSSTRESS_STAT_OUT( s, m_nPushEmptyPass ); } } // namespace cds_test diff --git a/test/stress/pqueue/push.cpp b/test/stress/pqueue/push.cpp index 990820a1..d66fe76e 100644 --- a/test/stress/pqueue/push.cpp +++ b/test/stress/pqueue/push.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "pqueue_type.h" @@ -169,8 +169,10 @@ namespace pqueue { pqueue_type pq( s_nQueueSize ); \ test( pq ); \ } - CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_less ) - CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_less_stat ) + CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_bitreverse_less ) + CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_bitreverse_less_stat ) + CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_monotonic_less ) + CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_monotonic_less_stat ) CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_cmp ) //CDSSTRESS_MSPriorityQueue( pqueue_push, MSPriorityQueue_dyn_mutex ) // too slow diff --git a/test/stress/pqueue/push_pop.cpp b/test/stress/pqueue/push_pop.cpp index a6828351..b529c69d 100644 --- a/test/stress/pqueue/push_pop.cpp +++ b/test/stress/pqueue/push_pop.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "pqueue_type.h" @@ -223,10 +223,12 @@ namespace { pqueue_type pq( s_nQueueSize ); \ test( pq ); \ } - CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_less ) - CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_less_stat ) + CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_bitreverse_less ) + CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_bitreverse_less_stat ) + CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_monotonic_less ) + CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_monotonic_less_stat ) CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_cmp ) - //CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_mutex ) too slow + //CDSSTRESS_MSPriorityQueue( pqueue_push_pop, MSPriorityQueue_dyn_mutex ) // too slow #define CDSSTRESS_MSPriorityQueue_static( fixture_t, pqueue_t ) \ TEST_F( fixture_t, pqueue_t ) \ diff --git a/test/unit/intrusive-set/CMakeLists.txt b/test/unit/intrusive-set/CMakeLists.txt index a05cce67..f8c0094c 100644 --- a/test/unit/intrusive-set/CMakeLists.txt +++ b/test/unit/intrusive-set/CMakeLists.txt @@ -11,6 +11,8 @@ set(CDSGTEST_SET_SOURCES intrusive_feldman_hashset_rcu_gpt.cpp intrusive_feldman_hashset_rcu_shb.cpp intrusive_feldman_hashset_rcu_sht.cpp + intrusive_michael_iterable_dhp.cpp + intrusive_michael_iterable_hp.cpp intrusive_michael_lazy_hp.cpp intrusive_michael_lazy_dhp.cpp intrusive_michael_lazy_nogc.cpp diff --git a/test/unit/intrusive-set/intrusive_michael_iterable_dhp.cpp b/test/unit/intrusive-set/intrusive_michael_iterable_dhp.cpp new file mode 100644 index 00000000..527a2f03 --- /dev/null +++ b/test/unit/intrusive-set/intrusive_michael_iterable_dhp.cpp @@ -0,0 +1,168 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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_michael_iterable_hp.h" + +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::DHP gc_type; + + class IntrusiveMichaelIterableSet_DHP : public cds_test::intrusive_set_hp + { + protected: + typedef cds_test::intrusive_set_hp base_class; + + protected: + void SetUp() + { + struct list_traits : public ci::iterable_list::traits + {}; + typedef ci::IterableList< gc_type, item_type, list_traits > list_type; + typedef ci::MichaelHashSet< gc_type, list_type > set_type; + + cds::gc::dhp::GarbageCollector::Construct( 16, set_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::GarbageCollector::Destruct(); + } + }; + + + TEST_F( IntrusiveMichaelIterableSet_DHP, cmp ) + { + typedef ci::IterableList< gc_type + , item_type + ,ci::iterable_list::make_traits< + ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > bucket_type; + + typedef ci::MichaelHashSet< gc_type, bucket_type, + ci::michael_set::make_traits< + ci::opt::hash< hash_int > + >::type + > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_DHP, less ) + { + typedef ci::IterableList< gc_type + , item_type + ,ci::iterable_list::make_traits< + ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > bucket_type; + + typedef ci::MichaelHashSet< gc_type, bucket_type, + ci::michael_set::make_traits< + ci::opt::hash< hash_int > + >::type + > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_DHP, cmpmix ) + { + struct list_traits : public ci::iterable_list::traits + { + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + }; + typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type; + + struct set_traits : public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_DHP, stat ) + { + struct list_traits: public ci::iterable_list::traits + { + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::iterable_list::stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_DHP, wrapped_stat ) + { + struct list_traits: public ci::iterable_list::traits + { + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::iterable_list::wrapped_stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + +} // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_iterable_hp.cpp b/test/unit/intrusive-set/intrusive_michael_iterable_hp.cpp new file mode 100644 index 00000000..98af775e --- /dev/null +++ b/test/unit/intrusive-set/intrusive_michael_iterable_hp.cpp @@ -0,0 +1,171 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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_michael_iterable_hp.h" + +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::HP gc_type; + + class IntrusiveMichaelIterableSet_HP : public cds_test::intrusive_set_hp + { + protected: + typedef cds_test::intrusive_set_hp base_class; + + protected: + void SetUp() + { + struct list_traits : public ci::iterable_list::traits + {}; + typedef ci::IterableList< gc_type, item_type, list_traits > list_type; + typedef ci::MichaelHashSet< gc_type, list_type > set_type; + + // +3 - for iterators + cds::gc::hp::GarbageCollector::Construct( set_type::c_nHazardPtrCount + 3, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + + TEST_F( IntrusiveMichaelIterableSet_HP, cmp ) + { + typedef ci::IterableList< gc_type + , item_type + ,ci::iterable_list::make_traits< + ci::opt::compare< cmp > + ,ci::opt::disposer< mock_disposer > + >::type + > bucket_type; + + typedef ci::MichaelHashSet< gc_type, bucket_type, + ci::michael_set::make_traits< + ci::opt::hash< hash_int > + >::type + > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_HP, less ) + { + typedef ci::IterableList< gc_type + , item_type + ,ci::iterable_list::make_traits< + ci::opt::less< less > + ,ci::opt::disposer< mock_disposer > + >::type + > bucket_type; + + typedef ci::MichaelHashSet< gc_type, bucket_type, + ci::michael_set::make_traits< + ci::opt::hash< hash_int > + >::type + > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_HP, cmpmix ) + { + struct list_traits : public ci::iterable_list::traits + { + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + }; + typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type; + + struct set_traits : public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_HP, stat ) + { + struct list_traits: public ci::iterable_list::traits + { + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::iterable_list::stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelIterableSet_HP, wrapped_stat ) + { + struct list_traits: public ci::iterable_list::traits + { + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::iterable_list::wrapped_stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + +} // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_lazy_dhp.cpp b/test/unit/intrusive-set/intrusive_michael_lazy_dhp.cpp index 633001a0..5931528a 100644 --- a/test/unit/intrusive-set/intrusive_michael_lazy_dhp.cpp +++ b/test/unit/intrusive-set/intrusive_michael_lazy_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_set_hp.h" @@ -158,6 +158,49 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelLazySet_DHP, base_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelLazySet_DHP, base_wrapped_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } TEST_F( IntrusiveMichaelLazySet_DHP, member_cmp ) { @@ -251,4 +294,48 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelLazySet_DHP, member_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelLazySet_DHP, member_wrapped_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + } // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_lazy_hp.cpp b/test/unit/intrusive-set/intrusive_michael_lazy_hp.cpp index 6583d32d..1379e832 100644 --- a/test/unit/intrusive-set/intrusive_michael_lazy_hp.cpp +++ b/test/unit/intrusive-set/intrusive_michael_lazy_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_set_hp.h" @@ -159,6 +159,50 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelLazySet_HP, base_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc, ci::opt::lock_type> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, base_mutex_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelLazySet_HP, base_wrapped_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + TEST_F( IntrusiveMichaelLazySet_HP, member_cmp ) { @@ -252,4 +296,48 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelLazySet_HP, member_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelLazySet_HP, member_wrapped_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + } // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_lazy_nogc.cpp b/test/unit/intrusive-set/intrusive_michael_lazy_nogc.cpp index c183de4f..0c282dd0 100644 --- a/test/unit/intrusive-set/intrusive_michael_lazy_nogc.cpp +++ b/test/unit/intrusive-set/intrusive_michael_lazy_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_set_nogc.h" @@ -145,6 +145,49 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelLazySet_NoGC, base_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelLazySet_NoGC, base_wrapped_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } TEST_F( IntrusiveMichaelLazySet_NoGC, member_cmp ) { @@ -238,4 +281,48 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelLazySet_NoGC, member_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelLazySet_NoGC, member_wrapped_stat ) + { + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + } // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_michael_dhp.cpp b/test/unit/intrusive-set/intrusive_michael_michael_dhp.cpp index 035c21f1..77e0d25e 100644 --- a/test/unit/intrusive-set/intrusive_michael_michael_dhp.cpp +++ b/test/unit/intrusive-set/intrusive_michael_michael_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_set_hp.h" @@ -131,6 +131,51 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelSet_DHP, base_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelSet_DHP, base_wrapped_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } TEST_F( IntrusiveMichaelSet_DHP, member_cmp ) { @@ -202,4 +247,50 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelSet_DHP, member_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelSet_DHP, member_wrapped_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + } // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_michael_hp.cpp b/test/unit/intrusive-set/intrusive_michael_michael_hp.cpp index a97824d3..92e000de 100644 --- a/test/unit/intrusive-set/intrusive_michael_michael_hp.cpp +++ b/test/unit/intrusive-set/intrusive_michael_michael_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_set_hp.h" @@ -133,6 +133,50 @@ namespace { } + TEST_F( IntrusiveMichaelSet_HP, base_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelSet_HP, base_wrapped_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + TEST_F( IntrusiveMichaelSet_HP, member_cmp ) { typedef ci::MichaelList< gc_type @@ -203,4 +247,50 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelSet_HP, member_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelSet_HP, member_wrapped_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef base_class::less less; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + } // namespace diff --git a/test/unit/intrusive-set/intrusive_michael_michael_nogc.cpp b/test/unit/intrusive-set/intrusive_michael_michael_nogc.cpp index 2ce9e2b2..02ab228c 100644 --- a/test/unit/intrusive-set/intrusive_michael_michael_nogc.cpp +++ b/test/unit/intrusive-set/intrusive_michael_michael_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_set_nogc.h" @@ -118,6 +118,49 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelSet_NoGC, base_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelSet_NoGC, base_wrapped_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef base_class::less less; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } TEST_F( IntrusiveMichaelSet_NoGC, member_cmp ) { @@ -189,4 +232,48 @@ namespace { test( s ); } + TEST_F( IntrusiveMichaelSet_NoGC, member_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + + TEST_F( IntrusiveMichaelSet_NoGC, member_wrapped_stat ) + { + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< gc_type, bucket_type, set_traits > set_type; + + set_type s( kSize, 2 ); + test( s ); + } + } // namespace diff --git a/test/unit/intrusive-set/test_intrusive_michael_iterable.h b/test/unit/intrusive-set/test_intrusive_michael_iterable.h new file mode 100644 index 00000000..e1507468 --- /dev/null +++ b/test/unit/intrusive-set/test_intrusive_michael_iterable.h @@ -0,0 +1,417 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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 CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_ITERABLE_H +#define CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_ITERABLE_H + +#include +#include + +#include +#include // ref + +// forward declaration +namespace cds { namespace intrusive {}} + +namespace cds_test { + + namespace ci = cds::intrusive; + namespace co = cds::opt; + + class intrusive_set: public fixture + { + public: + static size_t const kSize = 100; + + struct stat + { + unsigned int nDisposeCount ; // count of disposer calling + unsigned int nFindCount ; // count of find-functor calling + unsigned int nUpdateNewCount; + unsigned int nUpdateCount; + mutable unsigned int nEraseCount; + + stat() + { + clear_stat(); + } + + void clear_stat() + { + memset( this, 0, sizeof( *this ) ); + } + }; + + struct item_type: public stat + { + int nKey; + int nVal; + + item_type( int k ) + : nKey(k) + , nVal(0) + {} + + int key() const + { + return nKey; + } + }; + + struct hash_int { + size_t operator()( int i ) const + { + return co::v::hash()( i ); + } + template + size_t operator()( Q const& i ) const + { + return (*this)( i.key()); + } + }; + + struct simple_item_counter { + size_t m_nCount; + + simple_item_counter() + : m_nCount(0) + {} + + size_t operator ++() + { + return ++m_nCount; + } + + size_t operator --() + { + return --m_nCount; + } + + void reset() + { + m_nCount = 0; + } + + operator size_t() const + { + return m_nCount; + } + + }; + + + template + struct less + { + bool operator ()(const T& v1, const T& v2 ) const + { + return v1.key() < v2.key(); + } + + template + bool operator ()(const T& v1, const Q& v2 ) const + { + return v1.key() < v2; + } + + template + bool operator ()(const Q& v1, const T& v2 ) const + { + return v1 < v2.key(); + } + }; + + template + struct cmp { + int operator ()(const T& v1, const T& v2 ) const + { + if ( v1.key() < v2.key() ) + return -1; + return v1.key() > v2.key() ? 1 : 0; + } + + template + int operator ()(const T& v1, const Q& v2 ) const + { + if ( v1.key() < v2 ) + return -1; + return v1.key() > v2 ? 1 : 0; + } + + template + int operator ()(const Q& v1, const T& v2 ) const + { + if ( v1 < v2.key() ) + return -1; + return v1 > v2.key() ? 1 : 0; + } + }; + + struct other_item { + int nKey; + + explicit other_item( int k ) + : nKey( k ) + {} + + int key() const + { + return nKey; + } + }; + + struct other_less { + template + bool operator()( Q const& lhs, T const& rhs ) const + { + return lhs.key() < rhs.key(); + } + }; + + struct mock_disposer + { + template + void operator ()( T * p ) + { + ++p->nDisposeCount; + } + }; + + protected: + template + void test( Set& s ) + { + // Precondition: set is empty + // Postcondition: set is empty + + ASSERT_TRUE( s.empty() ); + ASSERT_CONTAINER_SIZE( s, 0 ); + size_t const nSetSize = kSize; + + typedef typename Set::value_type value_type; + + std::vector< value_type > data; + std::vector< size_t> indices; + data.reserve( kSize ); + indices.reserve( kSize ); + for ( size_t key = 0; key < kSize; ++key ) { + data.push_back( value_type( static_cast( key ))); + indices.push_back( key ); + } + shuffle( indices.begin(), indices.end() ); + + // insert/find + for ( auto idx : indices ) { + auto& i = data[ idx ]; + + ASSERT_FALSE( s.contains( i.nKey )); + ASSERT_FALSE( s.contains( i )); + ASSERT_FALSE( s.contains( other_item( i.key()), other_less())); + ASSERT_FALSE( s.find( i.nKey, []( value_type&, int ) {} )); + ASSERT_FALSE( s.find_with( other_item( i.key()), other_less(), []( value_type&, other_item const& ) {} )); + ASSERT_TRUE( s.find( i.nKey ) == s.end()); + ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less()) == s.end()); + + std::pair updResult; + + updResult = s.update( i, []( value_type&, value_type* ) + { + ASSERT_TRUE( false ); + }, false ); + EXPECT_FALSE( updResult.first ); + EXPECT_FALSE( updResult.second ); + + updResult = s.upsert( i, false ); + EXPECT_FALSE( updResult.first ); + EXPECT_FALSE( updResult.second ); + + switch ( i.key() % 4 ) { + case 0: + ASSERT_TRUE( s.insert( i )); + ASSERT_FALSE( s.insert( i )); + updResult = s.update( i, []( value_type& val, value_type* arg) + { + ASSERT_TRUE( arg != nullptr ); + EXPECT_EQ( val.key(), arg->key() ); + }, false ); + EXPECT_TRUE( updResult.first ); + EXPECT_FALSE( updResult.second ); + break; + case 1: + EXPECT_EQ( i.nUpdateNewCount, 0 ); + ASSERT_TRUE( s.insert( i, []( value_type& v ) { ++v.nUpdateNewCount;} )); + EXPECT_EQ( i.nUpdateNewCount, 1 ); + ASSERT_FALSE( s.insert( i, []( value_type& v ) { ++v.nUpdateNewCount;} ) ); + EXPECT_EQ( i.nUpdateNewCount, 1 ); + i.nUpdateNewCount = 0; + break; + case 2: + updResult = s.update( i, []( value_type& /*val*/, value_type* arg ) + { + EXPECT_TRUE( arg == nullptr ); + }); + EXPECT_TRUE( updResult.first ); + EXPECT_TRUE( updResult.second ); + break; + case 3: + updResult = s.upsert( i ); + EXPECT_TRUE( updResult.first ); + EXPECT_TRUE( updResult.second ); + break; + } + + ASSERT_TRUE( s.contains( i.nKey ) ); + ASSERT_TRUE( s.contains( i ) ); + ASSERT_TRUE( s.contains( other_item( i.key() ), other_less())); + EXPECT_EQ( i.nFindCount, 0 ); + ASSERT_TRUE( s.find( i.nKey, []( value_type& v, int ) { ++v.nFindCount; } )); + EXPECT_EQ( i.nFindCount, 1 ); + ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less(), []( value_type& v, other_item const& ) { ++v.nFindCount; } )); + EXPECT_EQ( i.nFindCount, 2 ); + ASSERT_TRUE( s.find( i.nKey ) != s.end() ); + ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less() ) != s.end() ); + EXPECT_EQ( s.find( i.nKey )->nKey, i.key() ); + EXPECT_EQ( s.find_with( other_item( i.key() ), other_less())->nKey, i.key() ); + } + ASSERT_FALSE( s.empty() ); + ASSERT_CONTAINER_SIZE( s, nSetSize ); + + std::for_each( data.begin(), data.end(), []( value_type& v ) { v.clear_stat(); }); + + // erase + shuffle( indices.begin(), indices.end() ); + for ( auto idx : indices ) { + auto& i = data[ idx ]; + + ASSERT_TRUE( s.contains( i.nKey ) ); + ASSERT_TRUE( s.contains( i ) ); + ASSERT_TRUE( s.contains( other_item( i.key() ), other_less() ) ); + EXPECT_EQ( i.nFindCount, 0 ); + ASSERT_TRUE( s.find( i.nKey, []( value_type& v, int ) { ++v.nFindCount; } ) ); + EXPECT_EQ( i.nFindCount, 1 ); + ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less(), []( value_type& v, other_item const& ) { ++v.nFindCount; } ) ); + EXPECT_EQ( i.nFindCount, 2 ); + ASSERT_TRUE( s.find( i.nKey ) != s.end() ); + ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less()) != s.end() ); + EXPECT_EQ( s.find( i.nKey )->nKey, i.key() ); + EXPECT_EQ( s.find_with( other_item( i.key() ), other_less())->nKey, i.key() ); + + + value_type v( i ); + switch ( i.key() % 6 ) { + case 0: + ASSERT_FALSE( s.unlink( v )); + ASSERT_TRUE( s.unlink( i )); + ASSERT_FALSE( s.unlink( i ) ); + break; + case 1: + ASSERT_TRUE( s.erase( i.key())); + ASSERT_FALSE( s.erase( i.key() ) ); + break; + case 2: + ASSERT_TRUE( s.erase( v )); + ASSERT_FALSE( s.erase( v ) ); + break; + case 3: + ASSERT_TRUE( s.erase_with( other_item( i.key()), other_less())); + ASSERT_FALSE( s.erase_with( other_item( i.key() ), other_less() ) ); + break; + case 4: + EXPECT_EQ( i.nEraseCount, 0 ); + ASSERT_TRUE( s.erase( v, []( value_type& val ) { ++val.nEraseCount; } )); + EXPECT_EQ( i.nEraseCount, 1 ); + ASSERT_FALSE( s.erase( v, []( value_type& val ) { ++val.nEraseCount; } )); + EXPECT_EQ( i.nEraseCount, 1 ); + break; + case 5: + EXPECT_EQ( i.nEraseCount, 0 ); + ASSERT_TRUE( s.erase_with( other_item( i.key() ), other_less(), []( value_type& val ) { ++val.nEraseCount; } )); + EXPECT_EQ( i.nEraseCount, 1 ); + ASSERT_FALSE( s.erase_with( other_item( i.key() ), other_less(), []( value_type& val ) { ++val.nEraseCount; } )); + EXPECT_EQ( i.nEraseCount, 1 ); + break; + } + + ASSERT_FALSE( s.contains( i.nKey )); + ASSERT_FALSE( s.contains( i )); + ASSERT_FALSE( s.contains( other_item( i.key()), other_less())); + ASSERT_FALSE( s.find( i.nKey, []( value_type&, int ) {} )); + ASSERT_FALSE( s.find_with( other_item( i.key()), other_less(), []( value_type&, other_item const& ) {} )); + ASSERT_TRUE( s.find( i.nKey ) == s.end() ); + ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less() ) == s.end() ); + } + ASSERT_TRUE( s.empty() ); + ASSERT_CONTAINER_SIZE( s, 0 ); + + // Force retiring cycle + Set::gc::force_dispose(); + for ( auto& i : data ) { + EXPECT_EQ( i.nDisposeCount, 1 ); + } + + // clear + for ( auto& i : data ) { + i.clear_stat(); + ASSERT_TRUE( s.insert( i )); + } + ASSERT_FALSE( s.empty() ); + ASSERT_CONTAINER_SIZE( s, nSetSize ); + + // Iterator test + for ( auto it = s.begin(); it != s.end(); ++it ) { + ++it->nFindCount; + } + for ( auto it = s.cbegin(); it != s.cend(); ++it ) { + EXPECT_EQ( it->nFindCount, 1 ); + } + for ( auto& i : data ) { + EXPECT_EQ( i.nFindCount, 1 ); + } + + // clear test + s.clear(); + + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + ASSERT_TRUE( s.begin() == s.end() ); + ASSERT_TRUE( s.cbegin() == s.cend() ); + + // Force retiring cycle + Set::gc::force_dispose(); + for ( auto& i : data ) { + EXPECT_EQ( i.nDisposeCount, 1 ); + } + + } + }; + +} // namespace cds_test + +#endif // #ifndef CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_ITERABLE_H diff --git a/test/unit/intrusive-set/test_intrusive_michael_iterable_hp.h b/test/unit/intrusive-set/test_intrusive_michael_iterable_hp.h new file mode 100644 index 00000000..65621cf0 --- /dev/null +++ b/test/unit/intrusive-set/test_intrusive_michael_iterable_hp.h @@ -0,0 +1,160 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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 CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_ITERABLE_HP_H +#define CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_ITERABLE_HP_H + +#include "test_intrusive_michael_iterable.h" + +// forward declaration +namespace cds { namespace intrusive {}} + +namespace cds_test { + + namespace ci = cds::intrusive; + namespace co = cds::opt; + + class intrusive_set_hp: public intrusive_set + { + typedef intrusive_set base_class; + + protected: + + template + void test( Set& s ) + { + // Precondition: set is empty + // Postcondition: set is empty + + base_class::test( s ); + + ASSERT_TRUE( s.empty() ); + ASSERT_CONTAINER_SIZE( s, 0 ); + + typedef typename Set::value_type value_type; + + std::vector< value_type > data; + std::vector< size_t> indices; + data.reserve( kSize ); + indices.reserve( kSize ); + for ( size_t key = 0; key < kSize; ++key ) { + data.push_back( value_type( static_cast(key) ) ); + indices.push_back( key ); + } + shuffle( indices.begin(), indices.end() ); + + typename Set::guarded_ptr gp; + + // get/extract from empty set + for ( auto idx : indices ) { + auto& i = data[idx]; + + gp = s.get( i ); + ASSERT_TRUE( !gp ); + gp = s.get( i.key() ); + ASSERT_TRUE( !gp ); + gp = s.get_with( other_item( i.key()), other_less()); + ASSERT_TRUE( !gp ); + + gp = s.extract( i ); + ASSERT_TRUE( !gp ); + gp = s.extract( i.key()); + ASSERT_TRUE( !gp ); + gp = s.extract_with( other_item( i.key()), other_less()); + ASSERT_TRUE( !gp ); + } + + // fill set + for ( auto& i : data ) { + i.nDisposeCount = 0; + ASSERT_TRUE( s.insert( i ) ); + } + + // get/extract + for ( auto idx : indices ) { + auto& i = data[idx]; + + EXPECT_EQ( i.nFindCount, 0 ); + gp = s.get( i ); + ASSERT_FALSE( !gp ); + ++gp->nFindCount; + EXPECT_EQ( i.nFindCount, 1 ); + + gp = s.get( i.key() ); + ASSERT_FALSE( !gp ); + ++gp->nFindCount; + EXPECT_EQ( i.nFindCount, 2 ); + + gp = s.get_with( other_item( i.key()), other_less()); + ASSERT_FALSE( !gp ); + ++gp->nFindCount; + EXPECT_EQ( i.nFindCount, 3 ); + + EXPECT_EQ( i.nEraseCount, 0 ); + switch ( i.key() % 3 ) { + case 0: + gp = s.extract( i.key()); + break; + case 1: + gp = s.extract( i ); + break; + case 2: + gp = s.extract_with( other_item( i.key() ), other_less() ); + break; + } + ASSERT_FALSE( !gp ); + ++gp->nEraseCount; + EXPECT_EQ( i.nEraseCount, 1 ); + + gp = s.extract( i ); + ASSERT_TRUE( !gp ); + gp = s.extract( i.key() ); + ASSERT_TRUE( !gp ); + gp = s.extract_with( other_item( i.key() ), other_less() ); + ASSERT_TRUE( !gp ); + } + + gp.release(); + + ASSERT_TRUE( s.empty() ); + ASSERT_CONTAINER_SIZE( s, 0 ); + + // Force retiring cycle + Set::gc::force_dispose(); + for ( auto& i : data ) { + EXPECT_EQ( i.nDisposeCount, 1 ); + } + + } + }; + +} // namespace cds_test + +#endif // #ifndef CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_ITERABLE_HP_H diff --git a/test/unit/intrusive-set/test_intrusive_michael_lazy_rcu.h b/test/unit/intrusive-set/test_intrusive_michael_lazy_rcu.h index c32fdb65..13d9591a 100644 --- a/test/unit/intrusive-set/test_intrusive_michael_lazy_rcu.h +++ b/test/unit/intrusive-set/test_intrusive_michael_lazy_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_LAZY_RCU_H #define CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_LAZY_RCU_H @@ -171,6 +171,59 @@ TYPED_TEST_P( IntrusiveMichaelLazySet, base_mutex ) this->test( s ); } +TYPED_TEST_P( IntrusiveMichaelLazySet, base_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::base_item_type base_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef typename TestFixture::template less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< rcu_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} + +TYPED_TEST_P( IntrusiveMichaelLazySet, base_wrapped_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::base_item_type base_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::base_hook< ci::opt::gc> hook; + typedef typename TestFixture::template less less; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< rcu_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} TYPED_TEST_P( IntrusiveMichaelLazySet, member_cmp ) { @@ -286,11 +339,64 @@ TYPED_TEST_P( IntrusiveMichaelLazySet, member_mutex ) this->test( s ); } +TYPED_TEST_P( IntrusiveMichaelLazySet, member_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::member_item_type member_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef typename TestFixture::template cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::stat<> stat; + }; + typedef ci::LazyList< rcu_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} + +TYPED_TEST_P( IntrusiveMichaelLazySet, member_wrapped_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::member_item_type member_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::lazy_list::traits + { + typedef ci::lazy_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef typename TestFixture::template cmp compare; + typedef mock_disposer disposer; + typedef ci::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< rcu_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( IntrusiveMichaelLazySet, - base_cmp, base_less, base_cmpmix, base_mutex, member_cmp, member_less, member_cmpmix, member_mutex + base_cmp, base_less, base_cmpmix, base_mutex, base_stat, base_wrapped_stat, member_cmp, member_less, member_cmpmix, member_mutex, member_stat, member_wrapped_stat ); diff --git a/test/unit/intrusive-set/test_intrusive_michael_michael_rcu.h b/test/unit/intrusive-set/test_intrusive_michael_michael_rcu.h index 199fef5c..a8b02e21 100644 --- a/test/unit/intrusive-set/test_intrusive_michael_michael_rcu.h +++ b/test/unit/intrusive-set/test_intrusive_michael_michael_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_MICHAEL_RCU_H #define CDSUNIT_SET_TEST_INTRUSIVE_MICHAEL_MICHAEL_RCU_H @@ -142,6 +142,61 @@ TYPED_TEST_P( IntrusiveMichaelSet, base_cmpmix ) this->test( s ); } +TYPED_TEST_P( IntrusiveMichaelSet, base_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::base_item_type base_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef typename TestFixture::template less less; + typedef typename TestFixture::template cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< rcu_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} + +TYPED_TEST_P( IntrusiveMichaelSet, base_wrapped_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::base_item_type base_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::base_hook< ci::opt::gc> hook; + typedef typename TestFixture::template less less; + typedef typename TestFixture::template cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< rcu_type, base_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} TYPED_TEST_P( IntrusiveMichaelSet, member_cmp ) { @@ -230,12 +285,64 @@ TYPED_TEST_P( IntrusiveMichaelSet, member_cmpmix ) this->test( s ); } +TYPED_TEST_P( IntrusiveMichaelSet, member_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::member_item_type member_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef typename TestFixture::template cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::stat<> stat; + }; + typedef ci::MichaelList< rcu_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} +TYPED_TEST_P( IntrusiveMichaelSet, member_wrapped_stat ) +{ + typedef typename TestFixture::rcu_type rcu_type; + typedef typename TestFixture::member_item_type member_item_type; + typedef typename TestFixture::mock_disposer mock_disposer; + typedef typename TestFixture::hash_int hash_int; + + struct list_traits: public ci::michael_list::traits + { + typedef ci::michael_list::member_hook< offsetof( member_item_type, hMember ), ci::opt::gc> hook; + typedef typename TestFixture::template cmp compare; + typedef mock_disposer disposer; + typedef ci::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< rcu_type, member_item_type, list_traits > bucket_type; + + struct set_traits: public ci::michael_set::traits + { + typedef hash_int hash; + typedef typename TestFixture::simple_item_counter item_counter; + }; + typedef ci::MichaelHashSet< rcu_type, bucket_type, set_traits > set_type; + + set_type s( TestFixture::kSize, 2 ); + this->test( s ); +} // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( IntrusiveMichaelSet, - base_cmp, base_less, base_cmpmix, member_cmp, member_less, member_cmpmix + base_cmp, base_less, base_cmpmix, base_stat, base_wrapped_stat, member_cmp, member_less, member_cmpmix, member_stat, member_wrapped_stat ); diff --git a/test/unit/list/CMakeLists.txt b/test/unit/list/CMakeLists.txt index 8ecf2d4a..407dd510 100644 --- a/test/unit/list/CMakeLists.txt +++ b/test/unit/list/CMakeLists.txt @@ -2,6 +2,8 @@ set(PACKAGE_NAME unit-list) set(CDSGTEST_LIST_SOURCES ../main.cpp + intrusive_iterable_dhp.cpp + intrusive_iterable_hp.cpp intrusive_lazy_hp.cpp intrusive_lazy_dhp.cpp intrusive_lazy_nogc.cpp diff --git a/test/unit/list/intrusive_iterable_dhp.cpp b/test/unit/list/intrusive_iterable_dhp.cpp new file mode 100644 index 00000000..38d8c527 --- /dev/null +++ b/test/unit/list/intrusive_iterable_dhp.cpp @@ -0,0 +1,172 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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; + + class IntrusiveIterableList_DHP : public cds_test::intrusive_iterable_list_hp + { + protected: + void SetUp() + { + typedef ci::IterableList< gc_type, item_type > list_type; + + // +1 - for guarded_ptr + // +3 - for iterator test + cds::gc::dhp::GarbageCollector::Construct( 16, list_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::GarbageCollector::Destruct(); + } + }; + + TEST_F( IntrusiveIterableList_DHP, less ) + { + typedef ci::IterableList< gc_type, item_type, + typename ci::iterable_list::make_traits< + ci::opt::disposer< mock_disposer > + ,cds::opt::less< less< item_type >> + , cds::opt::item_counter< cds::atomicity::item_counter > + >::type + > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_DHP, compare ) + { + typedef ci::IterableList< gc_type, item_type, + typename ci::iterable_list::make_traits< + ci::opt::disposer< mock_disposer > + , cds::opt::compare< cmp< item_type >> + , cds::opt::item_counter< cds::atomicity::item_counter > + >::type + > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_DHP, item_counting ) + { + struct traits : public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef cmp< item_type > compare; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_DHP, backoff ) + { + struct traits : public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef cmp< item_type > compare; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::backoff::pause back_off; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_DHP, seqcst ) + { + struct traits : public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_DHP, stat ) + { + struct traits: public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::iterable_list::stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_DHP, wrapped_stat ) + { + struct traits: public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::iterable_list::wrapped_stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + traits::stat::stat_type st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + +} // namespace diff --git a/test/unit/list/intrusive_iterable_hp.cpp b/test/unit/list/intrusive_iterable_hp.cpp new file mode 100644 index 00000000..1e253580 --- /dev/null +++ b/test/unit/list/intrusive_iterable_hp.cpp @@ -0,0 +1,174 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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::HP gc_type; + + class IntrusiveIterableList_HP : public cds_test::intrusive_iterable_list_hp + { + protected: + void SetUp() + { + typedef ci::IterableList< gc_type, item_type > list_type; + + // +1 - for guarded_ptr + // +3 - for iterator test + cds::gc::hp::GarbageCollector::Construct( list_type::c_nHazardPtrCount + 3, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + TEST_F( IntrusiveIterableList_HP, less ) + { + typedef ci::IterableList< gc_type, item_type, + typename ci::iterable_list::make_traits< + ci::opt::disposer< mock_disposer > + ,cds::opt::less< less< item_type >> + ,cds::opt::item_counter< cds::atomicity::item_counter > + + >::type + > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_HP, compare ) + { + typedef ci::IterableList< gc_type, item_type, + typename ci::iterable_list::make_traits< + ci::opt::disposer< mock_disposer > + , cds::opt::compare< cmp< item_type >> + , cds::opt::item_counter< cds::atomicity::item_counter > + >::type + > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_HP, item_counting ) + { + struct traits : public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef cmp< item_type > compare; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_HP, backoff ) + { + struct traits : public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef cmp< item_type > compare; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::backoff::pause back_off; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_HP, seqcst ) + { + struct traits : public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_HP, stat ) + { + struct traits: public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::iterable_list::stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveIterableList_HP, wrapped_stat ) + { + struct traits: public ci::iterable_list::traits { + typedef mock_disposer disposer; + typedef intrusive_iterable_list::less< item_type > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::iterable_list::wrapped_stat<> stat; + }; + typedef ci::IterableList< gc_type, item_type, traits > list_type; + + traits::stat::stat_type st; + + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + +} // namespace diff --git a/test/unit/list/intrusive_lazy_dhp.cpp b/test/unit/list/intrusive_lazy_dhp.cpp index a443b005..73246179 100644 --- a/test/unit/list/intrusive_lazy_dhp.cpp +++ b/test/unit/list/intrusive_lazy_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_list_hp.h" @@ -60,7 +60,7 @@ namespace { void TearDown() { cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct(); + cds::gc::dhp::GarbageCollector::Destruct(); } }; @@ -166,6 +166,42 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveLazyList_DHP, base_hook_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef intrusive_list_common::less< base_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveLazyList_DHP, base_hook_wrapped_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef intrusive_list_common::less< base_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + TEST_F( IntrusiveLazyList_DHP, member_hook ) { typedef ci::LazyList< gc_type, member_item, @@ -269,4 +305,41 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveLazyList_DHP, member_hook_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_common::less< member_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveLazyList_DHP, member_hook_wrapped_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_common::less< member_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/intrusive_lazy_hp.cpp b/test/unit/list/intrusive_lazy_hp.cpp index bddb5d4c..bf3d49b5 100644 --- a/test/unit/list/intrusive_lazy_hp.cpp +++ b/test/unit/list/intrusive_lazy_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_list_hp.h" @@ -167,6 +167,41 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveLazyList_HP, base_hook_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef intrusive_list_common::less< base_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveLazyList_HP, base_hook_wrapped_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + TEST_F( IntrusiveLazyList_HP, member_hook ) { typedef ci::LazyList< gc_type, member_item, @@ -270,4 +305,39 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveLazyList_HP, member_hook_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_common::less< member_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveLazyList_HP, member_hook_wrapped_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/intrusive_lazy_nogc.cpp b/test/unit/list/intrusive_lazy_nogc.cpp index bd97556c..10039d1d 100644 --- a/test/unit/list/intrusive_lazy_nogc.cpp +++ b/test/unit/list/intrusive_lazy_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_list_nogc.h" @@ -141,6 +141,40 @@ namespace { test_ordered_iterator( l ); } + TEST_F( IntrusiveLazyList_NOGC, base_hook_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef intrusive_list_nogc::less< base_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + } + + TEST_F( IntrusiveLazyList_NOGC, base_hook_wrapped_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef intrusive_list_nogc::less< base_item > less; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, base_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + } + TEST_F( IntrusiveLazyList_NOGC, member_hook ) { typedef ci::LazyList< gc_type, member_item, @@ -238,4 +272,38 @@ namespace { test_ordered_iterator( l ); } + TEST_F( IntrusiveLazyList_NOGC, member_hook_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_nogc::less< member_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + } + + TEST_F( IntrusiveLazyList_NOGC, member_hook_wrapped_stat ) + { + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_nogc::less< member_item > less; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< gc_type, member_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + } + } // namespace diff --git a/test/unit/list/intrusive_michael_dhp.cpp b/test/unit/list/intrusive_michael_dhp.cpp index d6de7f05..cbc0d20e 100644 --- a/test/unit/list/intrusive_michael_dhp.cpp +++ b/test/unit/list/intrusive_michael_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_list_hp.h" @@ -57,7 +57,7 @@ namespace { void TearDown() { cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct(); + cds::gc::dhp::GarbageCollector::Destruct(); } }; @@ -145,6 +145,40 @@ namespace { test_ordered_iterator( l ); test_hp( l ); } + TEST_F( IntrusiveMichaelList_DHP, base_hook_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef intrusive_list_common::less< base_item > less; + typedef cds::intrusive::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveMichaelList_DHP, base_hook_wrapped_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef cds::intrusive::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item, traits > list_type; + + cds::intrusive::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + TEST_F( IntrusiveMichaelList_DHP, member_hook ) { @@ -231,4 +265,38 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveMichaelList_DHP, member_hook_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_common::less< member_item > less; + typedef cds::intrusive::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveMichaelList_DHP, member_hook_wrapped_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef cds::intrusive::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item, traits > list_type; + + cds::intrusive::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/intrusive_michael_hp.cpp b/test/unit/list/intrusive_michael_hp.cpp index 26b0bb53..693a0bfb 100644 --- a/test/unit/list/intrusive_michael_hp.cpp +++ b/test/unit/list/intrusive_michael_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_intrusive_list_hp.h" @@ -147,6 +147,40 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveMichaelList_HP, base_hook_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef intrusive_list_common::less< base_item > less; + typedef cds::intrusive::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveMichaelList_HP, base_hook_wrapped_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::base_hook< cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< base_item > compare; + typedef cds::intrusive::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, base_item, traits > list_type; + + cds::intrusive::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + TEST_F( IntrusiveMichaelList_HP, member_hook ) { typedef ci::MichaelList< gc_type, member_item, @@ -232,4 +266,38 @@ namespace { test_hp( l ); } + TEST_F( IntrusiveMichaelList_HP, member_hook_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef intrusive_list_common::less< member_item > less; + typedef cds::intrusive::michael_list::stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item, traits > list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( IntrusiveMichaelList_HP, member_hook_wrapped_stat ) + { + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::member_hook< offsetof( member_item, hMember ), cds::opt::gc< gc_type >> hook; + typedef mock_disposer disposer; + typedef cmp< member_item > compare; + typedef cds::intrusive::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< gc_type, member_item, traits > list_type; + + cds::intrusive::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/kv_lazy_dhp.cpp b/test/unit/list/kv_lazy_dhp.cpp index 9b242974..61564f41 100644 --- a/test/unit/list/kv_lazy_dhp.cpp +++ b/test/unit/list/kv_lazy_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_kv_list_hp.h" @@ -49,7 +49,7 @@ namespace { void TearDown() { cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct(); + cds::gc::dhp::GarbageCollector::Destruct(); } }; @@ -159,4 +159,37 @@ namespace { test_hp( l ); } + TEST_F( LazyKVList_DHP, stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyKVList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( LazyKVList_DHP, wrapped_stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyKVList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/kv_lazy_hp.cpp b/test/unit/list/kv_lazy_hp.cpp index fd5f0c85..9bbd838a 100644 --- a/test/unit/list/kv_lazy_hp.cpp +++ b/test/unit/list/kv_lazy_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_kv_list_hp.h" @@ -160,4 +160,37 @@ namespace { test_hp( l ); } + TEST_F( LazyKVList_HP, stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyKVList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( LazyKVList_HP, wrapped_stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyKVList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/kv_lazy_nogc.cpp b/test/unit/list/kv_lazy_nogc.cpp index db732cbb..32c32558 100644 --- a/test/unit/list/kv_lazy_nogc.cpp +++ b/test/unit/list/kv_lazy_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_kv_list_nogc.h" @@ -137,4 +137,35 @@ namespace { test_ordered_iterator( l ); } + TEST_F( LazyKVList_NOGC, stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyKVList list_type; + + list_type l; + test( l ); + test_ordered_iterator( l ); + } + + TEST_F( LazyKVList_NOGC, wrapped_stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyKVList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + test( l ); + test_ordered_iterator( l ); + } + } // namespace diff --git a/test/unit/list/kv_michael_dhp.cpp b/test/unit/list/kv_michael_dhp.cpp index f942b96c..7a41833f 100644 --- a/test/unit/list/kv_michael_dhp.cpp +++ b/test/unit/list/kv_michael_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_kv_list_hp.h" @@ -49,7 +49,7 @@ namespace { void TearDown() { cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct(); + cds::gc::dhp::GarbageCollector::Destruct(); } }; @@ -143,4 +143,37 @@ namespace { test_hp( l ); } + TEST_F( MichaelKVList_DHP, stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( MichaelKVList_DHP, wrapped_stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/kv_michael_hp.cpp b/test/unit/list/kv_michael_hp.cpp index baca4a35..2912dccc 100644 --- a/test/unit/list/kv_michael_hp.cpp +++ b/test/unit/list/kv_michael_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_kv_list_hp.h" @@ -144,4 +144,37 @@ namespace { test_hp( l ); } + TEST_F( MichaelKVList_HP, stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( MichaelKVList_HP, wrapped_stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/kv_michael_nogc.cpp b/test/unit/list/kv_michael_nogc.cpp index 083aa6d5..6f81d5fe 100644 --- a/test/unit/list/kv_michael_nogc.cpp +++ b/test/unit/list/kv_michael_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_kv_list_nogc.h" @@ -122,4 +122,35 @@ namespace { test_ordered_iterator( l ); } + TEST_F( MichaelKVList_NOGC, stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + list_type l; + test( l ); + test_ordered_iterator( l ); + } + + TEST_F( MichaelKVList_NOGC, wrapped_stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + test( l ); + test_ordered_iterator( l ); + } + } // namespace diff --git a/test/unit/list/lazy_dhp.cpp b/test/unit/list/lazy_dhp.cpp index da84906f..3ebdcfcd 100644 --- a/test/unit/list/lazy_dhp.cpp +++ b/test/unit/list/lazy_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_list_hp.h" @@ -49,7 +49,7 @@ namespace { void TearDown() { cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct(); + cds::gc::dhp::GarbageCollector::Destruct(); } }; @@ -159,4 +159,38 @@ namespace { test_hp( l ); } + TEST_F( LazyList_DHP, stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( LazyList_DHP, wrapped_stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + } // namespace diff --git a/test/unit/list/lazy_hp.cpp b/test/unit/list/lazy_hp.cpp index 9189906f..a0cbbb95 100644 --- a/test/unit/list/lazy_hp.cpp +++ b/test/unit/list/lazy_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_list_hp.h" @@ -160,4 +160,37 @@ namespace { test_hp( l ); } + TEST_F( LazyList_HP, stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( LazyList_HP, wrapped_stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/lazy_nogc.cpp b/test/unit/list/lazy_nogc.cpp index c1507445..5ec0b15d 100644 --- a/test/unit/list/lazy_nogc.cpp +++ b/test/unit/list/lazy_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_list_nogc.h" @@ -137,4 +137,35 @@ namespace { test_ordered_iterator( l ); } + TEST_F( LazyList_NOGC, stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyList list_type; + + list_type l; + test( l ); + test_ordered_iterator( l ); + } + + TEST_F( LazyList_NOGC, wrapped_stat ) + { + struct traits: public cc::lazy_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + test( l ); + test_ordered_iterator( l ); + } + } // namespace diff --git a/test/unit/list/michael_dhp.cpp b/test/unit/list/michael_dhp.cpp index 1ecabe2c..e2a16e51 100644 --- a/test/unit/list/michael_dhp.cpp +++ b/test/unit/list/michael_dhp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_list_hp.h" @@ -49,7 +49,7 @@ namespace { void TearDown() { cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct(); + cds::gc::dhp::GarbageCollector::Destruct(); } }; @@ -143,4 +143,39 @@ namespace { test_hp( l ); } + TEST_F( MichaelList_DHP, stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + + }; + typedef cc::MichaelList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( MichaelList_DHP, wrapped_stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + + }; + typedef cc::MichaelList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/michael_hp.cpp b/test/unit/list/michael_hp.cpp index 6a2261be..bb28f46b 100644 --- a/test/unit/list/michael_hp.cpp +++ b/test/unit/list/michael_hp.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_list_hp.h" @@ -144,4 +144,39 @@ namespace { test_hp( l ); } + TEST_F( MichaelList_HP, stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + + }; + typedef cc::MichaelList list_type; + + list_type l; + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + + TEST_F( MichaelList_HP, wrapped_stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + + }; + typedef cc::MichaelList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + test_common( l ); + test_ordered_iterator( l ); + test_hp( l ); + } + } // namespace diff --git a/test/unit/list/michael_nogc.cpp b/test/unit/list/michael_nogc.cpp index 88331cb9..4ad27ce0 100644 --- a/test/unit/list/michael_nogc.cpp +++ b/test/unit/list/michael_nogc.cpp @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "test_list_nogc.h" @@ -122,4 +122,35 @@ namespace { test_ordered_iterator( l ); } + TEST_F( MichaelList_NOGC, stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + }; + typedef cc::MichaelList list_type; + + list_type l; + test( l ); + test_ordered_iterator( l ); + } + + TEST_F( MichaelList_NOGC, wrapped_stat ) + { + struct traits: public cc::michael_list::traits + { + typedef lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + }; + typedef cc::MichaelList list_type; + + cds::container::michael_list::stat<> st; + list_type l{ st }; + test( l ); + test_ordered_iterator( l ); + } + } // namespace diff --git a/test/unit/list/test_intrusive_iterable_list.h b/test/unit/list/test_intrusive_iterable_list.h new file mode 100644 index 00000000..8416a4c4 --- /dev/null +++ b/test/unit/list/test_intrusive_iterable_list.h @@ -0,0 +1,551 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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 CDSUNIT_LIST_TEST_INTRUSIVE_ITERABLE_LIST_H +#define CDSUNIT_LIST_TEST_INTRUSIVE_ITERABLE_LIST_H + +#include +#include + +namespace cds_test { + + class intrusive_iterable_list : public fixture + { + public: + struct stat { + int nDisposeCount; + int nUpdateExistsCall; + int nUpdateNewCall; + int nFindCall; + int nEraseCall; + int nInsertCall; + + stat() + : nDisposeCount( 0 ) + , nUpdateExistsCall( 0 ) + , nUpdateNewCall( 0 ) + , nFindCall( 0 ) + , nEraseCall( 0 ) + , nInsertCall( 0 ) + {} + + stat( const stat& s ) + { + *this = s; + } + + stat& operator =( const stat& s ) + { + memcpy( this, &s, sizeof( s ) ); + return *this; + } + }; + + struct item_type + { + int nKey; + int nVal; + + mutable stat s; + + item_type() + {} + + item_type( int key, int val ) + : nKey( key ) + , nVal( val ) + , s() + {} + + item_type( const item_type& v ) + : nKey( v.nKey ) + , nVal( v.nVal ) + , s() + {} + + const int& key() const + { + return nKey; + } + + item_type& operator=( item_type const& src ) + { + nKey = src.nKey; + nVal = src.nVal; + return *this; + } + + item_type& operator=( item_type&& src ) + { + nKey = src.nKey; + nVal = src.nVal; + return *this; + } + }; + + template + struct less + { + bool operator ()( const T& v1, const T& v2 ) const + { + return v1.key() < v2.key(); + } + + template + bool operator ()( const T& v1, const Q& v2 ) const + { + return v1.key() < v2; + } + + template + bool operator ()( const Q& v1, const T& v2 ) const + { + return v1 < v2.key(); + } + }; + + struct other_item { + int nKey; + + other_item( int n ) + : nKey( n ) + {} + }; + + struct other_less { + template + bool operator()( T const& i1, Q const& i2 ) const + { + return i1.nKey < i2.nKey; + } + }; + + template + struct cmp { + int operator ()( const T& v1, const T& v2 ) const + { + if ( v1.key() < v2.key() ) + return -1; + return v1.key() > v2.key() ? 1 : 0; + } + + template + int operator ()( const T& v1, const Q& v2 ) const + { + if ( v1.key() < v2 ) + return -1; + return v1.key() > v2 ? 1 : 0; + } + + template + int operator ()( const Q& v1, const T& v2 ) const + { + if ( v1 < v2.key() ) + return -1; + return v1 > v2.key() ? 1 : 0; + } + }; + + struct mock_disposer + { + template + void operator ()( T * p ) + { + ++p->s.nDisposeCount; + } + }; + + struct update_functor + { + template + void operator()( T& item, T * old ) + { + if ( !old ) + ++item.s.nUpdateNewCall; + else + ++item.s.nUpdateExistsCall; + } + }; + + struct find_functor + { + template + void operator ()( T& item, Q& /*val*/ ) + { + ++item.s.nFindCall; + } + }; + + struct erase_functor + { + template + void operator()( T const& item ) + { + item.s.nEraseCall++; + } + }; + + protected: + template + void test_common( List& l ) + { + // Precondition: list is empty + // Postcondition: list is empty + + static const size_t nSize = 20; + typedef typename List::value_type value_type; + value_type arr[ nSize ]; + value_type arr2[ nSize ]; + + for ( size_t i = 0; i < nSize; ++i ) { + arr[i].nKey = static_cast( i ); + arr[i].nVal = arr[i].nKey * 10; + + arr2[i] = arr[i]; + } + shuffle( arr, arr + nSize ); + shuffle( arr2, arr2 + nSize ); + + ASSERT_TRUE( l.empty() ); + ASSERT_CONTAINER_SIZE( l, 0 ); + + typedef typename List::iterator iterator; + + // insert / find + for ( auto& i : arr ) { + EXPECT_FALSE( l.contains( i.nKey )); + EXPECT_FALSE( l.contains( other_item( i.nKey ), other_less())); + EXPECT_FALSE( l.find( i.nKey, []( value_type& item, int ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 0 ); + EXPECT_FALSE( l.find_with( other_item( i.nKey ), other_less(), []( value_type& item, other_item const& ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 0 ); + + switch ( i.nKey % 4 ) { + case 0: + EXPECT_TRUE( l.insert( i )); + break; + case 1: + EXPECT_EQ( i.s.nInsertCall, 0 ); + EXPECT_TRUE( l.insert( i, []( value_type& i ) { ++i.s.nInsertCall; } )); + EXPECT_EQ( i.s.nInsertCall, 1 ); + break; + case 2: + { + std::pair ret = l.update( i, []( value_type& i, value_type * old ) { + EXPECT_TRUE( old == nullptr ); + EXPECT_EQ( i.s.nUpdateNewCall, 0 ); + ++i.s.nUpdateNewCall; + }, false ); + EXPECT_EQ( i.s.nUpdateNewCall, 0 ); + EXPECT_EQ( ret.first, false ); + EXPECT_EQ( ret.second, false ); + + ret = l.update( i, []( value_type& i, value_type * old ) { + EXPECT_TRUE( old == nullptr ); + EXPECT_EQ( i.s.nUpdateNewCall, 0 ); + ++i.s.nUpdateNewCall; + }, true ); + EXPECT_EQ( i.s.nUpdateNewCall, 1 ); + EXPECT_EQ( ret.first, true ); + EXPECT_EQ( ret.second, true ); + } + break; + case 3: + { + std::pair ret = l.upsert( i, false ); + EXPECT_EQ( ret.first, false ); + EXPECT_EQ( ret.second, false ); + + ret = l.upsert( i ); + EXPECT_EQ( ret.first, true ); + EXPECT_EQ( ret.second, true ); + } + break; + } + + EXPECT_TRUE( l.contains( i.nKey )); + EXPECT_TRUE( l.contains( i )); + EXPECT_TRUE( l.contains( other_item( i.nKey ), other_less())); + EXPECT_TRUE( l.find( i.nKey, []( value_type& item, int ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 1 ); + EXPECT_TRUE( l.find( i, []( value_type& item, value_type const& ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 2 ); + EXPECT_TRUE( l.find_with( other_item( i.nKey ), other_less(), []( value_type& item, other_item const& ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 3 ); + + EXPECT_FALSE( l.insert( i ) ); + ASSERT_FALSE( l.empty() ); + + int const ckey = i.nKey; + iterator it = l.find( ckey ); + ASSERT_FALSE( it == l.end() ); + EXPECT_EQ( it->nKey, i.nKey ); + EXPECT_EQ( (*it).nVal, i.nVal ); + check_ordered( it, l.end() ); + + it = l.find( i.nKey ); + ASSERT_FALSE( it == l.end() ); + EXPECT_EQ( it->nKey, i.nKey ); + EXPECT_EQ( (*it).nVal, i.nVal ); + check_ordered( it, l.end() ); + + it = l.find_with( other_item( i.nKey ), other_less() ); + ASSERT_FALSE( it == l.end() ); + EXPECT_EQ( it->nKey, i.nKey ); + EXPECT_EQ( it->nVal, i.nVal ); + check_ordered( it, l.end() ); + + } + ASSERT_CONTAINER_SIZE( l, nSize ); + + // check all items + for ( auto const& i : arr ) { + EXPECT_TRUE( l.contains( i.nKey )); + EXPECT_TRUE( l.contains( i )); + EXPECT_TRUE( l.contains( other_item( i.nKey ), other_less())); + EXPECT_TRUE( l.find( i.nKey, []( value_type& item, int ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 4 ); + EXPECT_TRUE( l.find( i, []( value_type& item, value_type const& ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 5 ); + EXPECT_TRUE( l.find_with( other_item( i.nKey ), other_less(), []( value_type& item, other_item const& ) { ++item.s.nFindCall; } )); + EXPECT_EQ( i.s.nFindCall, 6 ); + } + ASSERT_FALSE( l.empty() ); + ASSERT_CONTAINER_SIZE( l, nSize ); + + // update existing test + for ( auto& i : arr2 ) { + EXPECT_EQ( i.s.nUpdateExistsCall, 0 ); + std::pair ret = l.update( i, update_functor() ); + EXPECT_TRUE( ret.first ); + EXPECT_FALSE( ret.second ); + EXPECT_EQ( i.s.nUpdateExistsCall, 1 ); + } + + // update with the same value must be empty - no functor is called + for ( auto& i : arr2 ) { + EXPECT_EQ( i.s.nUpdateExistsCall, 1 ); + std::pair ret = l.update( i, update_functor() ); + EXPECT_TRUE( ret.first ); + EXPECT_FALSE( ret.second ); + EXPECT_EQ( i.s.nUpdateExistsCall, 1 ); + } + + for ( auto& i : arr ) { + EXPECT_EQ( i.s.nUpdateExistsCall, 0 ); + std::pair ret = l.update( i, []( value_type& i, value_type * old ) { + EXPECT_FALSE( old == nullptr ); + EXPECT_EQ( i.s.nUpdateExistsCall, 0 ); + ++i.s.nUpdateExistsCall; + }); + EXPECT_TRUE( ret.first ); + EXPECT_FALSE( ret.second ); + EXPECT_EQ( i.s.nUpdateExistsCall, 1 ); + } + + // erase test + for ( auto const& i : arr ) { + if ( i.nKey & 1 ) + EXPECT_TRUE( l.erase( i.nKey )); + else + EXPECT_TRUE( l.erase_with( other_item( i.nKey ), other_less() )); + + EXPECT_FALSE( l.contains( i )); + } + EXPECT_TRUE( l.empty() ); + EXPECT_CONTAINER_SIZE( l, 0 ); + + // Apply retired pointer to clean links + List::gc::force_dispose(); + + for ( auto const& i : arr ) + EXPECT_EQ( i.s.nDisposeCount, 2 ); + for ( auto const& i : arr2 ) + EXPECT_EQ( i.s.nDisposeCount, 1 ); + + // erase with functor + for ( auto& i : arr ) { + int const updateNewCall = i.s.nUpdateNewCall; + std::pair ret = l.update( i, update_functor(), false ); + EXPECT_FALSE( ret.first ); + EXPECT_FALSE( ret.second ); + EXPECT_EQ( i.s.nUpdateNewCall, updateNewCall ); + + ret = l.update( i, update_functor(), true ); + EXPECT_TRUE( ret.first ); + EXPECT_TRUE( ret.second ); + EXPECT_EQ( i.s.nUpdateNewCall, updateNewCall + 1 ); + } + EXPECT_FALSE( l.empty() ); + EXPECT_CONTAINER_SIZE( l, nSize ); + + for ( auto const& i : arr ) { + EXPECT_EQ( i.s.nEraseCall, 0 ); + if ( i.nKey & 1 ) { + EXPECT_TRUE( l.erase_with( other_item( i.nKey ), other_less(), erase_functor())); + EXPECT_FALSE( l.erase_with( other_item( i.nKey ), other_less(), erase_functor())); + } + else { + EXPECT_TRUE( l.erase( i.nKey, []( value_type& item) { ++item.s.nEraseCall; } )); + EXPECT_FALSE( l.erase( i.nKey, []( value_type& item) { ++item.s.nEraseCall; } )); + } + EXPECT_EQ( i.s.nEraseCall, 1 ); + EXPECT_FALSE( l.contains( i.nKey )); + } + EXPECT_TRUE( l.empty() ); + EXPECT_CONTAINER_SIZE( l, 0 ); + + // Apply retired pointer to clean links + List::gc::force_dispose(); + + for ( auto const& i : arr ) + EXPECT_EQ( i.s.nDisposeCount, 3 ); + + // clear test + for ( auto& i : arr ) + EXPECT_TRUE( l.insert( i )); + + EXPECT_FALSE( l.empty() ); + EXPECT_CONTAINER_SIZE( l, nSize ); + + l.clear(); + + EXPECT_TRUE( l.empty() ); + EXPECT_CONTAINER_SIZE( l, 0 ); + + // Apply retired pointer to clean links + List::gc::force_dispose(); + for ( auto const& i : arr ) { + EXPECT_EQ( i.s.nDisposeCount, 4 ); + EXPECT_FALSE( l.contains( i )); + } + + // unlink test + for ( auto& i : arr ) + EXPECT_TRUE( l.insert( i ) ); + for ( auto& i : arr ) { + value_type val( i ); + EXPECT_TRUE( l.contains( val )); + EXPECT_FALSE( l.unlink( val )); + EXPECT_TRUE( l.contains( val ) ); + EXPECT_TRUE( l.unlink( i )); + EXPECT_FALSE( l.unlink( i )); + EXPECT_FALSE( l.contains( i ) ); + } + EXPECT_TRUE( l.empty() ); + EXPECT_CONTAINER_SIZE( l, 0 ); + + // Apply retired pointer to clean links + List::gc::force_dispose(); + for ( auto const& i : arr ) { + EXPECT_EQ( i.s.nDisposeCount, 5 ); + EXPECT_FALSE( l.contains( i ) ); + } + + // Iterators on empty list + { + auto it = l.begin(); + auto itEnd = l.end(); + auto cit = l.cbegin(); + auto citEnd = l.cend(); + + EXPECT_TRUE( it == itEnd ); + EXPECT_TRUE( it == cit ); + EXPECT_TRUE( cit == citEnd ); + + ++it; + ++cit; + + EXPECT_TRUE( it == itEnd ); + EXPECT_TRUE( it == cit ); + EXPECT_TRUE( cit == citEnd ); + } + } + + template + void test_ordered_iterator( List& l ) + { + // Precondition: list is empty + // Postcondition: list is empty + + static const size_t nSize = 20; + typedef typename List::value_type value_type; + value_type arr[nSize]; + + for ( size_t i = 0; i < nSize; ++i ) { + arr[i].nKey = static_cast(i); + arr[i].nVal = arr[i].nKey * 10; + } + shuffle( arr, arr + nSize ); + + ASSERT_TRUE( l.empty() ); + ASSERT_CONTAINER_SIZE( l, 0 ); + + for ( auto& i : arr ) + EXPECT_TRUE( l.insert( i ) ); + + int key = 0; + for ( auto it = l.begin(); it != l.end(); ++it ) { + EXPECT_EQ( it->nKey, key ); + EXPECT_EQ( (*it).nKey, key ); + ++key; + } + + key = 0; + for ( auto it = l.cbegin(); it != l.cend(); ++it ) { + EXPECT_EQ( it->nKey, key ); + EXPECT_EQ( (*it).nKey, key ); + ++key; + } + + l.clear(); + List::gc::force_dispose(); + for ( auto const& i : arr ) { + EXPECT_EQ( i.s.nDisposeCount, 1 ); + EXPECT_FALSE( l.contains( i ) ); + } + } + + template + void check_ordered( Iterator first, Iterator last ) + { + while ( first != last ) { + Iterator it = first; + if ( ++it != last ) { + EXPECT_LT( first->nKey, it->nKey ); + } + first = it; + } + } + + }; + +} // namespace cds_test + +#endif // CDSUNIT_LIST_TEST_INTRUSIVE_ITERABLE_LIST_H diff --git a/test/unit/list/test_intrusive_iterable_list_hp.h b/test/unit/list/test_intrusive_iterable_list_hp.h new file mode 100644 index 00000000..4fbb63db --- /dev/null +++ b/test/unit/list/test_intrusive_iterable_list_hp.h @@ -0,0 +1,110 @@ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016 + + 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 CDSUNIT_LIST_TEST_INTRUSIVE_ITERABLE_LIST_HP_H +#define CDSUNIT_LIST_TEST_INTRUSIVE_ITERABLE_LIST_HP_H + +#include "test_intrusive_iterable_list.h" + +namespace cds_test { + + class intrusive_iterable_list_hp : public intrusive_iterable_list + { + protected: + template + void test_hp( List& l ) + { + // Precondition: list is empty + // Postcondition: list is empty + + static const size_t nSize = 20; + typedef typename List::value_type value_type; + value_type arr[nSize]; + + for ( size_t i = 0; i < nSize; ++i ) { + arr[i].nKey = static_cast(i); + arr[i].nVal = arr[i].nKey * 10; + } + shuffle( arr, arr + nSize ); + + typedef typename List::guarded_ptr guarded_ptr; + + ASSERT_TRUE( l.empty() ); + ASSERT_CONTAINER_SIZE( l, 0 ); + + guarded_ptr gp; + + // get() test + for ( auto& i : arr ) { + gp = l.get( i.nKey ); + EXPECT_TRUE( !gp ); + gp = l.get_with( other_item( i.nKey ), other_less() ); + EXPECT_TRUE( !gp ); + + EXPECT_TRUE( l.insert( i ) ); + + gp = l.get( i.nKey ); + ASSERT_FALSE( !gp ); + EXPECT_EQ( gp->nKey, i.nKey ); + EXPECT_EQ( gp->nVal, i.nVal ); + gp = l.get_with( other_item( i.nKey ), other_less() ); + ASSERT_FALSE( !gp ); + EXPECT_EQ( gp->nKey, i.nKey ); + EXPECT_EQ( gp->nVal, i.nVal ); + } + + // extract() test + for ( int i = 0; i < static_cast(nSize); ++i ) { + if ( i & 1 ) + gp = l.extract( i ); + else + gp = l.extract_with( other_item( i ), other_less() ); + ASSERT_FALSE( !gp ); + EXPECT_EQ( gp->nKey, i ); + + gp = l.extract( i ); + EXPECT_TRUE( !gp ); + gp = l.extract_with( other_item( i ), other_less() ); + EXPECT_TRUE( !gp ); + } + + ASSERT_TRUE( l.empty() ); + ASSERT_CONTAINER_SIZE( l, 0 ); + + List::gc::force_dispose(); + for ( auto const& i : arr ) { + EXPECT_EQ( i.s.nDisposeCount, 1 ); + EXPECT_FALSE( l.contains( i ) ); + } + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_LIST_TEST_INTRUSIVE_ITERABLE_LIST_HP_H diff --git a/test/unit/list/test_intrusive_lazy_rcu.h b/test/unit/list/test_intrusive_lazy_rcu.h index 6dfe773f..f5ddc504 100644 --- a/test/unit/list/test_intrusive_lazy_rcu.h +++ b/test/unit/list/test_intrusive_lazy_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_LIST_TEST_INTRUSIVE_LAZY_LIST_RCU_H #define CDSUNIT_LIST_TEST_INTRUSIVE_LAZY_LIST_RCU_H @@ -144,6 +144,41 @@ TYPED_TEST_P( IntrusiveLazyList, base_hook_seqcst ) this->test_rcu( l ); } +TYPED_TEST_P( IntrusiveLazyList, base_hook_stat ) +{ + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::base_item > compare; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< typename TestFixture::rcu_type, typename TestFixture::base_item, traits > list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( IntrusiveLazyList, base_hook_wrapped_stat ) +{ + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::base_hook< cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::base_item > compare; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< typename TestFixture::rcu_type, typename TestFixture::base_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + TYPED_TEST_P( IntrusiveLazyList, member_hook ) { typedef ci::LazyList< typename TestFixture::rcu_type, typename TestFixture::member_item, @@ -228,11 +263,46 @@ TYPED_TEST_P( IntrusiveLazyList, member_hook_back_off ) this->test_rcu( l ); } +TYPED_TEST_P( IntrusiveLazyList, member_hook_stat ) +{ + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( typename TestFixture::member_item, hMember ), cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::member_item > compare; + typedef typename TestFixture::template less< typename TestFixture::member_item > less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::stat<> stat; + }; + typedef ci::LazyList< typename TestFixture::rcu_type, typename TestFixture::member_item, traits > list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( IntrusiveLazyList, member_hook_wrapped_stat ) +{ + struct traits: public ci::lazy_list::traits { + typedef ci::lazy_list::member_hook< offsetof( typename TestFixture::member_item, hMember ), cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::member_item > compare; + typedef cds::atomicity::item_counter item_counter; + typedef cds::intrusive::lazy_list::wrapped_stat<> stat; + }; + typedef ci::LazyList< typename TestFixture::rcu_type, typename TestFixture::member_item, traits > list_type; + + cds::intrusive::lazy_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( IntrusiveLazyList, - base_hook, base_hook_cmp, base_hook_item_counting, base_hook_backoff, base_hook_seqcst, member_hook, member_hook_cmp, member_hook_item_counting, member_hook_seqcst, member_hook_back_off + base_hook, base_hook_cmp, base_hook_item_counting, base_hook_backoff, base_hook_seqcst, base_hook_stat, base_hook_wrapped_stat, member_hook, member_hook_cmp, member_hook_item_counting, member_hook_seqcst, member_hook_back_off, member_hook_stat, member_hook_wrapped_stat ); diff --git a/test/unit/list/test_intrusive_michael_rcu.h b/test/unit/list/test_intrusive_michael_rcu.h index 45b0b2a8..65924680 100644 --- a/test/unit/list/test_intrusive_michael_rcu.h +++ b/test/unit/list/test_intrusive_michael_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_LIST_TEST_INTRUSIVE_MICHAEL_LIST_RCU_H #define CDSUNIT_LIST_TEST_INTRUSIVE_MICHAEL_LIST_RCU_H @@ -144,6 +144,39 @@ TYPED_TEST_P( IntrusiveMichaelList, base_hook_seqcst ) this->test_rcu( l ); } +TYPED_TEST_P( IntrusiveMichaelList, base_hook_stat ) +{ + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::base_hook< cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::base_item > compare; + typedef cds::intrusive::michael_list::stat<> stat; + }; + typedef ci::MichaelList< typename TestFixture::rcu_type, typename TestFixture::base_item, traits > list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( IntrusiveMichaelList, base_hook_wrapped_stat ) +{ + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::base_hook< cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::base_item > compare; + typedef cds::intrusive::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< typename TestFixture::rcu_type, typename TestFixture::base_item, traits > list_type; + + cds::intrusive::michael_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + TYPED_TEST_P( IntrusiveMichaelList, member_hook ) { typedef ci::MichaelList< typename TestFixture::rcu_type, typename TestFixture::member_item, @@ -228,11 +261,44 @@ TYPED_TEST_P( IntrusiveMichaelList, member_hook_back_off ) this->test_rcu( l ); } +TYPED_TEST_P( IntrusiveMichaelList, member_hook_stat ) +{ + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::member_hook< offsetof( typename TestFixture::member_item, hMember ), cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::member_item > compare; + typedef cds::intrusive::michael_list::stat<> stat; + }; + typedef ci::MichaelList< typename TestFixture::rcu_type, typename TestFixture::member_item, traits > list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( IntrusiveMichaelList, member_hook_wrapped_stat ) +{ + struct traits: public ci::michael_list::traits { + typedef ci::michael_list::member_hook< offsetof( typename TestFixture::member_item, hMember ), cds::opt::gc< typename TestFixture::rcu_type >> hook; + typedef typename TestFixture::mock_disposer disposer; + typedef typename TestFixture::template cmp< typename TestFixture::member_item > compare; + typedef cds::intrusive::michael_list::wrapped_stat<> stat; + }; + typedef ci::MichaelList< typename TestFixture::rcu_type, typename TestFixture::member_item, traits > list_type; + + cds::intrusive::michael_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( IntrusiveMichaelList, - base_hook, base_hook_cmp, base_hook_item_counting, base_hook_backoff, base_hook_seqcst, member_hook, member_hook_cmp, member_hook_item_counting, member_hook_seqcst, member_hook_back_off + base_hook, base_hook_cmp, base_hook_item_counting, base_hook_backoff, base_hook_seqcst, base_hook_stat, base_hook_wrapped_stat, member_hook, member_hook_cmp, member_hook_item_counting, member_hook_seqcst, member_hook_back_off, member_hook_stat, member_hook_wrapped_stat ); diff --git a/test/unit/list/test_kv_lazy_rcu.h b/test/unit/list/test_kv_lazy_rcu.h index 2571f3e3..fb303459 100644 --- a/test/unit/list/test_kv_lazy_rcu.h +++ b/test/unit/list/test_kv_lazy_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_LIST_TEST_KV_LAZY_LIST_RCU_H @@ -165,10 +165,43 @@ TYPED_TEST_P( LazyKVList, mutex ) this->test_rcu( l ); } +TYPED_TEST_P( LazyKVList, stat ) +{ + struct traits: public cc::lazy_list::traits + { + typedef typename TestFixture::lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyKVList list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( LazyKVList, wrapped_stat ) +{ + struct traits: public cc::lazy_list::traits + { + typedef typename TestFixture::lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyKVList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( LazyKVList, - less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst, mutex + less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst, mutex, stat, wrapped_stat ); #endif // CDSUNIT_LIST_TEST_KV_LAZY_LIST_RCU_H diff --git a/test/unit/list/test_kv_michael_rcu.h b/test/unit/list/test_kv_michael_rcu.h index 730049b1..54a8239b 100644 --- a/test/unit/list/test_kv_michael_rcu.h +++ b/test/unit/list/test_kv_michael_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_LIST_TEST_MICHAEL_KV_LIST_RCU_H #define CDSUNIT_LIST_TEST_MICHAEL_KV_LIST_RCU_H @@ -148,10 +148,43 @@ TYPED_TEST_P( MichaelKVList, seq_cst ) this->test_rcu( l ); } +TYPED_TEST_P( MichaelKVList, stat ) +{ + struct traits: public cc::michael_list::traits + { + typedef typename TestFixture::lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( MichaelKVList, wrapped_stat ) +{ + struct traits: public cc::michael_list::traits + { + typedef typename TestFixture::lt less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + }; + typedef cc::MichaelKVList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( MichaelKVList, - less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst + less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst, stat, wrapped_stat ); #endif // CDSUNIT_LIST_TEST_MICHAEL_KV_LIST_RCU_H diff --git a/test/unit/list/test_lazy_rcu.h b/test/unit/list/test_lazy_rcu.h index f506d7b9..434d78f7 100644 --- a/test/unit/list/test_lazy_rcu.h +++ b/test/unit/list/test_lazy_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_LIST_TEST_LAZY_LIST_RCU_H @@ -149,6 +149,39 @@ TYPED_TEST_P( LazyList, seq_cst ) this->test_rcu( l ); } +TYPED_TEST_P( LazyList, stat ) +{ + struct traits: public cc::lazy_list::traits + { + typedef typename TestFixture::template lt< typename TestFixture::item> less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::stat<> stat; + }; + typedef cc::LazyList list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( LazyList, wrapped_stat ) +{ + struct traits: public cc::lazy_list::traits + { + typedef typename TestFixture::template lt< typename TestFixture::item> less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::lazy_list::wrapped_stat<> stat; + }; + typedef cc::LazyList list_type; + + cds::container::lazy_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + TYPED_TEST_P( LazyList, mutex ) { struct traits : public cc::lazy_list::traits @@ -168,7 +201,7 @@ TYPED_TEST_P( LazyList, mutex ) // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( LazyList, - less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst, mutex + less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst, mutex, stat, wrapped_stat ); #endif // CDSUNIT_LIST_TEST_LAZY_LIST_RCU_H diff --git a/test/unit/list/test_michael_rcu.h b/test/unit/list/test_michael_rcu.h index 143afc42..f0e4059a 100644 --- a/test/unit/list/test_michael_rcu.h +++ b/test/unit/list/test_michael_rcu.h @@ -25,7 +25,7 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CDSUNIT_LIST_TEST_MICHAEL_LIST_RCU_H #define CDSUNIT_LIST_TEST_MICHAEL_LIST_RCU_H @@ -148,10 +148,43 @@ TYPED_TEST_P( MichaelList, seq_cst ) this->test_rcu( l ); } +TYPED_TEST_P( MichaelList, stat ) +{ + struct traits: public cc::michael_list::traits + { + typedef typename TestFixture::template lt< typename TestFixture::item> less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::stat<> stat; + }; + typedef cc::MichaelList list_type; + + list_type l; + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + +TYPED_TEST_P( MichaelList, wrapped_stat ) +{ + struct traits: public cc::michael_list::traits + { + typedef typename TestFixture::template lt< typename TestFixture::item> less; + typedef cds::atomicity::item_counter item_counter; + typedef cds::container::michael_list::wrapped_stat<> stat; + }; + typedef cc::MichaelList list_type; + + cds::container::michael_list::stat<> st; + list_type l( st ); + this->test_common( l ); + this->test_ordered_iterator( l ); + this->test_rcu( l ); +} + // GCC 5: All test names should be written on single line, otherwise a runtime error will be encountered like as // "No test named can be found in this test case" REGISTER_TYPED_TEST_CASE_P( MichaelList, - less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst + less_ordered, compare_ordered, mix_ordered, item_counting, backoff, seq_cst, stat, wrapped_stat ); #endif // CDSUNIT_LIST_TEST_MICHAEL_LIST_RCU_H diff --git a/test/unit/misc/bitop.cpp b/test/unit/misc/bitop.cpp index 97a75897..54670dc0 100644 --- a/test/unit/misc/bitop.cpp +++ b/test/unit/misc/bitop.cpp @@ -25,13 +25,13 @@ 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. + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include -#include +//#include namespace { class bitop : public ::testing::Test @@ -134,4 +134,27 @@ namespace { } } + /* + TEST_F( bitop, bit_reverse_counter ) + { + cds::bitop::bit_reverse_counter<> c; + + while ( c.value() < 8 ) { + size_t res = c.inc(); + std::cout << "inc result: " << res + << " value: " << c.value() + << " reversed: " << c.reversed_value() + << " high_bit: " << c.high_bit() << "\n"; + } + + while ( c.value() > 0 ) { + size_t res = c.dec(); + std::cout << "dec result: " << res + << " value: " << c.value() + << " reversed: " << c.reversed_value() + << " high_bit: " << c.high_bit() << "\n"; + } + } + */ + } // namespace diff --git a/test/unit/pqueue/intrusive_mspqueue.cpp b/test/unit/pqueue/intrusive_mspqueue.cpp index c5e29c07..ee95693c 100644 --- a/test/unit/pqueue/intrusive_mspqueue.cpp +++ b/test/unit/pqueue/intrusive_mspqueue.cpp @@ -267,4 +267,32 @@ namespace { test( *pq ); } + TEST_F( IntrusiveMSPQueue, bit_reverse_counter ) + { + typedef cds::intrusive::MSPriorityQueue< value_type, + cds::intrusive::mspriority_queue::make_traits< + cds::opt::buffer< dyn_buffer_type > + , cds::opt::less< less > + , cds::opt::item_counter< cds::bitop::bit_reverse_counter<>> + >::type + > pqueue; + + pqueue pq( c_nCapacity ); + test( pq ); + } + + TEST_F( IntrusiveMSPQueue, monotonic_counter ) + { + typedef cds::intrusive::MSPriorityQueue< value_type, + cds::intrusive::mspriority_queue::make_traits< + cds::opt::buffer< dyn_buffer_type > + , cds::opt::less< less > + , cds::opt::item_counter< cds::intrusive::mspriority_queue::monotonic_counter > + >::type + > pqueue; + + pqueue pq( c_nCapacity ); + test( pq ); + } + } // namespace diff --git a/test/unit/pqueue/mspqueue.cpp b/test/unit/pqueue/mspqueue.cpp index 88ce0373..cf278ef3 100644 --- a/test/unit/pqueue/mspqueue.cpp +++ b/test/unit/pqueue/mspqueue.cpp @@ -282,4 +282,32 @@ namespace { test( *pq ); } + TEST_F( MSPQueue, bit_reverse_counter ) + { + typedef cds::container::MSPriorityQueue< value_type, + cds::container::mspriority_queue::make_traits< + cds::opt::buffer< dyn_buffer_type > + ,cds::opt::less< less > + ,cds::opt::item_counter< cds::bitop::bit_reverse_counter<>> + >::type + > pqueue; + + pqueue pq( c_nCapacity ); + test( pq ); + } + + TEST_F( MSPQueue, monotonic_counter ) + { + typedef cds::container::MSPriorityQueue< value_type, + cds::container::mspriority_queue::make_traits< + cds::opt::buffer< dyn_buffer_type > + , cds::opt::less< less > + , cds::opt::item_counter< cds::container::mspriority_queue::monotonic_counter> + >::type + > pqueue; + + pqueue pq( c_nCapacity ); + test( pq ); + } + } // namespace