#include <cds/container/details/base.h>
#include <cds/opt/compare.h>
#include <cds/urcu/options.h>
-#include <cds/lock/spinlock.h>
+#include <cds/sync/spinlock.h>
+#include <cds/sync/injecting_monitor.h>
namespace cds { namespace container {
/// BronsonAVLTree related declarations
namespace bronson_avltree {
- template <typename Key, typename T>
+ template <typename Key, typename T, typename SyncMonitor >
struct node;
//@cond
- template <typename Key, typename T, typename Lock>
- struct link
+ template <typename Node, typename T, typename SyncMonitor>
+ struct link_node
{
- typedef node<Key, T> node_type;
- typedef uint32_t version_type;
- typedef Lock lock_type;
+ typedef Node node_type;
+ typedef T mapped_type;
+ typedef uint32_t version_type; ///< version type (internal)
enum
{
atomics::atomic<node_type *> m_pParent; ///< Parent node
atomics::atomic<node_type *> m_pLeft; ///< Left child
atomics::atomic<node_type *> m_pRight; ///< Right child
- lock_type m_Lock; ///< Node-level lock
+ typename SyncMonitor::node_injection m_SyncMonitorInjection; ///< @ref cds_sync_monitor "synchronization monitor" injected data
+ atomics::atomic<mapped_type *> m_pValue; ///< Value
- node_type * m_pNextRemoved; ///< thread-local list o removed node
-
- link()
+ public:
+ link_node()
: m_nHeight( 0 )
, m_nVersion( 0 )
, m_pParent( nullptr )
, m_pLeft( nullptr )
, m_pRight( nullptr )
- , m_pNextRemoved( nullptr )
+ , m_pValue( nullptr )
{}
- link( int nHeight, version_type version, node_type * pParent, node_type * pLeft, node_type * pRight )
+ link_node( int nHeight, version_type version, node_type * pParent, node_type * pLeft, node_type * pRight )
: m_nHeight( nHeight )
, m_nVersion( version )
, m_pParent( pParent )
, m_pLeft( pLeft )
, m_pRight( pRight )
- , m_pNextRemoved( nullptr )
+ , m_pValue( nullptr )
{}
- atomics::atomic<node_type *>& child( int nDirection ) const
+ atomics::atomic<node_type *>& child( int nDirection )
{
assert( nDirection != 0 );
return nDirection < 0 ? m_pLeft : m_pRight;
return m_nHeight.load( order );
}
- void height( int h, atomics::memory_order order )
+ void height( int h, atomics::memory_order order )
{
m_nHeight.store( h, order );
+ }
template <typename BackOff>
void wait_until_shrink_completed( atomics::memory_order order ) const
{
BackOff bkoff;
- while ( is_shrinking( order ))
+ while ( is_shrinking( order ) )
bkoff();
}
bool is_unlinked( atomics::memory_order order ) const
{
- return (m_nVersion.load( order ) & unlinked) != 0;
+ return m_nVersion.load( order ) == unlinked;
}
bool is_shrinking( atomics::memory_order order ) const
{
return (m_nVersion.load( order ) & shrinking) != 0;
}
+
+ mapped_type * value( atomics::memory_order order ) const
+ {
+ return m_pValue.load( order );
+ }
+
+ bool is_valued( atomics::memory_order order ) const
+ {
+ return value( order ) != nullptr;
+ }
};
+ //@endcond
- template <typename Key, typename T, typename Lock>
- struct node : public link< Key, T, Lock >
+ /// BronsonAVLTree internal node
+ template <typename Key, typename T, typename SyncMonitor >
+ struct node<Key, T*, SyncMonitor>: public link_node< node<Key, T*, SyncMonitor>, T, SyncMonitor >
{
- typedef Key key_type;
- typedef T mapped_type;
- typedef link< key_type, mapped_type, Lock > base_class;
+ //@cond
+ typedef link_node< node<Key, T*, SyncMonitor>, T, SyncMonitor > base_class;
+ //@endcond
- key_type const m_key;
- atomics::atomic<mapped_type *> m_pValue;
+ typedef Key key_type; ///< key type
+ typedef T mapped_type; ///< value type
+ //@cond
+ typedef typename base_class::version_type version_type;
+ //@endcond
+ key_type const m_key; ///< Key
+ node * m_pNextRemoved; ///< thread-local list of removed node
+
+ public:
+ //@cond
template <typename Q>
node( Q&& key )
- : m_key( std::forward<Q>(key) )
- , m_pValue( nullptr )
+ : base_class()
+ , m_key( std::forward<Q>( key ) )
+ , m_pNextRemoved( nullptr )
{}
template <typename Q>
- node( Q&& key, int nHeight, version_type version, node_type * pParent, node_type * pLeft, node_type * pRight )
+ node( Q&& key, int nHeight, version_type version, node * pParent, node * pLeft, node * pRight )
: base_class( nHeight, version, pParent, pLeft, pRight )
, m_key( std::forward<Q>( key ) )
- , m_pValue( nullptr )
+ , m_pNextRemoved( nullptr )
{}
-
- T * value( atomics::memory_order order ) const
- {
- return m_pValue.load( order );
- }
+ //@endcond
};
- //@endcond
/// BronsonAVLTreeMap internal statistics
template <typename Counter = cds::atomicity::event_counter>
event_counter m_nInsertSuccess; ///< Count of inserting data node
event_counter m_nRelaxedInsertFailed; ///< Count of false creating of data nodes (only if @ref bronson_avltree::relaxed_insert "relaxed insertion" is enabled)
event_counter m_nInsertRetry; ///< Count of insert retries via concurrent operations
- event_counter m_nUpdateWaitShrinking; ///< Count of waiting until shrinking completed duting \p update() call
+ event_counter m_nUpdateWaitShrinking; ///< Count of waiting until shrinking completed during \p update() call
event_counter m_nUpdateRetry; ///< Count of update retries via concurrent operations
- event_counter m_nUpdateRootWaitShinking; ///< Count of waiting until root shrinking completed duting \p update() call
+ event_counter m_nUpdateRootWaitShrinking; ///< Count of waiting until root shrinking completed duting \p update() call
event_counter m_nUpdateSuccess; ///< Count of updating data node
- event_counter m_nUpdateUnlinked; ///< Count of updating of unlinked node attempts
+ event_counter m_nUpdateUnlinked; ///< Count of attempts to update unlinked node
+ event_counter m_nDisposedNode; ///< Count of disposed node
event_counter m_nDisposedValue; ///< Count of disposed value
+ event_counter m_nExtractedValue; ///< Count of extracted value
+ event_counter m_nRemoveRetry; ///< Count o erase/extract retries
+ event_counter m_nRemoveWaitShrinking; ///< ount of waiting until shrinking completed during \p erase() or \p extract() call
+ event_counter m_nRemoveRootWaitShrinking; ///< Count of waiting until root shrinking completed duting \p erase() or \p extract() call
+
+ event_counter m_nRightRotation; ///< Count of single right rotation
+ event_counter m_nLeftRotation; ///< Count of single left rotation
+ event_counter m_nLeftRightRotation; ///< Count of double left-over-right rotation
+ event_counter m_nRightLeftRotation; ///< Count of double right-over-left rotation
+
+ event_counter m_nRotateAfterRightRotation; ///< Count of rotation required after single right rotation
+ event_counter m_nRemoveAfterRightRotation; ///< Count of removal required after single right rotation
+ event_counter m_nDamageAfterRightRotation; ///< Count of damaged node after single right rotation
+
+ event_counter m_nRotateAfterLeftRotation; ///< Count of rotation required after signle left rotation
+ event_counter m_nRemoveAfterLeftRotation; ///< Count of removal required after single left rotation
+ event_counter m_nDamageAfterLeftRotation; ///< Count of damaged node after single left rotation
+
+ event_counter m_nRotateAfterRLRotation; ///< Count of rotation required after right-over-left rotation
+ event_counter m_nRemoveAfterRLRotation; ///< Count of removal required after right-over-left rotation
+ event_counter m_nRotateAfterLRRotation; ///< Count of rotation required after left-over-right rotation
+ event_counter m_nRemoveAfterLRRotation; ///< Count of removal required after left-over-right rotation
+
+ event_counter m_nInsertRebalanceReq; ///< Count of rebalance required after inserting
+ event_counter m_nRemoveRebalanceReq; ///< Count of rebalance required after removing
//@cond
void onFindSuccess() { ++m_nFindSuccess ; }
void onUpdateRootWaitShrinking() { ++m_nUpdateRootWaitShrinking; }
void onUpdateSuccess() { ++m_nUpdateSuccess; }
void onUpdateUnlinked() { ++m_nUpdateUnlinked; }
+ void onDisposeNode() { ++m_nDisposedNode; }
void onDisposeValue() { ++m_nDisposedValue; }
-
+ void onExtractValue() { ++m_nExtractedValue; }
+ void onRemoveRetry() { ++m_nRemoveRetry; }
+ void onRemoveWaitShrinking() { ++m_nRemoveWaitShrinking; }
+ void onRemoveRootWaitShrinking() { ++m_nRemoveRootWaitShrinking; }
+
+ void onRotateRight() { ++m_nRightRotation; }
+ void onRotateLeft() { ++m_nLeftRotation; }
+ void onRotateRightOverLeft() { ++m_nRightLeftRotation; }
+ void onRotateLeftOverRight() { ++m_nLeftRightRotation; }
+
+ void onRotateAfterRightRotation() { ++m_nRotateAfterRightRotation; }
+ void onRemoveAfterRightRotation() { ++m_nRemoveAfterRightRotation; }
+ void onDamageAfterRightRotation() { ++m_nDamageAfterRightRotation; }
+
+ void onRotateAfterLeftRotation() { ++m_nRotateAfterLeftRotation; }
+ void onRemoveAfterLeftRotation() { ++m_nRemoveAfterLeftRotation; }
+ void onDamageAfterLeftRotation() { ++m_nDamageAfterLeftRotation; }
+
+ void onRotateAfterRLRotation() { ++m_nRotateAfterRLRotation; }
+ void onRemoveAfterRLRotation() { ++m_nRemoveAfterRLRotation; }
+ void onRotateAfterLRRotation() { ++m_nRotateAfterLRRotation; }
+ void onRemoveAfterLRRotation() { ++m_nRemoveAfterLRRotation; }
+
+ void onInsertRebalanceRequired() { ++m_nInsertRebalanceReq; }
+ void onRemoveRebalanceRequired() { ++m_nRemoveRebalanceReq; }
//@endcond
};
void onUpdateRootWaitShrinking() const {}
void onUpdateSuccess() const {}
void onUpdateUnlinked() const {}
+ void onDisposeNode() const {}
void onDisposeValue() const {}
-
+ void onExtractValue() const {}
+ void onRemoveRetry() const {}
+ void onRemoveWaitShrinking() const {}
+ void onRemoveRootWaitShrinking() const {}
+
+ void onRotateRight() const {}
+ void onRotateLeft() const {}
+ void onRotateRightOverLeft() const {}
+ void onRotateLeftOverRight() const {}
+
+ void onRotateAfterRightRotation() const {}
+ void onRemoveAfterRightRotation() const {}
+ void onDamageAfterRightRotation() const {}
+
+ void onRotateAfterLeftRotation() const {}
+ void onRemoveAfterLeftRotation() const {}
+ void onDamageAfterLeftRotation() const {}
+
+ void onRotateAfterRLRotation() const {}
+ void onRemoveAfterRLRotation() const {}
+ void onRotateAfterLRRotation() const {}
+ void onRemoveAfterLRRotation() const {}
+
+ void onInsertRebalanceRequired() const {}
+ void onRemoveRebalanceRequired() const {}
//@endcond
};
- /// Option to allow relaxed insert into Bronson et al AVL-tree
+ /// Option to allow relaxed insert into \ref cds_container_BronsonAVLTreeMap_rcu "Bronson et al AVL-tree"
/**
- By default, this opton is disabled and the new node is created under its parent lock.
+ By default, this option is disabled and the new node is created under its parent lock.
In this case, it is guaranteed the new node will be attached to its parent.
On the other hand, constructing of the new node can be too complex to make it under the lock,
that can lead to lock contention.
When this option is enabled, the new node is created before locking the parent node.
- After that, the parent is locked and checked whether the new node can be attached to the parent.
+ After that, the parent is locked and checked whether the new node may be attached to the parent.
In this case, false node creating can be performed, but locked section can be significantly small.
*/
template <bool Enable>
//@endcond
};
- /// BronsnAVLTreeMap traits
+ /// \p BronsonAVLTreeMap traits
/**
Note that there are two main specialization of Bronson et al AVL-tree:
- - pointer-oriented - the tree node stores an user-provided pointer to value: <tt>BronsonAVLTreeMap<GC, Key, T *, Traits> </tt>
- - data-oriented - the tree node contains a copy of values: <tt>BronsonAVLTreeMap<GC, Key, T, Traits> </tt> where \p T is not a pointer type.
+ - \ref cds_container_BronsonAVLTreeMap_rcu_ptr "pointer-oriented" - the tree node stores an user-provided pointer to value
+ - \ref cds_container_BronsonAVLTreeMap_rcu "data-oriented" - the tree node contains a copy of values
Depends on tree specialization, different traits member can be used.
*/
typedef opt::none less;
/// Allocator for internal node
+ typedef CDS_DEFAULT_ALLOCATOR node_allocator;
+
+ /// Allocator for node's value (not used in \p BronsonAVLTreeMap<RCU, Key, T*, Traits> specialisation)
typedef CDS_DEFAULT_ALLOCATOR allocator;
/// Disposer (only for pointer-oriented tree specialization)
/**
The functor used for dispose removed values.
The user-provided disposer is used only for pointer-oriented tree specialization
- like \p BronsonAVLTreeMap<GC, Key, T*, Traits>. When the node becomes the rounting node without value,
+ like \p BronsonAVLTreeMap<GC, Key, T*, Traits>. When the node becomes the routing node without value,
the disposer will be called to signal that the memory for the value can be safely freed.
Default is \ref cds::intrusive::opt::delete_disposer "cds::container::opt::v::delete_disposer<>" which calls \p delete operator.
*/
typedef opt::v::delete_disposer<> disposer;
- /// Node lock
- typedef std::mutex lock_type;
+ /// @ref cds_sync_monitor "Synchronization monitor" type for node-level locking
+ typedef cds::sync::injecting_monitor<cds::sync::spin> sync_monitor;
/// Enable relaxed insertion.
/**
List of available options see \p opt::rcu_check_deadlock
*/
typedef cds::opt::v::rcu_throw_deadlock rcu_check_deadlock;
-
- //@cond
- // Internal traits, not for direct usage
- typedef opt::none node_type;
- //@endcond
};
/// Metafunction converting option list to BronsonAVLTreeMap traits
/**
Note that there are two main specialization of Bronson et al AVL-tree:
- - pointer-oriented - the tree node stores an user-provided pointer to value: <tt>BronsonAVLTreeMap<GC, Key, T *, Traits> </tt>
- - data-oriented - the tree node contains a copy of values: <tt>BronsonAVLTreeMap<GC, Key, T, Traits> </tt> where \p T is not a pointer type.
+ - \ref cds_container_BronsonAVLTreeMap_rcu_ptr "pointer-oriented" - the tree node stores an user-provided pointer to value
+ - \ref cds_container_BronsonAVLTreeMap_rcu "data-oriented" - the tree node contains a copy of values
Depends on tree specialization, different options can be specified.
- \p opt::compare - key compare functor. No default functor is provided.
If the option is not specified, \p %opt::less is used.
- \p opt::less - specifies binary predicate used for key compare. At least \p %opt::compare or \p %opt::less should be defined.
- - \p opt::allocator - the allocator for internal nodes. Default is \ref CDS_DEFAULT_ALLOCATOR.
+ - \p opt::node_allocator - the allocator for internal nodes. Default is \ref CDS_DEFAULT_ALLOCATOR.
+ - \p opt::allocator - the allocator for node's value. Default is \ref CDS_DEFAULT_ALLOCATOR.
+ This option is not used in \p BronsonAVLTreeMap<RCU, Key, T*, Traits> specialisation
- \ref cds::intrusive::opt::disposer "container::opt::disposer" - the functor used for dispose removed values.
The user-provided disposer is used only for pointer-oriented tree specialization
like \p BronsonAVLTreeMap<GC, Key, T*, Traits>. When the node becomes the rounting node without value,
the disposer will be called to signal that the memory for the value can be safely freed.
Default is \ref cds::intrusive::opt::delete_disposer "cds::container::opt::v::delete_disposer<>" which calls \p delete operator.
Due the nature of GC schema the disposer may be called asynchronously.
- - \p opt::lock_type - node lock type, default is \p std::mutex
+ - \p opt::sync_monitor - @ref cds_sync_monitor "synchronization monitor" type for node-level locking,
+ default is \p cds::sync::injecting_monitor<cds::sync::spin>
- \p bronson_avltree::relaxed_insert - enable (\p true) or disable (\p false, the default)
@ref bronson_avltree::relaxed_insert "relaxed insertion"
- \p opt::item_counter - the type of item counting feature, by default it is disabled (\p atomicity::empty_item_counter)
>::type type;
# endif
};
-
-
} // namespace bronson_avltree
// Forwards