Commit acb6cf6b by Danila Klimenko

chromatic_tree: updated documentation, fixed test cases for MSVC

parent 2f75565c
...@@ -90,6 +90,132 @@ ChromaticTreeNode<Key, Value>::GetRight() { ...@@ -90,6 +90,132 @@ ChromaticTreeNode<Key, Value>::GetRight() {
return right_; return right_;
} }
template<typename Key, typename Value>
void ChromaticTreeNode<Key, Value>::Retire() {
retired_ = true;
}
template<typename Key, typename Value>
bool ChromaticTreeNode<Key, Value>::IsRetired() const {
return retired_;
}
template<typename Key, typename Value>
embb::base::Mutex& ChromaticTreeNode<Key, Value>::GetMutex() {
return mutex_;
}
template<typename GuardedType>
UniqueHazardPointer<GuardedType>::
UniqueHazardPointer()
: hazard_guard_(NULL), undefined_guard_(NULL), active_(false) {}
template<typename GuardedType>
UniqueHazardPointer<GuardedType>::
UniqueHazardPointer(AtomicGuard& hazard_guard, GuardedPtr undefined_guard)
: hazard_guard_(&hazard_guard),
undefined_guard_(undefined_guard),
active_(LoadGuardedPointer() == undefined_guard_) {}
template<typename GuardedType>
UniqueHazardPointer<GuardedType>::
~UniqueHazardPointer() {
if (IsActive()) ClearHazard();
}
template<typename GuardedType>
bool UniqueHazardPointer<GuardedType>::
ProtectHazard(const AtomicGuard& hazard) {
assert(OwnsHazardGuard());
// Read the hazard and store it into the guard
StoreGuardedPointer(hazard.Load());
// Check whether the guard is valid
SetActive(LoadGuardedPointer() == hazard.Load());
// Clear the guard if it is invalid
if (!IsActive()) ClearHazard();
return IsActive();
}
template<typename GuardedType>
UniqueHazardPointer<GuardedType>::operator GuardedPtr () const {
assert(IsActive());
return LoadGuardedPointer();
}
template<typename GuardedType>
typename UniqueHazardPointer<GuardedType>::GuardedPtr
UniqueHazardPointer<GuardedType>::operator->() const {
assert(IsActive());
return LoadGuardedPointer();
}
template<typename GuardedType>
GuardedType& UniqueHazardPointer<GuardedType>::operator*() const {
assert(IsActive());
return *(LoadGuardedPointer());
}
template<typename GuardedType>
void UniqueHazardPointer<GuardedType>::
AdoptGuard(const UniqueHazardPointer<GuardedType>& other) {
assert(OwnsHazardGuard());
StoreGuardedPointer(other.LoadGuardedPointer());
SetActive(other.active_);
}
template<typename GuardedType>
void UniqueHazardPointer<GuardedType>::
Swap(UniqueHazardPointer<GuardedType>& other) {
std::swap(hazard_guard_, other.hazard_guard_);
std::swap(undefined_guard_, other.undefined_guard_);
std::swap(active_, other.active_);
}
template<typename GuardedType>
typename UniqueHazardPointer<GuardedType>::GuardedPtr
UniqueHazardPointer<GuardedType>::ReleaseHazard() {
assert(IsActive());
GuardedPtr released_hazard = LoadGuardedPointer();
ClearHazard();
SetActive(false);
return released_hazard;
}
template<typename GuardedType>
bool UniqueHazardPointer<GuardedType>::IsActive() const {
return active_;
}
template<typename GuardedType>
void UniqueHazardPointer<GuardedType>::SetActive(bool active) {
active_ = active;
}
template<typename GuardedType>
void UniqueHazardPointer<GuardedType>::ClearHazard() {
StoreGuardedPointer(undefined_guard_);
}
template<typename GuardedType>
typename UniqueHazardPointer<GuardedType>::GuardedPtr
UniqueHazardPointer<GuardedType>::LoadGuardedPointer() const {
return hazard_guard_->Load();
}
template<typename GuardedType>
void UniqueHazardPointer<GuardedType>::StoreGuardedPointer(GuardedPtr ptr) {
hazard_guard_->Store(ptr);
}
template<typename GuardedType>
bool UniqueHazardPointer<GuardedType>::OwnsHazardGuard() const {
return hazard_guard_ != NULL;
}
} // namespace internal } // namespace internal
...@@ -314,7 +440,7 @@ TryDelete(const Key& key, Value& old_value) { ...@@ -314,7 +440,7 @@ TryDelete(const Key& key, Value& old_value) {
template<typename Key, typename Value, typename Compare, typename NodePool> template<typename Key, typename Value, typename Compare, typename NodePool>
size_t ChromaticTree<Key, Value, Compare, NodePool>:: size_t ChromaticTree<Key, Value, Compare, NodePool>::
GetCapacity() { GetCapacity() const {
return capacity_; return capacity_;
} }
...@@ -326,8 +452,9 @@ GetUndefinedValue() { ...@@ -326,8 +452,9 @@ GetUndefinedValue() {
template<typename Key, typename Value, typename Compare, typename NodePool> template<typename Key, typename Value, typename Compare, typename NodePool>
bool ChromaticTree<Key, Value, Compare, NodePool>:: bool ChromaticTree<Key, Value, Compare, NodePool>::
IsEmpty() { IsEmpty() const {
return IsLeaf(entry_->GetLeft()); NodePtr entry = entry_; //Bug: "operator->()" is not const in AtomicPointer<>
return IsLeaf(entry->GetLeft());
} }
template<typename Key, typename Value, typename Compare, typename NodePool> template<typename Key, typename Value, typename Compare, typename NodePool>
...@@ -466,6 +593,15 @@ IsBalanced(const NodePtr& node) const { ...@@ -466,6 +593,15 @@ IsBalanced(const NodePtr& node) const {
template<typename Key, typename Value, typename Compare, typename NodePool> template<typename Key, typename Value, typename Compare, typename NodePool>
void ChromaticTree<Key, Value, Compare, NodePool>:: void ChromaticTree<Key, Value, Compare, NodePool>::
RetireHazardousNode(HazardNodePtr& node, UniqueLock& node_lock) {
node->Retire();
node_lock.Unlock();
NodePtr node_to_delete = node.ReleaseHazard();
node_hazard_manager_.EnqueuePointerForDeletion(node_to_delete);
}
template<typename Key, typename Value, typename Compare, typename NodePool>
void ChromaticTree<Key, Value, Compare, NodePool>::
FreeNode(NodePtr node) { FreeNode(NodePtr node) {
#ifdef EMBB_DEBUG #ifdef EMBB_DEBUG
node->GetLeft() = reinterpret_cast<NodePtr>(INVALID_POINTER); node->GetLeft() = reinterpret_cast<NodePtr>(INVALID_POINTER);
......
...@@ -110,11 +110,29 @@ class ChromaticTreeNode { ...@@ -110,11 +110,29 @@ class ChromaticTreeNode {
*/ */
AtomicChildPointer& GetRight(); AtomicChildPointer& GetRight();
void Retire() { retired_ = true; } /**
bool IsRetired() const { return retired_; } * Marks node for deletion from the tree
embb::base::Mutex& GetMutex() { return mutex_; } */
void Retire();
/**
* Checks whether the node is marked for deletion from the tree
*
* \return \c true if node is retired, \c false otherwise
*/
bool IsRetired() const;
/**
* Accessor for the FGL mutex
*
* \return Reference to this node's mutex
*/
embb::base::Mutex& GetMutex();
private: private:
/**
* Disable copy construction and assignment.
*/
ChromaticTreeNode(const ChromaticTreeNode&); ChromaticTreeNode(const ChromaticTreeNode&);
ChromaticTreeNode& operator=(const ChromaticTreeNode&); ChromaticTreeNode& operator=(const ChromaticTreeNode&);
...@@ -123,77 +141,175 @@ class ChromaticTreeNode { ...@@ -123,77 +141,175 @@ class ChromaticTreeNode {
const int weight_; /**< Weight of the node */ const int weight_; /**< Weight of the node */
AtomicChildPointer left_; /**< Pointer to left child node */ AtomicChildPointer left_; /**< Pointer to left child node */
AtomicChildPointer right_; /**< Pointer to right child node */ AtomicChildPointer right_; /**< Pointer to right child node */
embb::base::Atomic<bool> retired_; /**< Retired (marked for deletion) flag */
embb::base::Atomic<bool> retired_; embb::base::Mutex mutex_; /**< Fine-grained locking tree: per node mutex */
embb::base::Mutex mutex_;
}; };
/**
* Ownership wrapper for a hazard pointer
*
* Uses an entry of the hazard table to provide protection for a single
* hazardous pointer. While providing standard pointer dereference and member
* access operators, it requires special care for pointer assignment (realized
* via 'ProtectHazard' method).
* On destruction, it clears the wrapped hazard table entry, releasing the
* protected hazardous pointer (if any).
*
* \tparam GuardedType Type of the object to be protected by the hazard pointer
*/
template<typename GuardedType> template<typename GuardedType>
class UniqueHazardGuard { class UniqueHazardPointer {
public: public:
typedef embb::base::Atomic<GuardedType> AtomicGuard; /**
* Typedef for a pointer to the guarded object.
*/
typedef GuardedType* GuardedPtr;
/**
* Typedef for a atomic pointer to the guarded object.
*/
typedef embb::base::Atomic<GuardedPtr> AtomicGuard;
UniqueHazardGuard(AtomicGuard& guard, const GuardedType& undefined_guard) /**
: guard_(guard), * Creates an uninitialized, empty wrapper.
undefined_guard_(undefined_guard), *
active_(guard_.Load() == undefined_guard_) {} * An uninitialized wrapper may only be swapped with another wrapper (using
* \c Swap() method) or checked for being active (using 'IsActive()' method,
* which should always return /c false for an uninitialized wrapper).
*/
UniqueHazardPointer();
~UniqueHazardGuard() { /**
if (active_) SetUndefinedGuard(); * Creates a wrapper that uses the given hazard table entry (referred to as
} * "guard") to protect hazardous pointers.
*
* \param[IN] hazard_guard Reference to a hazard table entry
* \param[IN] undefined_guard Dummy value used to clear the hazard table entry
*/
explicit
UniqueHazardPointer(AtomicGuard& hazard_guard,
GuardedPtr undefined_guard = NULL);
bool ProtectHazard(const AtomicGuard& hazard) { /**
// Read the hazard and store it into the guard * If initialized and active, clears the hazard table entry.
guard_ = hazard.Load(); */
~UniqueHazardPointer();
// Check whether the guard is valid /**
active_ = (guard_.Load() == hazard.Load()); * Tries to protect the given hazard using the wrapped hazard pointer (guard).
* If it succeeds, the hazard may be safely dereferenced as long as the guard
* is not destroyed or reset to protect another hazard.
*
* \param hazard The hazard to be protected
* \return \c true if the specified hazard is now protected by the guard,
* \c false if the hazard was modified by a concurrent thread
*/
bool ProtectHazard(const AtomicGuard& hazard);
// Clear the guard if it is invalid /**
if (!active_) SetUndefinedGuard(); * Type cast operator.
*
* \return The hazardous pointer protected by this wrapper
*/
operator GuardedPtr () const;
return active_; /**
} * Pointer member access operator.
*
* \return The hazardous pointer protected by this wrapper
*/
GuardedPtr operator->() const;
bool IsActive() const { /**
return active_; * Pointer dereference operator.
} *
* \return Reference to the object pointed to by the protected pointer
*/
GuardedType& operator*() const;
operator GuardedType () const { /**
assert(active_ == true); * Protects the hazard that is currently protected by another wrapper (so it
return guard_.Load(); * becomes protected by two guards simultaneously). The other wrapper remains
} * unmodified.
*
* \param other Another wrapper those protected pointer is to be protected by
* the calling wrapper
*/
void AdoptGuard(const UniqueHazardPointer<GuardedType>& other);
GuardedType operator->() const { /**
assert(active_ == true); * Swaps the guard ownership with another wrapper. Swaps not just the
return guard_.Load(); * protected hazards, but the hazard guards themselves.
} *
* \param other Another wrapper to swap guards with
*/
void Swap(UniqueHazardPointer<GuardedType>& other);
void AdoptGuard(const UniqueHazardGuard<GuardedType>& other) { /**
guard_ = other.guard_.Load(); * Clears the hazard guard and returns the hazard previously protected by that
active_ = other.active_; * guard.
} *
* \return The hazardous pointer previously protected by this wrapper
*/
GuardedPtr ReleaseHazard();
GuardedType ReleaseHazard() { /**
assert(active_ == true); * Check whether the wrapper is active.
GuardedType released_hazard = guard_.Load(); *
SetUndefinedGuard(); * \return \c true if the wrapper is initialized and currently protecting some
active_ = false; * hazard, \c false otherwise
return released_hazard; */
} bool IsActive() const;
private: private:
void SetUndefinedGuard() { /**
guard_ = undefined_guard_; * Sets the 'active' flag of this wrapper.
} *
* \param active The new value for the flag
*/
void SetActive(bool active);
/**
* Reset the wrapped hazard guard to a state when it is not protecting any
* hazards.
*/
void ClearHazard();
// Non-copyable /**
UniqueHazardGuard(const UniqueHazardGuard<GuardedType>&); * Retrieves the hazardous pointer currently protected by the wrapped guard.
UniqueHazardGuard<GuardedType>& *
operator=(const UniqueHazardGuard<GuardedType>&); * \return The hazardous pointer protected by this wrapper
*/
GuardedPtr LoadGuardedPointer() const;
/**
* Updates the wrapped guard to protect the specified hazardous pointer.
*
* \param ptr Hazardous pointer to be protected
*/
void StoreGuardedPointer(GuardedPtr ptr);
/**
* Check whether the wrapper is initialized (i.e. it wraps some hazard guard)
*
* \return \c true if this wrapper is initialized, \c false otherwise
*/
bool OwnsHazardGuard() const;
AtomicGuard& guard_; /**
GuardedType undefined_guard_; * Disable copy construction and assignment.
*/
UniqueHazardPointer(const UniqueHazardPointer&);
UniqueHazardPointer& operator=(const UniqueHazardPointer&);
/**
* Pointer to a hazard table entry (the guard) that is used to store the
* hazardous pointers
*/
AtomicGuard* hazard_guard_;
/** Dummy value used to clear the hazard guard from any hazards */
GuardedPtr undefined_guard_;
/** Flag set to true when the guard is protecting some hazardous pointer */
bool active_; bool active_;
}; };
...@@ -318,7 +434,7 @@ class ChromaticTree { ...@@ -318,7 +434,7 @@ class ChromaticTree {
* \param[IN] key Key to be removed * \param[IN] key Key to be removed
* *
* \return \c true if the given key-value pair was successfully deleted from * \return \c true if the given key-value pair was successfully deleted from
* the tree, \c false if the tree is out of memory * the tree, \c false if there is not enough memory
*/ */
bool TryDelete(const Key& key); bool TryDelete(const Key& key);
...@@ -332,7 +448,7 @@ class ChromaticTree { ...@@ -332,7 +448,7 @@ class ChromaticTree {
* tree for the given key * tree for the given key
* *
* \return \c true if the given key-value pair was successfully deleted from * \return \c true if the given key-value pair was successfully deleted from
* the tree, \c false if the tree is out of memory * the tree, \c false if there is not enough memory
*/ */
bool TryDelete(const Key& key, Value& old_value); bool TryDelete(const Key& key, Value& old_value);
...@@ -342,7 +458,7 @@ class ChromaticTree { ...@@ -342,7 +458,7 @@ class ChromaticTree {
* *
* \return Number of key-value pairs the tree can store * \return Number of key-value pairs the tree can store
*/ */
size_t GetCapacity(); size_t GetCapacity() const;
/** /**
* Accessor for the dummy value used by the tree * Accessor for the dummy value used by the tree
...@@ -356,7 +472,7 @@ class ChromaticTree { ...@@ -356,7 +472,7 @@ class ChromaticTree {
* *
* \return \c true if the tree stores no key-value pairs, \c false otherwise * \return \c true if the tree stores no key-value pairs, \c false otherwise
*/ */
bool IsEmpty(); bool IsEmpty() const;
private: private:
/** /**
...@@ -371,8 +487,13 @@ class ChromaticTree { ...@@ -371,8 +487,13 @@ class ChromaticTree {
* Typedef for an atomic pointer to a node of the tree. * Typedef for an atomic pointer to a node of the tree.
*/ */
typedef embb::base::Atomic<NodePtr> AtomicNodePtr; typedef embb::base::Atomic<NodePtr> AtomicNodePtr;
/**
typedef internal::UniqueHazardGuard<NodePtr> HazardNodePtr; * Typedef for an pointer to a node protected by a Hazard Pointer.
*/
typedef internal::UniqueHazardPointer<Node> HazardNodePtr;
/**
* Typedef for the UniqueLock class.
*/
typedef embb::base::UniqueLock<embb::base::Mutex> UniqueLock; typedef embb::base::UniqueLock<embb::base::Mutex> UniqueLock;
...@@ -474,15 +595,30 @@ class ChromaticTree { ...@@ -474,15 +595,30 @@ class ChromaticTree {
*/ */
int GetHeight(const NodePtr& node) const; int GetHeight(const NodePtr& node) const;
/**
* Check whether the tree is currently in a balanced state (if it is a valid
* red-black tree).
*
* \return \c true if the tree is balanced, \c false otherwise
*/
bool IsBalanced() const; bool IsBalanced() const;
/**
* Check whether a subtree rooted at the given node is balanced.
*
* \param[IN] node Root of the subtree for which the balance is checked
*
* \return \c true if the tree is balanced, \c false otherwise
*/
bool IsBalanced(const NodePtr& node) const; bool IsBalanced(const NodePtr& node) const;
void RetireHazardousNode(HazardNodePtr& node, UniqueLock& node_lock) { /**
node->Retire(); * Free a tree node using the Hazard Pointers memory reclamation routines.
node_lock.Unlock(); *
NodePtr node_to_delete = node.ReleaseHazard(); * \param[IN] node A node to be freed.
node_hazard_manager_.EnqueuePointerForDeletion(node_to_delete); * \param[IN] node_lock A lock holding the mutex of the \c node to be freed.
} */
void RetireHazardousNode(HazardNodePtr& node, UniqueLock& node_lock);
/** /**
* Free a tree node by returning it to the node pool. * Free a tree node by returning it to the node pool.
......
...@@ -127,7 +127,8 @@ void TreeTest<Tree>::Worker:: ...@@ -127,7 +127,8 @@ void TreeTest<Tree>::Worker::
ReplaceHalf() { ReplaceHalf() {
// Replace some of the elements that were inserted earlier // Replace some of the elements that were inserted earlier
::std::random_shuffle(elements_.begin(), elements_.end()); ::std::random_shuffle(elements_.begin(), elements_.end());
ElementIterator elements_middle = elements_.begin() + elements_.size() / 2; ElementIterator elements_middle = elements_.begin() +
static_cast<ptrdiff_t>(elements_.size() / 2);
for (ElementIterator it = elements_.begin(); it != elements_middle; ++it) { for (ElementIterator it = elements_.begin(); it != elements_middle; ++it) {
Value old_value; Value old_value;
Value expected = it->second; Value expected = it->second;
...@@ -166,7 +167,8 @@ void TreeTest<Tree>::Worker:: ...@@ -166,7 +167,8 @@ void TreeTest<Tree>::Worker::
DeleteHalf() { DeleteHalf() {
// Delete half of the elements from the tree // Delete half of the elements from the tree
::std::random_shuffle(elements_.begin(), elements_.end()); ::std::random_shuffle(elements_.begin(), elements_.end());
ElementIterator elements_middle = elements_.begin() + elements_.size() / 2; ElementIterator elements_middle = elements_.begin() +
static_cast<ptrdiff_t>(elements_.size() / 2);
for (ElementIterator it = elements_.begin(); it != elements_middle; ++it) { for (ElementIterator it = elements_.begin(); it != elements_middle; ++it) {
Value old_value; Value old_value;
Value expected = it->second; Value expected = it->second;
...@@ -179,6 +181,7 @@ DeleteHalf() { ...@@ -179,6 +181,7 @@ DeleteHalf() {
template<typename Tree> template<typename Tree>
void TreeTest<Tree>:: void TreeTest<Tree>::
TreeTestInsertDelete_Pre() { TreeTestInsertDelete_Pre() {
embb_internal_thread_index_reset();
tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_); tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_);
} }
...@@ -220,6 +223,7 @@ TreeTestInsertDelete_Post() { ...@@ -220,6 +223,7 @@ TreeTestInsertDelete_Post() {
template<typename Tree> template<typename Tree>
void TreeTest<Tree>:: void TreeTest<Tree>::
TreeTestConcurrentGet_Pre() { TreeTestConcurrentGet_Pre() {
embb_internal_thread_index_reset();
tree_ = new Tree(TREE_CAPACITY / 2, bad_key_, bad_value_); tree_ = new Tree(TREE_CAPACITY / 2, bad_key_, bad_value_);
ElementVector elements; ElementVector elements;
...@@ -242,6 +246,7 @@ TreeTestConcurrentGet_Pre() { ...@@ -242,6 +246,7 @@ TreeTestConcurrentGet_Pre() {
template<typename Tree> template<typename Tree>
void TreeTest<Tree>:: void TreeTest<Tree>::
TreeTestConcurrentGetMinimal_Pre() { TreeTestConcurrentGetMinimal_Pre() {
embb_internal_thread_index_reset();
tree_ = new Tree(NUM_TEST_THREADS / 2, bad_key_, bad_value_); tree_ = new Tree(NUM_TEST_THREADS / 2, bad_key_, bad_value_);
for (int i = 0; i < NUM_TEST_THREADS / 2; ++i) { for (int i = 0; i < NUM_TEST_THREADS / 2; ++i) {
...@@ -300,6 +305,7 @@ TreeTestConcurrentGet_Post() { ...@@ -300,6 +305,7 @@ TreeTestConcurrentGet_Post() {
template<typename Tree> template<typename Tree>
void TreeTest<Tree>:: void TreeTest<Tree>::
TreeTestBalance_Pre() { TreeTestBalance_Pre() {
embb_internal_thread_index_reset();
tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_); tree_ = new Tree(TREE_CAPACITY, bad_key_, bad_value_);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment