2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #ifndef CDSLIB_INTRUSIVE_DETAILS_ELLEN_BINTREE_BASE_H
32 #define CDSLIB_INTRUSIVE_DETAILS_ELLEN_BINTREE_BASE_H
34 #include <type_traits>
35 #include <cds/intrusive/details/base.h>
36 #include <cds/opt/options.h>
37 #include <cds/urcu/options.h>
38 #include <cds/details/marked_ptr.h>
40 namespace cds { namespace intrusive {
42 /// EllenBinTree related declarations
43 namespace ellen_bintree {
45 template <class GC> struct base_node;
46 template <class GC, typename Tag = opt::none> struct node;
47 template <class GC, typename Key> struct internal_node;
51 Update descriptor is used internally for helping concurrent threads
52 to complete modifying operation.
53 Usually, you should not use \p update_desc type directly until
54 you want to develop special free-list of update descriptor.
57 - \p LeafNode - leaf node type, see \ref node
58 - \p InternalNode - internal node type, see \ref internal_node
60 @note Size of update descriptor is constant.
61 It does not depends of template arguments.
63 template <typename LeafNode, typename InternalNode>
66 typedef LeafNode leaf_node;
67 typedef InternalNode internal_node;
69 typedef cds::details::marked_ptr< update_desc, 3 > update_ptr;
79 internal_node * pParent;
85 internal_node * pGrandParent;
86 internal_node * pParent;
88 update_desc * pUpdateParent;
89 bool bDisposeLeaf; // true if pLeaf should be disposed, false otherwise (for extract operation, RCU)
99 update_desc * pNextRetire; // for local retired list (RCU)
102 : pNextRetire( nullptr )
108 struct alignas( void* ) basic_node
111 internal = 1, ///< set for internal node
112 key_infinite1 = 2, ///< set if node's key is Inf1
113 key_infinite2 = 4, ///< set if node's key is Inf2
115 key_infinite = key_infinite1 | key_infinite2 ///< Cumulative infinite flags
118 atomics::atomic<unsigned int> m_nFlags; ///< Internal flags
120 /// Constructs leaf (bIntrenal == false) or internal (bInternal == true) node
121 explicit basic_node( bool bInternal )
123 m_nFlags.store( bInternal ? internal: 0, atomics::memory_order_release );
126 /// Checks if the node is a leaf
129 return !is_internal();
132 /// Checks if the node is internal
133 bool is_internal() const
135 return (m_nFlags.load(atomics::memory_order_acquire) & internal) != 0;
138 /// Returns infinite key, 0 if the node is not infinite
139 unsigned int infinite_key() const
141 return m_nFlags.load(atomics::memory_order_acquire) & key_infinite;
144 /// Sets infinite key for the node (for internal use only!!!)
145 void infinite_key( int nInf )
147 unsigned int nFlags = m_nFlags.load(atomics::memory_order_relaxed);
148 nFlags &= ~key_infinite;
151 nFlags |= key_infinite1;
154 nFlags |= key_infinite2;
162 m_nFlags.store( nFlags, atomics::memory_order_release );
167 struct base_node: public basic_node
169 typedef basic_node base_class;
171 typedef GC gc ; ///< Garbage collector
173 /// Constructs leaf (bIntrenal == false) or internal (bInternal == true) node
174 explicit base_node( bool bInternal )
175 : base_class( bInternal )
180 /// Ellen's binary tree leaf node
183 - \p GC - one of \ref cds_garbage_collector "garbage collector type"
184 - \p Tag - a \ref cds_intrusive_hook_tag "tag"
186 template <typename GC,
187 # ifdef CDS_DOXYGEN_INVOKED
188 typename Tag = opt::none
194 # ifndef CDS_DOXYGEN_INVOKED
195 : public base_node< GC >
199 typedef base_node< GC > base_class;
202 typedef GC gc; ///< Garbage collector
203 typedef Tag tag; ///< Tag
207 : base_class( false )
211 /// Ellen's binary tree internal node
215 - \p LeafNode - leaf node type
217 template <typename Key, typename LeafNode>
219 # ifndef CDS_DOXYGEN_INVOKED
220 : public base_node<typename LeafNode::gc>
224 typedef base_node<typename LeafNode::gc> base_class;
227 typedef Key key_type; ///< key type
228 typedef LeafNode leaf_node; ///< type of leaf node
229 typedef update_desc< leaf_node, internal_node > update_desc_type; ///< Update descriptor
230 typedef typename update_desc_type::update_ptr update_ptr; ///< Marked pointer to update descriptor
232 key_type m_Key; ///< Regular key
233 atomics::atomic<base_class *> m_pLeft; ///< Left subtree
234 atomics::atomic<base_class *> m_pRight; ///< Right subtree
235 atomics::atomic<update_ptr> m_pUpdate; ///< Update descriptor
237 atomics::atomic<uintptr_t> m_nEmptyUpdate; ///< ABA prevention for m_pUpdate, from 0..2^16 step 4
244 , m_pRight( nullptr )
245 , m_pUpdate( update_ptr())
247 m_nEmptyUpdate.store( 0, atomics::memory_order_release );
251 update_ptr null_update_desc()
253 return update_ptr( reinterpret_cast<update_desc_type *>( ((m_nEmptyUpdate.fetch_add(1, atomics::memory_order_relaxed) + 1 ) << 2) & 0xFFFF ));
256 base_class * get_child( bool bRight, atomics::memory_order mo ) const
258 return bRight ? m_pRight.load( mo ) : m_pLeft.load( mo );
263 /// Types of EllenBinTree node
265 This struct declares different \p %EllenBinTree node types.
266 It can be useful for simplifying \p %EllenBinTree node declaration in your application.
269 - \p GC - one of \ref cds_garbage_collector "garbage collector type"
271 - \p Tag - a \ref cds_intrusive_hook_tag "tag"
273 template <typename GC, typename Key, typename Tag = opt::none>
276 typedef node<GC, Tag> leaf_node_type; ///< Leaf node type
277 typedef internal_node<Key, leaf_node_type> internal_node_type; ///< Internal node type
278 typedef update_desc<leaf_node_type, internal_node_type> update_desc_type; ///< Update descriptor type
283 struct default_hook {
284 typedef undefined_gc gc;
285 typedef opt::none tag;
290 template < typename HookType, typename... Options>
293 typedef typename opt::make_options< default_hook, Options...>::type options;
294 typedef typename options::gc gc;
295 typedef typename options::tag tag;
296 typedef node<gc, tag> node_type;
297 typedef HookType hook_type;
304 - \p opt::gc - garbage collector
305 - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
307 template < typename... Options >
308 struct base_hook: public hook< opt::base_hook_tag, Options... >
313 \p MemberOffset defines offset in bytes of \ref node member into your structure.
314 Use \p offsetof macro to define \p MemberOffset
317 - \p opt::gc - garbage collector
318 - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
320 template < size_t MemberOffset, typename... Options >
321 struct member_hook: public hook< opt::member_hook_tag, Options... >
324 static CDS_CONSTEXPR const size_t c_nMemberOffset = MemberOffset;
330 \p NodeTraits defines type traits for node.
331 See \ref node_traits for \p NodeTraits interface description
334 - opt::gc - garbage collector
335 - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
337 template <typename NodeTraits, typename... Options >
338 struct traits_hook: public hook< opt::traits_hook_tag, Options... >
341 typedef NodeTraits node_traits;
345 /// Key extracting functor option setter
346 template <typename Type>
347 struct key_extractor {
349 template <typename Base> struct pack: public Base
351 typedef Type key_extractor;
356 /// Update descriptor allocator option setter
357 template <typename Type>
358 struct update_desc_allocator {
360 template <typename Base> struct pack: public Base
362 typedef Type update_desc_allocator;
367 /// EllenBinTree internal statistics
368 template <typename Counter = cds::atomicity::event_counter>
370 typedef Counter event_counter ; ///< Event counter type
372 event_counter m_nInternalNodeCreated ; ///< Total count of created internal node
373 event_counter m_nInternalNodeDeleted ; ///< Total count of deleted internal node
374 event_counter m_nUpdateDescCreated ; ///< Total count of created update descriptors
375 event_counter m_nUpdateDescDeleted ; ///< Total count of deleted update descriptors
377 event_counter m_nInsertSuccess ; ///< Count of success insertion
378 event_counter m_nInsertFailed ; ///< Count of failed insertion
379 event_counter m_nInsertRetries ; ///< Count of unsuccessful retries of insertion
380 event_counter m_nUpdateExist ; ///< Count of \p update() call for existed node
381 event_counter m_nUpdateNew ; ///< Count of \p update() call for new node
382 event_counter m_nUpdateRetries ; ///< Count of unsuccessful retries of ensuring
383 event_counter m_nEraseSuccess ; ///< Count of successful call of \p erase and \p unlink
384 event_counter m_nEraseFailed ; ///< Count of failed call of \p erase and \p unlink
385 event_counter m_nEraseRetries ; ///< Count of unsuccessful retries inside erasing/unlinking
386 event_counter m_nFindSuccess ; ///< Count of successful \p find call
387 event_counter m_nFindFailed ; ///< Count of failed \p find call
388 event_counter m_nExtractMinSuccess ; ///< Count of successful call of \p extract_min
389 event_counter m_nExtractMinFailed ; ///< Count of failed call of \p extract_min
390 event_counter m_nExtractMinRetries ; ///< Count of unsuccessful retries inside \p extract_min
391 event_counter m_nExtractMaxSuccess ; ///< Count of successful call of \p extract_max
392 event_counter m_nExtractMaxFailed ; ///< Count of failed call of \p extract_max
393 event_counter m_nExtractMaxRetries ; ///< Count of unsuccessful retries inside \p extract_max
394 event_counter m_nSearchRetry ; ///< How many times the deleting node was encountered while searching
396 event_counter m_nHelpInsert ; ///< The number of insert help from the other thread
397 event_counter m_nHelpDelete ; ///< The number of delete help from the other thread
398 event_counter m_nHelpMark ; ///< The number of delete help (mark phase) from the other thread
399 event_counter m_nHelpGuardSuccess ; ///< The number of successful guarding of update descriptor data
400 event_counter m_nHelpGuardFailed ; ///< The number of failed guarding of update descriptor data
403 void onInternalNodeCreated() { ++m_nInternalNodeCreated ; }
404 void onInternalNodeDeleted() { ++m_nInternalNodeDeleted ; }
405 void onUpdateDescCreated() { ++m_nUpdateDescCreated ; }
406 void onUpdateDescDeleted() { ++m_nUpdateDescDeleted ; }
407 void onInsertSuccess() { ++m_nInsertSuccess ; }
408 void onInsertFailed() { ++m_nInsertFailed ; }
409 void onInsertRetry() { ++m_nInsertRetries ; }
410 void onUpdateExist() { ++m_nUpdateExist ; }
411 void onUpdateNew() { ++m_nUpdateNew ; }
412 void onUpdateRetry() { ++m_nUpdateRetries ; }
413 void onEraseSuccess() { ++m_nEraseSuccess ; }
414 void onEraseFailed() { ++m_nEraseFailed ; }
415 void onEraseRetry() { ++m_nEraseRetries ; }
416 void onExtractMinSuccess() { ++m_nExtractMinSuccess ; }
417 void onExtractMinFailed() { ++m_nExtractMinFailed ; }
418 void onExtractMinRetry() { ++m_nExtractMinRetries ; }
419 void onExtractMaxSuccess() { ++m_nExtractMaxSuccess ; }
420 void onExtractMaxFailed() { ++m_nExtractMaxFailed ; }
421 void onExtractMaxRetry() { ++m_nExtractMaxRetries ; }
422 void onFindSuccess() { ++m_nFindSuccess ; }
423 void onFindFailed() { ++m_nFindFailed ; }
424 void onSearchRetry() { ++m_nSearchRetry ; }
425 void onHelpInsert() { ++m_nHelpInsert ; }
426 void onHelpDelete() { ++m_nHelpDelete ; }
427 void onHelpMark() { ++m_nHelpMark ; }
428 void onHelpGuardSuccess() { ++m_nHelpGuardSuccess ; }
429 void onHelpGuardFailed() { ++m_nHelpGuardFailed ; }
433 /// EllenBinTree empty statistics
436 void onInternalNodeCreated() const {}
437 void onInternalNodeDeleted() const {}
438 void onUpdateDescCreated() const {}
439 void onUpdateDescDeleted() const {}
440 void onInsertSuccess() const {}
441 void onInsertFailed() const {}
442 void onInsertRetry() const {}
443 void onUpdateExist() const {}
444 void onUpdateNew() const {}
445 void onUpdateRetry() const {}
446 void onEraseSuccess() const {}
447 void onEraseFailed() const {}
448 void onEraseRetry() const {}
449 void onExtractMinSuccess() const {}
450 void onExtractMinFailed() const {}
451 void onExtractMinRetry() const {}
452 void onExtractMaxSuccess() const {}
453 void onExtractMaxFailed() const {}
454 void onExtractMaxRetry() const {}
455 void onFindSuccess() const {}
456 void onFindFailed() const {}
457 void onSearchRetry() const {}
458 void onHelpInsert() const {}
459 void onHelpDelete() const {}
460 void onHelpMark() const {}
461 void onHelpGuardSuccess() const {}
462 void onHelpGuardFailed() const {}
466 /// EllenBinTree traits
469 /// Hook used (mandatory)
471 Possible values are: \p ellen_bintree::base_hook, \p ellen_bintree::member_hook, \p ellen_bintree::traits_hook.
473 typedef base_hook<> hook;
475 /// Key extracting functor (mandatory)
477 You should explicit define a valid functor.
478 The functor has the following prototype:
480 struct key_extractor {
481 void operator ()( Key& dest, T const& src );
484 It should initialize \p dest key from \p src data.
485 The functor is used to initialize internal nodes.
487 typedef opt::none key_extractor;
489 /// Key comparison functor
491 No default functor is provided. If the option is not specified, the \p less is used.
493 See \p cds::opt::compare option description for functor interface.
495 You should provide \p compare or \p less functor.
496 See \ref cds_intrusive_EllenBinTree_rcu_less "predicate requirements".
498 typedef opt::none compare;
500 /// Specifies binary predicate used for key compare.
502 See \p cds::opt::less option description for predicate interface.
504 You should provide \p compare or \p less functor.
505 See \ref cds_intrusive_EllenBinTree_rcu_less "predicate requirements".
507 typedef opt::none less;
511 The functor used for dispose removed items. Default is \p opt::v::empty_disposer.
513 typedef opt::v::empty_disposer disposer;
517 The type for item counter, by default it is disabled (\p atomicity::empty_item_counter).
518 To enable it use \p atomicity::item_counter or \p atomicity::cache_friendly_item_counter
520 typedef atomicity::empty_item_counter item_counter;
522 /// C++ memory ordering model
524 List of available memory ordering see \p opt::memory_model
526 typedef opt::v::relaxed_ordering memory_model;
528 /// Allocator for update descriptors
530 The allocator type is used for \p ellen_bintree::update_desc.
532 Update descriptor is helping data structure with short lifetime and it is good candidate
533 for pooling. The number of simultaneously existing descriptors is bounded and it is
534 limited by number of threads working with the tree.
535 Therefore, a bounded lock-free container like \p cds::container::VyukovMPMCCycleQueue
536 is a good choice for the free-list of update descriptors,
537 see \p cds::memory::vyukov_queue_pool free-list implementation.
539 Also notice that size of update descriptor is constant and not dependent on the type of data
540 stored in the tree so single free-list object can be used for several \p EllenBinTree object.
542 typedef CDS_DEFAULT_ALLOCATOR update_desc_allocator;
544 /// Allocator for internal nodes
546 The allocator type is used for \p ellen_bintree::internal_node.
548 typedef CDS_DEFAULT_ALLOCATOR node_allocator;
550 /// Internal statistics
552 By default, internal statistics is disabled (\p ellen_bintree::empty_stat).
553 To enable it use \p ellen_bintree::stat.
555 typedef empty_stat stat;
557 /// Back-off strategy
558 typedef cds::backoff::empty back_off;
560 /// RCU deadlock checking policy (only for \ref cds_intrusive_EllenBinTree_rcu "RCU-based EllenBinTree")
562 List of available options see \p opt::rcu_check_deadlock
564 typedef cds::opt::v::rcu_throw_deadlock rcu_check_deadlock;
567 /// Metafunction converting option list to EllenBinTree traits
570 - \p opt::hook - hook used. Possible values are: \p ellen_bintree::base_hook, \p ellen_bintree::member_hook, \p ellen_bintree::traits_hook.
571 If the option is not specified, <tt>ellen_bintree::base_hook<></tt> is used.
572 - \p ellen_bintree::key_extractor - key extracting functor, mandatory option. The functor has the following prototype:
574 struct key_extractor {
575 void operator ()( Key& dest, T const& src );
578 It should initialize \p dest key from \p src data. The functor is used to initialize internal nodes.
579 - \p opt::compare - key compare functor. No default functor is provided.
580 If the option is not specified, \p %opt::less is used.
581 - \p opt::less - specifies binary predicate used for key compare. At least \p %opt::compare or \p %opt::less should be defined.
582 - \p opt::disposer - the functor used for dispose removed nodes. Default is \p opt::v::empty_disposer. Due the nature
583 of GC schema the disposer may be called asynchronously. The disposer is used only for leaf nodes.
584 - \p opt::item_counter - the type of item counting feature, by default it is disabled (\p atomicity::empty_item_counter)
585 To enable it use \p atomicity::item_counter or \p atomicity::cache_friendly_item_counter
586 - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
587 or \p opt::v::sequential_consistent (sequentially consisnent memory model).
588 - \p ellen_bintree::update_desc_allocator - an allocator of \ref ellen_bintree::update_desc "update descriptors",
589 default is \ref CDS_DEFAULT_ALLOCATOR.
590 Note that update descriptor is helping data structure with short lifetime and it is good candidate for pooling.
591 The number of simultaneously existing descriptors is bounded and depends on the number of threads
592 working with the tree and GC internals.
593 A bounded lock-free container like \p cds::container::VyukovMPMCCycleQueue is good candidate
594 for the free-list of update descriptors, see cds::memory::vyukov_queue_pool free-list implementation.
595 Also notice that size of update descriptor is constant and not dependent on the type of data
596 stored in the tree so single free-list object can be used for all \p %EllenBinTree objects.
597 - \p opt::node_allocator - the allocator for internal nodes. Default is \ref CDS_DEFAULT_ALLOCATOR.
598 - \p opt::stat - internal statistics, by default it is disabled (\p ellen_bintree::empty_stat)
599 To enable statistics use \p \p ellen_bintree::stat
600 - \p opt::backoff - back-off strategy, by default no strategy is used (\p cds::backoff::empty)
601 - \p opt::rcu_check_deadlock - a deadlock checking policy for RCU-based tree, default is \p opt::v::rcu_throw_deadlock
603 template <typename... Options>
605 # ifdef CDS_DOXYGEN_INVOKED
606 typedef implementation_defined type ; ///< Metafunction result
608 typedef typename cds::opt::make_options<
609 typename cds::opt::find_type_traits< traits, Options... >::type
618 template <typename Key, typename T, typename Compare, typename NodeTraits>
621 typedef Compare key_compare;
622 typedef Key key_type;
623 typedef T value_type;
624 typedef NodeTraits node_traits;
626 template <typename Q1, typename Q2>
627 int operator()( Q1 const& v1, Q2 const& v2) const
629 return key_compare()( v1, v2 );
632 template <typename LeafNode>
633 int operator()( internal_node<key_type, LeafNode> const& n1, internal_node<key_type, LeafNode> const& n2 ) const
635 if ( n1.infinite_key())
636 return n2.infinite_key() ? n1.infinite_key() - n2.infinite_key() : 1;
637 else if ( n2.infinite_key())
639 return operator()( n1.m_Key, n2.m_Key );
642 template <typename LeafNode, typename Q>
643 int operator()( internal_node<key_type, LeafNode> const& n, Q const& v ) const
645 if ( n.infinite_key())
647 return operator()( n.m_Key, v );
650 template <typename LeafNode, typename Q>
651 int operator()( Q const& v, internal_node<key_type, LeafNode> const& n ) const
653 if ( n.infinite_key())
655 return operator()( v, n.m_Key );
658 template <typename GC, typename Tag>
659 int operator()( node<GC, Tag> const& n1, node<GC, Tag> const& n2 ) const
661 if ( n1.infinite_key() != n2.infinite_key())
662 return n1.infinite_key() - n2.infinite_key();
663 return operator()( *node_traits::to_value_ptr( n1 ), *node_traits::to_value_ptr( n2 ));
666 template <typename GC, typename Tag, typename Q>
667 int operator()( node<GC, Tag> const& n, Q const& v ) const
669 if ( n.infinite_key())
671 return operator()( *node_traits::to_value_ptr( n ), v );
674 template <typename GC, typename Tag, typename Q>
675 int operator()( Q const& v, node<GC, Tag> const& n ) const
677 if ( n.infinite_key())
679 return operator()( v, *node_traits::to_value_ptr( n ));
682 template <typename GC>
683 int operator()( base_node<GC> const& n1, base_node<GC> const& n2 ) const
685 if ( n1.infinite_key() != n2.infinite_key())
686 return n1.infinite_key() - n2.infinite_key();
689 return operator()( node_traits::to_leaf_node( n1 ), node_traits::to_leaf_node( n2 ));
691 return operator()( node_traits::to_leaf_node( n1 ), node_traits::to_internal_node( n2 ));
695 return operator()( node_traits::to_internal_node( n1 ), node_traits::to_leaf_node( n2 ));
697 return operator()( node_traits::to_internal_node( n1 ), node_traits::to_internal_node( n2 ));
700 template <typename GC, typename Q>
701 int operator()( base_node<GC> const& n, Q const& v ) const
703 if ( n.infinite_key())
706 return operator()( node_traits::to_leaf_node( n ), v );
707 return operator()( node_traits::to_internal_node( n ), v );
710 template <typename GC, typename Q>
711 int operator()( Q const& v, base_node<GC> const& n ) const
713 return -operator()( n, v );
716 template <typename GC, typename LeafNode >
717 int operator()( base_node<GC> const& i, internal_node<key_type, LeafNode> const& n ) const
720 return operator()( static_cast<LeafNode const&>(i), n );
721 return operator()( static_cast<internal_node<key_type, LeafNode> const&>(i), n );
724 template <typename GC, typename LeafNode >
725 int operator()( internal_node<key_type, LeafNode> const& n, base_node<GC> const& i ) const
727 return -operator()( i, n );
730 template <typename GC, typename Tag >
731 int operator()( node<GC, Tag> const& n, internal_node<key_type, node<GC, Tag> > const& i ) const
733 if ( !n.infinite_key()) {
734 if ( i.infinite_key())
736 return operator()( n, i.m_Key );
739 if ( !i.infinite_key())
741 return int( n.infinite_key()) - int( i.infinite_key());
744 template <typename GC, typename Tag >
745 int operator()( internal_node<key_type, node<GC, Tag> > const& i, node<GC, Tag> const& n ) const
747 return -operator()( n, i );
752 } // namespace details
754 } // namespace ellen_bintree
757 template < class GC, typename Key, typename T, class Traits = ellen_bintree::traits >
760 }} // namespace cds::intrusive
762 #endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_ELLEN_BINTREE_BASE_H