/* * Copyright (c) 2014-2016, 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_H_ #define EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_ #include #include #include #include #include #include #if defined(EMBB_PLATFORM_COMPILER_MSVC) #define EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME #else #define EMBB_CONTAINERS_CPP_DEPENDANT_TYPENAME typename #endif // forward declaration for white-box test, used in friend declaration of // HazardPointer class. namespace embb { namespace containers { namespace test { class HazardPointerTest2; } } } namespace embb { namespace containers { namespace internal { /** * This class contains a hazard pointer implementation following publication: * * Maged M. Michael. "Hazard pointers: Safe memory reclamation for lock-free * objects." IEEE Transactions on Parallel and Distributed Systems, 15.6 (2004) * : 491-504. * * Hazard pointers are a wait-free memory reclamation scheme for lock-free * algorithms. Loosely speaking, they act as garbage collector. The release of * objects contained within the memory, managed by the hazard pointer class, is * intercepted and possibly delayed to avoid concurrency bugs. * * Before accessing an object, threads announce their intention to do so (i.e. * the intention to dereference the respective pointer) to the hazard pointer * class. This is called guarding. From now on, the hazard pointer class will * prohibit the release or reuse of the guarded object. This is necessary, to * assure that the object is not released or reused while it is accessed and to * assure that it has not unnoticed changed (effectively avoiding the ABA * problem). * * Note that after guarding an object, a consecutive check that the object (i.e. * its pointer) is still valid is necessary; the object release could already * have been started when guarding the object. Guarding is repeated, until this * check eventually succeeds. Note that this "guard-and-check" loop makes the * usage of the hazard pointer class lock-free, even though its implementation * is wait-free. * * Internally, guarding is realized by providing each thread slots, where * pointers can be placed that should not be freed (so called guards). When * trying to release an object, it is checked if the object's pointer is * guarded, and if so this object is not released, but instead put into a * retired list for later release, when all guards for this object have been * removed. * * In contrast to the original implementation, our implementation consumes only * fixed-size memory. Note that the number of threads accessing the hazard * pointer object accounts quadratic for the memory consumption: managed objects * are provided from outside and the number of accessors accounts quadric for * the minimum count of those objects. * * Also in contrast to the original implementation, we do not provide a HelpScan * functionality, which gives threads the possibility, to not participate in the * garbage collection anymore: other threads will help to clean-up the objects * protected by the exiting thread. The reason is, that the only use-case would * be a crashing thread, not participating anymore. However, as the thread has * to signal its exit himself, this is not possible to realize anyways. In the * end, it is still guaranteed that all memory is properly returned (in the * destructor). * * Additionally, the original implementation holds a threshold, which determines * when objects shall be freed. In this implementation, we free whenever it is * possibly to do so, as we want to keep the memory footprint as low as * possible. We also don't see a performance drop in the current algorithms that * are using hazard pointers, when not using a threshold. * * \tparam GuardType the type of the guards. Usually the pointer type of some * object to protect. */ template< typename GuardType > class HazardPointer { public: /** * The user of the hazard pointer class has to provide the memory that is * managed here. The user has to take into account, that releasing of memory * might be delayed. He has therefore to provide more memory than he wants to * guarantee at each point in time. More specific, on top of the guaranteed * count of objects, he has to provide the additional count of objects that * can be (worst-case) contained in the retired lists and therefore are not * released yet. The size sum of all retired lists is guardsPerThread * * accessorCount * accessorCount, which is computed using this function. So * the result of function denotes to the user, how many objects he has to * allocate additionally to the guaranteed count. * * \waitfree */ static size_t ComputeMaximumRetiredObjectCount( size_t guardsPerThread, /**<[IN] the count of guards per thread*/ int accessors = -1 /**<[IN] Number of accessors. Determines, how many threads will access the hazard pointer object. Default value -1 will allow the maximum amount of threads as defined with \c embb::base::Thread::GetThreadsMaxCount()*/ ); /** * Initializes the hazard pointer object * * \notthreadsafe * * \memory We dynamically allocate the following: * * (sizeof(Atomic) * accessors) + (sizeof(Atomic) * * guards_per_thread * accessors) + (2*sizeof(GuardType) * * guards_per_thread * accessors^2) * * The last addend is the dominant one, as accessorCount accounts * quadratically for it. */ HazardPointer( embb::base::Function free_guard_callback, /**<[IN] Callback to the function that shall be called when a retired guard can be deleted */ GuardType undefined_guard, /**<[IN] The guard value denoting "not guarded"*/ int guards_per_thread, /**<[IN] Number of guards per thread*/ int accessors = -1 /**<[IN] Number of accessors. Determines, how many threads will access this hazard pointer object. Default value -1 will allow the maximum amount of threads as defined with \c embb::base::Thread::GetThreadsMaxCount()*/ ); /** * Deallocates internal data structures. Additionally releases all objects * currently held in the retired lists, using the release functor passed in * the constructor. * * \notthreadsafe */ ~HazardPointer(); /** * Guards \c to_guard. If the guarded_element is passed to \c EnqueueForDeletion * it is prevented from release from now on. The user must have a check, that * EnqueueForDeletion has not been called on to_guard, before the guarding took * effect. * * \waitfree */ void Guard( int guard_position, /**<[IN] position to place guard*/ GuardType to_guard /**<[IN] element to guard*/ ); /** * Enqueue guarded element for deletion. If not guarded, it is deleted * immediately. If it is guarded, it is added to a thread local retired list, * and deleted in a subsequent call to \c EnqueueForDeletion, when no guard is * placed on it anymore. */ void EnqueueForDeletion( GuardType guarded_element /**<[IN] element to logically delete*/ ); /** * Explicitly remove guard from thread local slot. * * \waitfree */ void RemoveGuard(int guard_position); private: /** * HazardPointerTest2 is a white-box test, needing access to private members * of this class. So declaring it as friend. */ friend class embb::containers::test::HazardPointerTest2; /** * This number determines the amount of maximal accessors (threads) that * will access this hazard pointer instance. Note that a thread once * accessing this object will be permanently count as accessor, even if not * participating anymore. If too many threads access this object, an * exception is thrown. */ unsigned int max_accessors_count_; /** * The guard value denoting "not guarded" */ GuardType undefined_guard_; /** * The maximal count of guards that can be set per thread. */ int max_guards_per_thread_; /** * The functor that is called to release an object. This is called by this * class, when it is safe to do so, i.e., no thread accesses this object * anymore. */ embb::base::Function release_object_callback_; /** * Mapping from EMBB thread id to hazard pointer thread ids. Hazard pointer * thread ids are in range [0;accesor_count-1]. The position of a EMBB thread * id in that array determines the respective hazard pointer thread id. */ embb::base::Atomic* thread_id_mapping_; /** * The hazard pointer guards, represented as array. Each thread has a fixed * set of slots (guardsPerThread) within this array. */ embb::base::Atomic* guards_; /** * \see threadLocalRetiredLists documentation */ GuardType* thread_local_retired_lists_temp_; /** * A list of lists, represented as single array. Each thread maintains a list * of retired pointers, that are objects that are logically released but not * released because some thread placed a guard on it. */ GuardType* thread_local_retired_lists_; /** * Each thread is assigned a thread index (starting with 0). Get the index of * the current thread. Note that this is not the global index, but an hazard * pointer class internal one. The user is free to define less accessors than * the amount of default threads. This is useful, as the number of accessors * accounts quadratic for the memory consumption, so the user should have the * possibility to avoid memory wastage when only having a small, fixed size, * number of accessors. * * @return current (hazard pointer object local) thread index */ unsigned int GetObjectLocalThreadIndex(); /** * Copy retired list \c sourceList to retired list \c targetList */ static void CopyRetiredList( GuardType* source_list, /**<[IN] the source retired list*/ GuardType* target_list, /**<[IN] the target retired list*/ unsigned int single_retired_list_size, /**<[IN] the size of a thread local retired list*/ GuardType undefined_guard /**<[IN] the undefined guard (usually the NULL pointer)*/ ); static void UpdateRetiredList( GuardType* retired_list, /**<[IN] the old retired list*/ GuardType* updated_retired_list, /**<[IN] the updated retired list*/ unsigned int retired_list_size, /**<[IN] the size of a thread local retired list*/ GuardType to_retire, /**<[IN] the element to retire*/ GuardType considered_hazard, /**<[IN] the currently considered hazard*/ GuardType undefined_guard /**<[IN] the undefined guard (usually the NULL pointer)*/ ); }; } // namespace internal } // namespace containers } // namespace embb #include "./hazard_pointer-inl.h" #endif // EMBB_CONTAINERS_INTERNAL_HAZARD_POINTER_H_