/* * Copyright (c) 2014, Siemens AG. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR 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. */ #ifndef EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ #define EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_ namespace embb { namespace containers { namespace internal { template< typename ElementT > FixedSizeList::FixedSizeList(size_t max_size) : max_size(max_size), size(0) { elementsArray = new ElementT[max_size]; } template< typename ElementT > inline size_t FixedSizeList::GetSize() const { return size; } template< typename ElementT > inline size_t FixedSizeList::GetMaxSize() const { return max_size; } template< typename ElementT > inline void FixedSizeList::clear() { size = 0; } template< typename ElementT > typename FixedSizeList::iterator FixedSizeList::begin() const { return &elementsArray[0]; } template< typename ElementT > typename FixedSizeList::iterator FixedSizeList::end() const { return &elementsArray[size]; } template< typename ElementT > FixedSizeList< ElementT > & FixedSizeList::operator= (const FixedSizeList & other) { size = 0; if (max_size < other.size) { EMBB_THROW(embb::base::ErrorException, "Copy target to small"); } for (const_iterator it = other.begin(); it != other.end(); ++it) { PushBack(*it); } return *this; } template< typename ElementT > bool FixedSizeList::PushBack(ElementT const el) { if (size + 1 > max_size) { return false; } elementsArray[size] = el; size++; return true; } template< typename ElementT > FixedSizeList::~FixedSizeList() { delete[] elementsArray; } template< typename GuardType > bool HazardPointerThreadEntry::IsActive() { return (is_active == 1); } template< typename GuardType > bool HazardPointerThreadEntry::TryReserve() { int expected = 0; return is_active.CompareAndSwap(expected, 1); } template< typename GuardType > void HazardPointerThreadEntry::Deactivate() { is_active = 0; } template< typename GuardType > size_t HazardPointerThreadEntry::GetRetiredCounter() { return retired_list.GetSize(); } template< typename GuardType > FixedSizeList< GuardType >& HazardPointerThreadEntry:: GetRetired() { return retired_list; } template< typename GuardType > FixedSizeList< GuardType >& HazardPointerThreadEntry:: GetRetiredTemp() { return retired_list_temp; } template< typename GuardType > FixedSizeList< GuardType >& HazardPointerThreadEntry:: GetHazardTemp() { return hazard_pointer_list_temp; } template< typename GuardType > void HazardPointerThreadEntry:: SetRetired(internal::FixedSizeList< GuardType > const & retired_list) { this->retired_list = retired_list; } template< typename GuardType > HazardPointerThreadEntry:: HazardPointerThreadEntry(GuardType undefined_guard, int guards_per_thread, size_t max_size_retired_list) : undefined_guard(undefined_guard), guards_per_thread(guards_per_thread), max_size_retired_list(max_size_retired_list), retired_list(max_size_retired_list), retired_list_temp(max_size_retired_list), hazard_pointer_list_temp(embb::base::Thread::GetThreadsMaxCount() * guards_per_thread) { // Initialize guarded pointer list guarded_pointers = static_cast*> (embb::base::Allocation::Allocate( sizeof(embb::base::Atomic)*guards_per_thread)); for (int i = 0; i != guards_per_thread; ++i) { new (static_cast(&guarded_pointers[i])) embb::base::Atomic(undefined_guard); } } template< typename GuardType > HazardPointerThreadEntry::~HazardPointerThreadEntry() { for (int i = 0; i != guards_per_thread; ++i) { guarded_pointers[i].~Atomic(); } embb::base::Allocation::Free(guarded_pointers); } template< typename GuardType > GuardType HazardPointerThreadEntry::GetGuard(int pos) const { return guarded_pointers[pos]; } template< typename GuardType > void HazardPointerThreadEntry::AddRetired(GuardType pointerToGuard) { retired_list.PushBack(pointerToGuard); } template< typename GuardType > void HazardPointerThreadEntry:: GuardPointer(int guardNumber, GuardType pointerToGuard) { guarded_pointers[guardNumber] = pointerToGuard; } template< typename GuardType > void HazardPointerThreadEntry::SetActive(bool active) { if (active == true) is_active = 1; else is_active = 0; } template< typename GuardType > unsigned int HazardPointer< GuardType >::GetCurrentThreadIndex() { unsigned int thread_index; int return_val = embb_internal_thread_index(&thread_index); if (return_val != EMBB_SUCCESS) EMBB_THROW(embb::base::ErrorException, "Could not get thread id!"); return thread_index; } template< typename GuardType > bool HazardPointer< GuardType >::IsThresholdExceeded() { double retiredCounterLocThread = static_cast(GetHazardPointerElementForCurrentThread(). GetRetiredCounter()); return (retiredCounterLocThread >= RETIRE_THRESHOLD * static_cast(active_hazard_pointer)* static_cast(guards_per_thread)); } template< typename GuardType > size_t HazardPointer< GuardType >::GetActiveHazardPointers() { return active_hazard_pointer; } template< typename GuardType > typename HazardPointer< GuardType >::HazardPointerThreadEntry_t & HazardPointer< GuardType >::GetHazardPointerElementForCurrentThread() { // For each thread, there is a slot in the hazard pointer array. // Initially, the active flag of a hazard pointer entry is false. // Only the respective thread changes the flag from true to false. // This means that the current thread tells that he is about to // stop operating, and the others are responsible for his retired // list. // int expected = false; HazardPointerThreadEntry_t* current_thread_entry = &hazard_pointer_thread_entry_array[GetCurrentThreadIndex()]; // If not active, activate it if (!current_thread_entry->IsActive()) { current_thread_entry->SetActive(true); active_hazard_pointer++; } return hazard_pointer_thread_entry_array[GetCurrentThreadIndex()]; } template< typename GuardType > void HazardPointer< GuardType >::HelpScan() { // This is a little bit different than in the paper. In the paper, // the retired nodes from other threads are added to our retired list. // To be able to give a bound on memory consumption, we execute scan // for those threads, without moving elements. The effect shall be // the same. for (size_t i = 0; i != hazard_pointers; ++i) { // Try to find non active lists... if (!hazard_pointer_thread_entry_array[i].IsActive() && hazard_pointer_thread_entry_array[i].TryReserve()) { // Here: grab retired things, first check if there are any... if (hazard_pointer_thread_entry_array[i].GetRetiredCounter() > 0) { Scan(&hazard_pointer_thread_entry_array[i]); } // We are done, mark it as deactivated again hazard_pointer_thread_entry_array[i].Deactivate(); } } } template< typename GuardType > void HazardPointer< GuardType >:: Scan(HazardPointerThreadEntry_t* currentHazardPointerEntry) { // In this function, we compute the intersection between local retired // pointers and all hazard pointers. This intersection cannot be deleted and // forms the new local retired pointers list. // It is assumed that the union of all retired pointers contains no two // pointers with the same value. However, the union of all hazard guards // might. // Here, we store the temporary hazard pointers. We have to store them, // as iterating multiple time over them might be expensive, as this // atomic array is shared between threads. currentHazardPointerEntry->GetHazardTemp().clear(); // Get all active hazard pointers! for (unsigned int i = 0; i != hazard_pointers; ++i) { // Only consider guards of active threads if (hazard_pointer_thread_entry_array[i].IsActive()) { // For each guard in an hazard pointer entry for (int pos = 0; pos != guards_per_thread; ++pos) { GuardType guard = hazard_pointer_thread_entry_array[i].GetGuard(pos); // UndefinedGuard means not guarded if (guard == undefined_guard) continue; currentHazardPointerEntry->GetHazardTemp().PushBack(guard); } } } currentHazardPointerEntry->GetRetiredTemp().clear(); // Sort them, we will do a binary search on each entry from the retired list std::sort( currentHazardPointerEntry->GetHazardTemp().begin(), currentHazardPointerEntry->GetHazardTemp().end()); for ( EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME FixedSizeList< GuardType >::iterator it = currentHazardPointerEntry->GetRetired().begin(); it != currentHazardPointerEntry->GetRetired().end(); ++it) { if (false == ::std::binary_search( currentHazardPointerEntry->GetHazardTemp().begin(), currentHazardPointerEntry->GetHazardTemp().end(), *it)) { this->free_guard_callback(*it); } else { currentHazardPointerEntry->GetRetiredTemp().PushBack(*it); } } currentHazardPointerEntry->SetRetired( currentHazardPointerEntry->GetRetiredTemp()); } template< typename GuardType > size_t HazardPointer< GuardType >::GetRetiredListMaxSize() const { return static_cast(RETIRE_THRESHOLD * static_cast(embb::base::Thread::GetThreadsMaxCount()) * static_cast(guards_per_thread)) + 1; } template< typename GuardType > HazardPointer< GuardType >::HazardPointer( embb::base::Function free_guard_callback, GuardType undefined_guard, int guards_per_thread) : undefined_guard(undefined_guard), guards_per_thread(guards_per_thread), active_hazard_pointer(0), free_guard_callback(free_guard_callback) { hazard_pointers = embb::base::Thread::GetThreadsMaxCount(); hazard_pointer_thread_entry_array = static_cast( embb::base::Allocation::Allocate(sizeof(HazardPointerThreadEntry_t) * hazard_pointers)); for (size_t i = 0; i != hazard_pointers; ++i) { new (static_cast(&(hazard_pointer_thread_entry_array[i]))) HazardPointerThreadEntry_t(undefined_guard, guards_per_thread, GetRetiredListMaxSize()); } } template< typename GuardType > HazardPointer< GuardType >::~HazardPointer() { for (size_t i = 0; i != hazard_pointers; ++i) { hazard_pointer_thread_entry_array[i].~HazardPointerThreadEntry_t(); } embb::base::Allocation::Free(static_cast < void* > (hazard_pointer_thread_entry_array)); } template< typename GuardType > void HazardPointer< GuardType >::DeactivateCurrentThread() { HazardPointerThreadEntry_t* current_thread_entry = &hazard_pointer_thread_entry_array[GetCurrentThreadIndex()]; // Deactivating a non-active hazard pointer entry has no effect! if (!current_thread_entry->IsActive()) { return; } else { current_thread_entry->SetActive(false); active_hazard_pointer--; } } template< typename GuardType > void HazardPointer< GuardType >::GuardPointer(int guardPosition, GuardType guardedElement) { GetHazardPointerElementForCurrentThread().GuardPointer( guardPosition, guardedElement); } template< typename GuardType > void HazardPointer< GuardType >::EnqueuePointerForDeletion( GuardType guardedElement) { GetHazardPointerElementForCurrentThread().AddRetired(guardedElement); if (IsThresholdExceeded()) { HazardPointerThreadEntry_t* currentHazardPointerEntry = &GetHazardPointerElementForCurrentThread(); Scan(currentHazardPointerEntry); // Help deactivated threads to clean their retired nodes. HelpScan(); } } template const double embb::containers::internal::HazardPointer:: RETIRE_THRESHOLD = 1.25f; } // namespace internal } // namespace containers } // namespace embb #endif // EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_INL_H_