// // RefCountedObject.h // // Library: Foundation // Package: Core // Module: RefCountedObject // // Definition of the RefCountedObject class. // // Copyright (c) 2004-2009, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #ifndef Foundation_RefCountedObject_INCLUDED #define Foundation_RefCountedObject_INCLUDED #include "Poco/Foundation.h" #include "Poco/MemoryPool.h" #include "Poco/Mutex.h" #include #if defined(_DEBUG) && defined(POCO_REFCOUNT_DC) #define ENABLE_REFCOUNT_DC #endif #ifdef ENABLE_REFCOUNT_DC #include "Poco/NestedDiagnosticContext.h" #include "Poco/OrderedMap.h" #include "Poco/Mutex.h" namespace Poco { // // RefCountDiagnosticContext // class Foundation_API RefCountDiagnosticContext /// This utility class is used for debugging and testing purposes /// of the reference counting functionality and should not be used /// for any other purpose. it is enabled only for debug builds and /// if POCO_REFCOUNT_DC macro is defined in Config.h. /// /// See poco_rcdc_* set of macros below for explanation how it is used. { public: class TraceRecord { public: TraceRecord(void *ptr, int refCount, const char* func, const std::string &backtrace = ""); TraceRecord(const TraceRecord& other); TraceRecord(TraceRecord&& other); TraceRecord& operator == (const TraceRecord& other); TraceRecord& operator == (TraceRecord&& other); private: TraceRecord(); void* _ptr = 0; int _refCount = 0; std::string _functionName; std::string _backtrace; friend class RefCountDiagnosticContext; }; typedef OrderedMap> TraceMap; RefCountDiagnosticContext(bool full = false); ~RefCountDiagnosticContext(); void addEntry(TraceRecord&& record) const; void dumpRef(std::ostream &os, bool leakOnly = true) const; void dumpAllRef(std::ostream &os) const; void dumpLeakRef(std::ostream &os) const; void clear() const; // definition in Foundation.cpp static RefCountDiagnosticContext& get(); private: RefCountDiagnosticContext(const RefCountDiagnosticContext&) = delete; RefCountDiagnosticContext& operator = (const RefCountDiagnosticContext&) = delete; RefCountDiagnosticContext(RefCountDiagnosticContext&&) = delete; RefCountDiagnosticContext& operator = (RefCountDiagnosticContext&&) = delete; static TraceMap& traceMap() { return _traceMap; } // definitions in Foundation.cpp static TraceMap _traceMap; static SpinlockMutex _mutex; static bool _full; }; // // inlines // inline void RefCountDiagnosticContext::addEntry(TraceRecord&& record) const { SpinlockMutex::ScopedLock l(_mutex); traceMap()[record._ptr].emplace_back(std::move(record)); } inline void RefCountDiagnosticContext::dumpAllRef(std::ostream& os) const { dumpRef(os, false); if (!_full) { os << std::endl; os << "Full dump requested but not recorded, leaks only (if any) displayed."; os << std::endl; } } inline void RefCountDiagnosticContext::dumpLeakRef(std::ostream& os) const { dumpRef(os, true); } inline void RefCountDiagnosticContext::clear() const { SpinlockMutex::ScopedLock l(_mutex); traceMap().clear(); } typedef RefCountDiagnosticContext RCDC; } // namespace Poco // Diagnostic utility macros for tracing and managing records of refcount increments/decrements. // To trim stack backtrace entries from beginning or end, adjust parameters to NDC::backTrace() // below. Usable only for classes that inherit from [Weak]RefCountedObject. // // Note: since access to the internal RefCountDiagnosticContext (static) storage is protected // by a static mutex, using this functionality partially disables the multi-threaded nature // of RefCounted access, so it should never be permanently enabled in code, but used as a // temporary reference counting troubleshooting tool (compile debug, with POCO_REFCOUNT_DC in // Config.h defined to enable/disable this functionality). // // Backtrace works only with g++ at this time; without it this functionality is of limited use. // Disabled by default for all builds and always (even if POCO_REFCOUNT_DC is defined) disabled // for non-debug builds. #define poco_rcdc_log RCDC::get().addEntry(RCDC::TraceRecord((void*)this, _counter, __func__, NDC::backtrace(1, 7))) #define poco_rcdc_dump_leak(os) RCDC::get().dumpLeakRef(os) #define poco_rcdc_dump_all(os) RCDC::get().dumpAllRef(os) #define poco_rcdc_reset RCDC::get().clear(); #else // !ENABLE_REFCOUNT_DC namespace Poco { typedef void RCDC; } #define poco_rcdc_log #define poco_rcdc_dump_leak(os) #define poco_rcdc_dump_all(os) #define poco_rcdc_reset #endif // ENABLE_REFCOUNT_DC namespace Poco { // // RefCountedObject // class RefCounter /// A class for thread-safe atomic reference counting. { public: RefCounter(); /// Creates RefCounter. ~RefCounter(); /// Destroys RefCounter. int operator++ (); /// Prefix increment. int operator++ (int); /// Postfix increment. int operator-- (); /// Prefix decrement. int operator-- (int); /// Postfix decrement. operator int(); /// Returns the current value as int. private: RefCounter(const RefCounter&) = delete; RefCounter(RefCounter&&) = delete; RefCounter& operator = (const RefCounter&) = delete; RefCounter& operator = (RefCounter&&) = delete; mutable std::atomic _counter; }; // // inlines // inline int RefCounter::operator++() { #ifdef ENABLE_REFCOUNT_DC int c = _counter.fetch_add(1, std::memory_order_relaxed) + 1; poco_rcdc_log; return c; #endif return _counter.fetch_add(1, std::memory_order_relaxed) + 1; } inline int RefCounter::operator++(int) { #ifdef ENABLE_REFCOUNT_DC int c = _counter.fetch_add(1, std::memory_order_relaxed); poco_rcdc_log; return c; #endif return _counter.fetch_add(1, std::memory_order_relaxed); } inline int RefCounter::operator--() { #ifdef ENABLE_REFCOUNT_DC int c = _counter.fetch_sub(1, std::memory_order_acquire) - 1; poco_rcdc_log; return c; #endif return _counter.fetch_sub(1, std::memory_order_acquire) - 1; } inline int RefCounter::operator--(int) { #ifdef ENABLE_REFCOUNT_DC int c = _counter.fetch_sub(1, std::memory_order_acquire); poco_rcdc_log; return c; #endif return _counter.fetch_sub(1, std::memory_order_acquire); } inline RefCounter::operator int() { return _counter; } // // RefCountedObject // class Foundation_API RefCountedObject /// A class for thread-safe strong reference counting. /// /// It maintains the reference count, and deletes itself /// when the count reaches zero; it follows that a /// RefCountedObject must always be created on the heap. { public: RefCountedObject(); /// Creates RefCountedObject void duplicate() const; /// Increments the reference counter. void release(); /// Decrements the reference counter. /// If counter value after decrement is zero, /// this object is deleted. int referenceCount() const; /// Returns the reference count. protected: virtual ~RefCountedObject(); /// Destroys RefCountedObject private: RefCountedObject(const RefCountedObject&) = delete; RefCountedObject(RefCountedObject&&) = delete; RefCountedObject& operator = (const RefCountedObject&) = delete; RefCountedObject& operator = (RefCountedObject&&) = delete; mutable RefCounter _counter; friend class RefCountDiagnosticContext; }; // // inlines // inline void RefCountedObject::duplicate() const { _counter++; } inline void RefCountedObject::release() { if (--_counter == 0) delete this; } inline int RefCountedObject::referenceCount() const { return _counter; } typedef RefCountedObject RCO; // // WeakRefCounter // // global memory pool for weak counters; // see definition in Foundation.cpp template extern FastMemoryPool& getFastMemoryPool(); class Foundation_API WeakRefCounter /// A class for thread-safe strong and weak reference /// counting. /// /// WeakRefCounter is created on the heap from WeakRefCountedObject /// and may outlive the WeakRefCountedObject that created it. /// It maintains two reference counts, "strong" and "weak"; deletes /// itself when both counts reach zero. This is the only reference /// counting facility accessed by WeakRefPtr. /// /// Note: this class has a very specific library-internal purpose; /// it is not meant for general use. { public: WeakRefCounter(); /// Creates WeakRefCounter. void duplicate(); /// Increments strong counter. int release(); /// Decrements strong counter. /// If, after decrement, both strong and weak /// counters are zero, it deletes this object. WeakRefCounter* duplicateWeak(); /// Increments weak counter. WeakRefCounter* releaseWeak(); /// Decrements weak counter. /// If, after decrement, both strong and weak /// counters are zero, it deletes this object. int count() const; /// Rteturns the current value of the strong /// reference count. int weakCount() const; /// Rteturns the current value of the weak /// reference count. private: ~WeakRefCounter(); /// Destroys WeakRefCounter. void* operator new(std::size_t); /// Returns a block of memory from FastMemoryPool. void operator delete(void* ptr); /// Returns the block of memory to the FastMemoryPool. void* operator new [] (std::size_t); /// Not used; must never be accessed. /// throws InvalidAccessException. void operator delete [] (void* ptr); /// Not used; does nothing. WeakRefCounter(const WeakRefCounter&); WeakRefCounter(WeakRefCounter&&); WeakRefCounter& operator = (const WeakRefCounter&); WeakRefCounter& operator = (WeakRefCounter&&); mutable RefCounter _counter; mutable RefCounter _weakCounter; friend class WeakRefCountedObject; template friend class FastMemoryPool; }; // // inlines // inline void WeakRefCounter::duplicate() { _counter++; } inline int WeakRefCounter::release() { if (--_counter == 0 && _weakCounter == 0) { delete this; return 0; } return _counter; } inline WeakRefCounter* WeakRefCounter::duplicateWeak() { _weakCounter++; return this; } inline WeakRefCounter* WeakRefCounter::releaseWeak() { if (--_weakCounter == 0 && _counter == 0) { delete this; return nullptr; } return this; } inline int WeakRefCounter::count() const { return _counter; } inline int WeakRefCounter::weakCount() const { return _weakCounter; } // // RefCountedObject // class Foundation_API WeakRefCountedObject /// A base class for objects that employ weak/strong /// reference counting based garbage collection. /// /// Reference-counted objects inhibit construction /// by copying and assignment. /// /// The semantics of the RefCountedImpl object /// and involved classes are as follows: /// /// - WeakRefCountedObject (WRCO) is used by WeakRefPtr and /// RefPtr; RefPtr accesses only WRCO, WeakRefPtr accesses /// only WeakRefCounter (which is created from this class /// and may outlive it if "strong" count reaches zero before /// the weak one) /// /// - WRCO must be created on the heap and should never be /// created on the stack (if a user class inherits from WRCO, /// it should have protected destructor) /// /// - both reference counts are maintained by WeakRefCounter, /// which deletes itself under normal circumstances /// (see below for an exception to normal circumstances) /// /// - WeakRefCounter may "outlive" this object if weak reference /// count is greater than zero at the time when the strong /// reference count reaches zero /// /// - WeakRefCounter will always delete itself. except in one "corner" /// case when a WRCO object is created outside of RefPtr/WealRefPtr /// wrappers and deleted using `delete` operator, without release() /// ever be called; since not calling release() means that the counter /// was never notified of the reference count change, it follows that /// it is the responsibility of this object to delete the counter /// /// - reference counting is thread-safe; reference counted objects, /// as well as the smart pointers "wrapping" them, however, are not /// thread-safe; access to null counter pointer means there is a /// bug somewhere in user application (there are debug build checks /// indicating such circumstances, but no non-debug checks, /// so - thread carefully!) /// /// - mutexes are not used, so there is no performance difference between /// single- and multi-threaded modes of operation; note that, depending /// on the underlying atomics implementation, locking may still happen /// in both modes { public: void duplicate() const; /// Increments the object's reference count. void release() const throw(); /// Decrements the object's reference count; /// deletes the object if the count reaches /// zero. int referenceCount() const; /// Returns the reference count. WeakRefCounter* counter() const; /// Return pointer to counter. protected: WeakRefCountedObject(); /// Creates the RefCountedObjectImpl. /// The initial reference count is one.; virtual ~WeakRefCountedObject(); /// Destroys the RefCountedObjectImpl. private: WeakRefCountedObject(const WeakRefCountedObject&) = delete; WeakRefCountedObject& operator = (const WeakRefCountedObject&) = delete; WeakRefCountedObject(WeakRefCountedObject&&) = delete; WeakRefCountedObject& operator = (WeakRefCountedObject&&) = delete; mutable std::atomic _pCounter; friend class RefCountDiagnosticContext; template friend class FastMemoryPool; }; typedef WeakRefCountedObject WRCO; // // inlines // inline void WeakRefCountedObject::duplicate() const { poco_assert_dbg(_pCounter.load() != 0); _pCounter.load()->duplicate(); } inline void WeakRefCountedObject::release() const throw() { poco_assert_dbg(_pCounter.load() != 0); if(_pCounter.load()->release() == 0) { // _pCounter has deleted itself, disarm // the delete in the destructor _pCounter = 0; delete this; return; } } inline int WeakRefCountedObject::referenceCount() const { return _pCounter.load()->count(); } inline WeakRefCounter* WeakRefCountedObject::counter() const { return _pCounter; } } // namespace Poco #endif // Foundation_RefCountedObject_INCLUDED