/*
This file is a part of libcds - Concurrent Data Structures library
- (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+ (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
Source code repo: http://github.com/khizmax/libcds/
Download: http://sourceforge.net/projects/libcds/files/
-
+
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
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
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 <typename... Options>
struct rebind_traits {
, typename cds::opt::make_options< traits, Options...>::type
> type;
};
+
+ // Stat selector
+ template <typename Stat>
+ using select_stat_wrapper = lazy_list::select_stat_wrapper< Stat >;
//@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
/// Returns a forward const iterator addressing the first element in a list
const_iterator cbegin() const
{
- const_iterator it( const_cast<node_type *>(&m_Head) );
+ const_iterator it( const_cast<node_type *>(&m_Head));
++it; // skip dummy head
return it;
}
/// Returns an const iterator that addresses the location succeeding the last element in a list
const_iterator cend() const
{
- return const_iterator( const_cast<node_type *>(&m_Tail) );
+ return const_iterator( const_cast<node_type *>(&m_Tail));
}
public:
/// 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 <typename Stat, typename = std::enable_if<std::is_same<stat, lazy_list::wrapped_stat<Stat>>::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()
{
template <typename Q>
value_type * contains( Q const& key )
{
- return find_at( &m_Head, key, key_comparator() );
+ return find_at( &m_Head, key, key_comparator());
}
//@cond
template <typename Q>
typename std::enable_if<Sort, value_type *>::type contains( Q const& key, Less pred )
{
CDS_UNUSED( pred );
- return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>() );
+ return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
}
//@cond
template <typename Q, typename Less, bool Sort = c_bSort>
*/
void clear()
{
- clear( disposer() );
+ clear( disposer());
}
/// Checks if the list is empty
return m_ItemCounter.value();
}
+ /// Returns const reference to internal statistics
+ stat const& statistics() const
+ {
+ return m_Stat;
+ }
+
protected:
//@cond
// split-list support
// 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( pHead, *node_traits::to_value_ptr( pNode ) );
+ return insert_at( pHead, *node_traits::to_value_ptr( pNode ));
}
bool insert_at( node_type * pHead, value_type& val )
{
auto_lock_position alp( pos );
if ( validate( pos.pPred, pos.pCur )) {
- if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) {
+ 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 )
// 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 <typename Func>
search( pHead, val, pos, pred );
if ( pos.pCur != &m_Tail ) {
std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
- if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) )
+ 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;
}
value_type * find_at( node_type * pHead, Q& val, Pred pred)
{
iterator it = find_at_( pHead, val, pred );
- if ( it != end() )
+ if ( it != end())
return &*it;
return nullptr;
}
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();
}
node_type * pCur = pHead;
node_type * pPrev = pHead;
- while ( pCur != pTail && ( pCur == pHead || !equal( *node_traits::to_value_ptr( *pCur ), key, eq ) )) {
+ while ( pCur != pTail && ( pCur == pHead || !equal( *node_traits::to_value_ptr( *pCur ), key, eq ))) {
pPrev = pCur;
pCur = pCur->m_pNext.load(memory_model::memory_order_acquire);
}
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
if ( pred( *node_traits::to_value_ptr( pHead ))) {
assert( pPred != nullptr );
pPred->m_pNext.store( p, memory_model::memory_order_relaxed );
- dispose_node( pHead, disposer() );
+ dispose_node( pHead, disposer());
}
else
pPred = pHead;