/* * Copyright 2002-2019 Intel Corporation. * * This software and the related documents are Intel copyrighted materials, and your * use of them is governed by the express license under which they were provided to * you ("License"). Unless the License provides otherwise, you may not use, modify, * copy, publish, distribute, disclose or transmit this software or the related * documents without Intel's prior written permission. * * This software and the related documents are provided as is, with no express or * implied warranties, other than those that are expressly stated in the License. */ // : atomic // : component public header #ifndef ATOMIC_IDSET_HPP #define ATOMIC_IDSET_HPP #include "atomic/ops.hpp" #include "atomic/nullstats.hpp" namespace ATOMIC { /*! @brief Maintains a set of unique IDs. * * A non-blocking utility that maintains a set of small integral IDs. Clients * can request a unique ID and release an ID when it is no longer needed. The * IDSET reuses IDs that have been released to ensure that the ID numbers remain * small. * * @param MaxID The IDSET allows IDs in the inclusive range [1, MaxID] * @param STATS Type of an object that collects statistics. See NULLSTATS for a model. * * @par Example: * \code * #include "atomic/idset.hpp" * * ATOMIC::IDSET<31> IdGenerator; * * void Foo() * { * UINT32 id = IdGenerator.GetID(); * IdGenerator.ReleaseID(id); * } * \endcode */ template class /**/ IDSET { public: /*! * Construct a new IDSET. This method is NOT atomic. * * @param[in] stats The new statistics collection object. */ IDSET(STATS *stats=0) : _stats(stats) { for (UINT32 i = 0; i < _numElements; i++) _bits[i] = 0; // If MaxID is not an even multiple of the number of bits in a UINT32, // the _bits[] will contain some "extra" bits. Permanently reserve these // extra bit positions so GetID() never returns an ID greater than MaxID. // const UINT32 MaxIDMod32 = MaxID % 32; if (MaxIDMod32) _bits[_numElements-1] = ( (1<<((32-MaxIDMod32)%32)) - 1) << MaxIDMod32; } /*! * Set the statistics collection object. This method is NOT atomic. * * @param[in] stats The new statistics collection object. */ void SetStatsNonAtomic(STATS *stats) { _stats = stats; } /*! * Request a new ID that is not currently in use. * * @return Returns an ID in the range [1, MaxID] or 0 if there are no * unused IDs. */ UINT32 GetID() { EXPONENTIAL_BACKOFF backoff(1, _stats); for (UINT32 i = 0; i < _numElements; i++) { UINT32 val = OPS::Load(&_bits[i]); while (val != 0xffffffff) { UINT32 bit = 0; for (UINT32 tval = val; tval & 1; tval >>= 1) bit++; UINT32 newval = val | (1 << bit); if (OPS::CompareAndDidSwap(&_bits[i], val, newval)) return i*sizeof(UINT32)*8 + bit + 1; backoff.Delay(); val = OPS::Load(&_bits[i]); } } return 0; } /*! * Release an ID, making it available for reuse. * * @param[in] id The ID, which must be in the range [1,MaxID]. */ void ReleaseID(UINT32 id) { id--; UINT32 i = id >> 5; UINT32 bit = 1 << (id & 0x1f); UINT32 val; UINT32 newval; EXPONENTIAL_BACKOFF backoff(1, _stats); do { backoff.Delay(); val = OPS::Load(&_bits[i]); newval = val & ~bit; } while (!OPS::CompareAndDidSwap(&_bits[i], val, newval)); } /*! * Return true for an id is in use, false otherwise * * @param[in] id The ID, which must be in the range [1,MaxID]. */ bool IsIDInUse(UINT32 id) { id--; UINT32 i = id >> 5; UINT32 bit = 1 << (id & 0x1f); UINT32 val = OPS::Load(&_bits[i]); return ((val&bit) != 0); } private: static const UINT32 _numElements = (MaxID + 8*sizeof(UINT32)-1) / (8*sizeof(UINT32)); volatile UINT32 _bits[_numElements]; STATS *_stats; // Object which collects statistics, or NULL }; } // namespace #endif // file guard