Added more statistics to BronsonAVLTree
[libcds.git] / cds / container / details / bronson_avltree_base.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_CONTAINER_DETAILS_BRONSON_AVLTREE_BASE_H
4 #define CDSLIB_CONTAINER_DETAILS_BRONSON_AVLTREE_BASE_H
5
6 #include <cds/container/details/base.h>
7 #include <cds/opt/compare.h>
8 #include <cds/urcu/options.h>
9 #include <cds/sync/spinlock.h>
10 #include <cds/sync/injecting_monitor.h>
11
12 namespace cds { namespace container {
13
14     /// BronsonAVLTree related declarations
15     namespace bronson_avltree {
16
17         template <typename Key, typename T, typename SyncMonitor >
18         struct node;
19
20         //@cond
21         template <typename Node, typename T, typename SyncMonitor>
22         struct link_node
23         {
24             typedef Node     node_type;
25             typedef T        mapped_type;
26             typedef uint32_t version_type;  ///< version type (internal)
27
28             enum
29             {
30                 shrinking = 1,
31                 unlinked = 2,
32                 version_flags = shrinking | unlinked
33                 // the rest is version counter
34             };
35
36             atomics::atomic< int >          m_nHeight;  ///< Node height
37             atomics::atomic<version_type>   m_nVersion; ///< Version bits
38             atomics::atomic<node_type *>    m_pParent;  ///< Parent node
39             atomics::atomic<node_type *>    m_pLeft;    ///< Left child
40             atomics::atomic<node_type *>    m_pRight;   ///< Right child
41             typename SyncMonitor::node_injection m_SyncMonitorInjection;    ///< @ref cds_sync_monitor "synchronization monitor" injected data
42             atomics::atomic<mapped_type *>  m_pValue;   ///< Value
43
44         public:
45             link_node()
46                 : m_nHeight( 0 )
47                 , m_nVersion( 0 )
48                 , m_pParent( nullptr )
49                 , m_pLeft( nullptr )
50                 , m_pRight( nullptr )
51                 , m_pValue( nullptr )
52             {}
53
54             link_node( int nHeight, version_type version, node_type * pParent, node_type * pLeft, node_type * pRight )
55                 : m_nHeight( nHeight )
56                 , m_nVersion( version )
57                 , m_pParent( pParent )
58                 , m_pLeft( pLeft )
59                 , m_pRight( pRight )
60                 , m_pValue( nullptr )
61             {}
62
63             atomics::atomic<node_type *>& child( int nDirection )
64             {
65                 assert( nDirection != 0 );
66                 return nDirection < 0 ? m_pLeft : m_pRight;
67             }
68
69             void child( node_type * pChild, int nDirection, atomics::memory_order order )
70             {
71                 assert( nDirection != 0 );
72                 if ( nDirection < 0 )
73                     m_pLeft.store( pChild, order );
74                 else
75                     m_pRight.store( pChild, order );
76             }
77
78             version_type version( atomics::memory_order order ) const
79             {
80                 return m_nVersion.load( order );
81             }
82
83             void version( version_type ver, atomics::memory_order order )
84             {
85                 m_nVersion.store( ver, order );
86             }
87
88             int height( atomics::memory_order order ) const
89             {
90                 return m_nHeight.load( order );
91             }
92
93             void height( int h, atomics::memory_order order )
94             {
95                 m_nHeight.store( h, order );
96             }
97
98             template <typename BackOff>
99             void wait_until_shrink_completed( atomics::memory_order order ) const
100             {
101                 BackOff bkoff;
102                 while ( is_shrinking( order ) )
103                     bkoff();
104             }
105
106             bool is_unlinked( atomics::memory_order order ) const
107             {
108                 return m_nVersion.load( order ) == unlinked;
109             }
110
111             bool is_shrinking( atomics::memory_order order ) const
112             {
113                 return (m_nVersion.load( order ) & shrinking) != 0;
114             }
115
116             mapped_type * value( atomics::memory_order order ) const
117             {
118                 return m_pValue.load( order );
119             }
120
121             bool is_valued( atomics::memory_order order ) const
122             {
123                 return value( order ) != nullptr;
124             }
125         };
126         //@endcond
127
128         /// BronsonAVLTree internal node
129         template <typename Key, typename T, typename SyncMonitor >
130         struct node<Key, T*, SyncMonitor>: public link_node< node<Key, T*, SyncMonitor>, T, SyncMonitor >
131         {
132             //@cond
133             typedef link_node< node<Key, T*, SyncMonitor>, T, SyncMonitor > base_class;
134             //@endcond
135
136             typedef Key key_type;       ///< key type
137             typedef T   mapped_type;    ///< value type
138             //@cond
139             typedef typename base_class::version_type version_type;
140             //@endcond
141
142             key_type const                  m_key;      ///< Key
143             node *                          m_pNextRemoved; ///< thread-local list of removed node
144
145         public:
146             //@cond
147             template <typename Q>
148             node( Q&& key )
149                 : base_class()
150                 , m_key( std::forward<Q>( key ) )
151                 , m_pNextRemoved( nullptr )
152             {}
153
154             template <typename Q>
155             node( Q&& key, int nHeight, version_type version, node * pParent, node * pLeft, node * pRight )
156                 : base_class( nHeight, version, pParent, pLeft, pRight )
157                 , m_key( std::forward<Q>( key ) )
158                 , m_pNextRemoved( nullptr )
159             {}
160             //@endcond
161         };
162
163         /// BronsonAVLTreeMap internal statistics
164         template <typename Counter = cds::atomicity::event_counter>
165         struct stat {
166             typedef Counter   event_counter; ///< Event counter type
167
168             event_counter   m_nFindSuccess; ///< Count of success \p find() call
169             event_counter   m_nFindFailed;  ///< Count of failed \p find() call
170             event_counter   m_nFindRetry;   ///< Count of retries during \p find()
171             event_counter   m_nFindWaitShrinking;   ///< Count of waiting until shrinking completed duting \p find() call
172
173             event_counter   m_nInsertSuccess;       ///< Count of inserting data node
174             event_counter   m_nRelaxedInsertFailed; ///< Count of false creating of data nodes (only if @ref bronson_avltree::relaxed_insert "relaxed insertion" is enabled)
175             event_counter   m_nInsertRetry;         ///< Count of insert retries via concurrent operations
176             event_counter   m_nUpdateWaitShrinking; ///< Count of waiting until shrinking completed during \p update() call
177             event_counter   m_nUpdateRetry;         ///< Count of update retries via concurrent operations
178             event_counter   m_nUpdateRootWaitShrinking;  ///< Count of waiting until root shrinking completed duting \p update() call
179             event_counter   m_nUpdateSuccess;       ///< Count of updating data node
180             event_counter   m_nUpdateUnlinked;      ///< Count of attempts to update unlinked node
181             event_counter   m_nDisposedNode;        ///< Count of disposed node
182             event_counter   m_nDisposedValue;       ///< Count of disposed value
183             event_counter   m_nExtractedValue;      ///< Count of extracted value
184             event_counter   m_nRemoveRetry;         ///< Count o erase/extract retries
185             event_counter   m_nRemoveWaitShrinking; ///< ount of waiting until shrinking completed during \p erase() or \p extract() call
186             event_counter   m_nRemoveRootWaitShrinking;  ///< Count of waiting until root shrinking completed duting \p erase() or \p extract() call
187
188             event_counter   m_nRightRotation;       ///< Count of single right rotation
189             event_counter   m_nLeftRotation;        ///< Count of single left rotation
190             event_counter   m_nLeftRightRotation;   ///< Count of double left-over-right rotation
191             event_counter   m_nRightLeftRotation;   ///< Count of double right-over-left rotation
192
193             event_counter   m_nInsertRebalanceReq;  ///< Count of rebalance required after inserting
194             event_counter   m_nRemoveRebalanceReq;  ///< Count of rebalance required after removing
195
196             //@cond
197             void onFindSuccess()        { ++m_nFindSuccess      ; }
198             void onFindFailed()         { ++m_nFindFailed       ; }
199             void onFindRetry()          { ++m_nFindRetry        ; }
200             void onFindWaitShrinking()  { ++m_nFindWaitShrinking; }
201
202             void onInsertSuccess()          { ++m_nInsertSuccess    ; }
203             void onRelaxedInsertFailed()    { ++m_nRelaxedInsertFailed; }
204             void onInsertRetry()            { ++m_nInsertRetry      ; }
205             void onUpdateWaitShrinking()    { ++m_nUpdateWaitShrinking; }
206             void onUpdateRetry()            { ++m_nUpdateRetry; }
207             void onUpdateRootWaitShrinking() { ++m_nUpdateRootWaitShrinking; }
208             void onUpdateSuccess()          { ++m_nUpdateSuccess;  }
209             void onUpdateUnlinked()         { ++m_nUpdateUnlinked; }
210             void onDisposeNode()            { ++m_nDisposedNode; }
211             void onDisposeValue()           { ++m_nDisposedValue; }
212             void onExtractValue()           { ++m_nExtractedValue; }
213             void onRemoveRetry()            { ++m_nRemoveRetry; }
214             void onRemoveWaitShrinking()    { ++m_nRemoveWaitShrinking; }
215             void onRemoveRootWaitShrinking() { ++m_nRemoveRootWaitShrinking; }
216
217             void onRotateRight()            { ++m_nRightRotation; }
218             void onRotateLeft()             { ++m_nLeftRotation; }
219             void onRotateRightOverLeft()    { ++m_nRightLeftRotation; }
220             void onRotateLeftOverRight()    { ++m_nLeftRightRotation; }
221
222             void onInsertRebalanceRequired() { ++m_nInsertRebalanceReq; }
223             void onRemoveRebalanceRequired() { ++m_nRemoveRebalanceReq; }
224             //@endcond
225         };
226
227         /// BronsonAVLTreeMap empty statistics
228         struct empty_stat {
229             //@cond
230             void onFindSuccess()        const {}
231             void onFindFailed()         const {}
232             void onFindRetry()          const {}
233             void onFindWaitShrinking()  const {}
234
235             void onInsertSuccess()          const {}
236             void onRelaxedInsertFailed()    const {}
237             void onInsertRetry()            const {}
238             void onUpdateWaitShrinking()    const {}
239             void onUpdateRetry()            const {}
240             void onUpdateRootWaitShrinking() const {}
241             void onUpdateSuccess()          const {}
242             void onUpdateUnlinked()         const {}
243             void onDisposeNode()            const {}
244             void onDisposeValue()           const {}
245             void onExtractValue()           const {}
246             void onRemoveRetry()            const {}
247             void onRemoveWaitShrinking()    const {}
248             void onRemoveRootWaitShrinking() const {}
249
250             void onRotateRight()            const {}
251             void onRotateLeft()             const {}
252             void onRotateRightOverLeft()    const {}
253             void onRotateLeftOverRight()    const {}
254
255             void onInsertRebalanceRequired() const {}
256             void onRemoveRebalanceRequired() const {}
257             //@endcond
258         };
259
260         /// Option to allow relaxed insert into \ref cds_container_BronsonAVLTreeMap_rcu "Bronson et al AVL-tree"
261         /**
262             By default, this option is disabled and the new node is created under its parent lock.
263             In this case, it is guaranteed the new node will be attached to its parent.
264             On the other hand, constructing of the new node can be too complex to make it under the lock,
265             that can lead to lock contention.
266
267             When this option is enabled, the new node is created before locking the parent node.
268             After that, the parent is locked and checked whether the new node may be attached to the parent.
269             In this case, false node creating can be performed, but locked section can be significantly small.
270         */
271         template <bool Enable>
272         struct relaxed_insert {
273             //@cond
274             template <typename Base> struct pack : public Base
275             {
276                 enum { relaxed_insert = Enable };
277             };
278             //@endcond
279         };
280
281         /// \p BronsonAVLTreeMap traits
282         /**
283             Note that there are two main specialization of Bronson et al AVL-tree:
284             - \ref cds_container_BronsonAVLTreeMap_rcu_ptr "pointer-oriented" - the tree node stores an user-provided pointer to value
285             - \ref cds_container_BronsonAVLTreeMap_rcu "data-oriented" - the tree node contains a copy of values
286
287             Depends on tree specialization, different traits member can be used.
288         */
289         struct traits
290         {
291             /// Key comparison functor
292             /**
293                 No default functor is provided. If the option is not specified, the \p less is used.
294
295                 See \p cds::opt::compare option description for functor interface.
296
297                 You should provide \p compare or \p less functor.
298             */
299             typedef opt::none                       compare;
300
301             /// Specifies binary predicate used for key compare.
302             /**
303                 See \p cds::opt::less option description for predicate interface.
304
305                 You should provide \p compare or \p less functor.
306             */
307             typedef opt::none                       less;
308
309             /// Allocator for internal node
310             typedef CDS_DEFAULT_ALLOCATOR           node_allocator;
311
312             /// Allocator for node's value (not used in \p BronsonAVLTreeMap<RCU, Key, T*, Traits> specialisation)
313             typedef CDS_DEFAULT_ALLOCATOR           allocator;
314
315             /// Disposer (only for pointer-oriented tree specialization)
316             /**
317                 The functor used for dispose removed values.
318                 The user-provided disposer is used only for pointer-oriented tree specialization
319                 like \p BronsonAVLTreeMap<GC, Key, T*, Traits>. When the node becomes the routing node without value,
320                 the disposer will be called to signal that the memory for the value can be safely freed.
321                 Default is \ref cds::intrusive::opt::delete_disposer "cds::container::opt::v::delete_disposer<>" which calls \p delete operator.
322             */
323             typedef opt::v::delete_disposer<>       disposer;
324
325             /// @ref cds_sync_monitor "Synchronization monitor" type for node-level locking
326             typedef cds::sync::injecting_monitor<cds::sync::spin>  sync_monitor;
327
328             /// Enable relaxed insertion.
329             /**
330                 About relaxed insertion see \p bronson_avltree::relaxed_insert option.
331                 By default, this option is disabled.
332             */
333             static bool const relaxed_insert = false;
334
335             /// Item counter
336             /**
337                 The type for item counter, by default it is disabled (\p atomicity::empty_item_counter).
338                 To enable it use \p atomicity::item_counter
339             */
340             typedef atomicity::empty_item_counter     item_counter;
341
342             /// C++ memory ordering model
343             /**
344                 List of available memory ordering see \p opt::memory_model
345             */
346             typedef opt::v::relaxed_ordering        memory_model;
347
348             /// Internal statistics
349             /**
350                 By default, internal statistics is disabled (\p ellen_bintree::empty_stat).
351                 To enable it use \p ellen_bintree::stat.
352             */
353             typedef empty_stat                      stat;
354
355             /// Back-off strategy
356             typedef cds::backoff::empty             back_off;
357
358             /// RCU deadlock checking policy (only for \ref cds_container_BronsonAVLTreeMap_rcu "RCU-based BronsonAVLTree")
359             /**
360                 List of available options see \p opt::rcu_check_deadlock
361             */
362             typedef cds::opt::v::rcu_throw_deadlock      rcu_check_deadlock;
363         };
364
365         /// Metafunction converting option list to BronsonAVLTreeMap traits
366         /**
367             Note that there are two main specialization of Bronson et al AVL-tree:
368             - \ref cds_container_BronsonAVLTreeMap_rcu_ptr "pointer-oriented" - the tree node stores an user-provided pointer to value
369             - \ref cds_container_BronsonAVLTreeMap_rcu "data-oriented" - the tree node contains a copy of values
370
371             Depends on tree specialization, different options can be specified.
372
373             \p Options are:
374             - \p opt::compare - key compare functor. No default functor is provided.
375                 If the option is not specified, \p %opt::less is used.
376             - \p opt::less - specifies binary predicate used for key compare. At least \p %opt::compare or \p %opt::less should be defined.
377             - \p opt::node_allocator - the allocator for internal nodes. Default is \ref CDS_DEFAULT_ALLOCATOR.
378             - \p opt::allocator - the allocator for node's value. Default is \ref CDS_DEFAULT_ALLOCATOR.
379                 This option is not used in \p BronsonAVLTreeMap<RCU, Key, T*, Traits> specialisation
380             - \ref cds::intrusive::opt::disposer "container::opt::disposer" - the functor used for dispose removed values.
381                 The user-provided disposer is used only for pointer-oriented tree specialization
382                 like \p BronsonAVLTreeMap<GC, Key, T*, Traits>. When the node becomes the rounting node without value,
383                 the disposer will be called to signal that the memory for the value can be safely freed.
384                 Default is \ref cds::intrusive::opt::delete_disposer "cds::container::opt::v::delete_disposer<>" which calls \p delete operator.
385                 Due the nature of GC schema the disposer may be called asynchronously.
386             - \p opt::sync_monitor -  @ref cds_sync_monitor "synchronization monitor" type for node-level locking,
387                 default is \p cds::sync::injecting_monitor<cds::sync::spin>
388             - \p bronson_avltree::relaxed_insert - enable (\p true) or disable (\p false, the default) 
389                 @ref bronson_avltree::relaxed_insert "relaxed insertion"
390             - \p opt::item_counter - the type of item counting feature, by default it is disabled (\p atomicity::empty_item_counter)
391                 To enable it use \p atomicity::item_counter
392             - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
393                 or \p opt::v::sequential_consistent (sequentially consisnent memory model).
394             - \p opt::stat - internal statistics, by default it is disabled (\p bronson_avltree::empty_stat)
395                 To enable statistics use \p \p bronson_avltree::stat
396             - \p opt::backoff - back-off strategy, by default no strategy is used (\p cds::backoff::empty)
397             - \p opt::rcu_check_deadlock - a deadlock checking policy for RCU-based tree, default is \p opt::v::rcu_throw_deadlock
398         */
399         template <typename... Options>
400         struct make_traits {
401 #   ifdef CDS_DOXYGEN_INVOKED
402             typedef implementation_defined type ;   ///< Metafunction result
403 #   else
404             typedef typename cds::opt::make_options<
405                 typename cds::opt::find_type_traits< traits, Options... >::type
406                 ,Options...
407             >::type   type;
408 #   endif
409         };
410     } // namespace bronson_avltree
411
412     // Forwards
413     template < class GC, typename Key, typename T, class Traits = bronson_avltree::traits >
414     class BronsonAVLTreeMap;
415
416 }} // namespace cds::container
417
418
419 #endif // #ifndef CDSLIB_CONTAINER_DETAILS_BRONSON_AVLTREE_BASE_H