From 0f7b65a9216458b290113bae0ff75bd3ec4dce36 Mon Sep 17 00:00:00 2001 From: frohlich Date: Wed, 24 Jun 2009 05:19:52 +0000 Subject: [PATCH] Provide a thread safe SGWeakPtr implementation. Extend SGAtomic with atomic exchange and add. Import updates from the original implementation of that in OpenFDM. Modified Files: Makefile.am SGAtomic.cxx SGAtomic.hxx SGReferenced.hxx SGSharedPtr.hxx Added Files: SGWeakPtr.hxx SGWeakReferenced.hxx --- simgear/structure/Makefile.am | 2 + simgear/structure/SGAtomic.cxx | 14 +++- simgear/structure/SGAtomic.hxx | 25 ++++-- simgear/structure/SGReferenced.hxx | 9 --- simgear/structure/SGSharedPtr.hxx | 35 ++++++--- simgear/structure/SGWeakPtr.hxx | 75 ++++++++++++++++++ simgear/structure/SGWeakReferenced.hxx | 103 +++++++++++++++++++++++++ 7 files changed, 238 insertions(+), 25 deletions(-) create mode 100644 simgear/structure/SGWeakPtr.hxx create mode 100644 simgear/structure/SGWeakReferenced.hxx diff --git a/simgear/structure/Makefile.am b/simgear/structure/Makefile.am index d0009177..0f54669b 100644 --- a/simgear/structure/Makefile.am +++ b/simgear/structure/Makefile.am @@ -17,6 +17,8 @@ include_HEADERS = \ SGSharedPtr.hxx \ SGSmplhist.hxx \ SGSmplstat.hxx \ + SGWeakPtr.hxx \ + SGWeakReferenced.hxx \ Singleton.hxx libsgstructure_a_SOURCES = \ diff --git a/simgear/structure/SGAtomic.cxx b/simgear/structure/SGAtomic.cxx index 9eabf4ec..ee219d84 100644 --- a/simgear/structure/SGAtomic.cxx +++ b/simgear/structure/SGAtomic.cxx @@ -1,6 +1,6 @@ /* -*-c++-*- * - * Copyright (C) 2005-2006 Mathias Froehlich + * Copyright (C) 2005-2009 Mathias Froehlich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -53,6 +53,18 @@ unsigned __sync_add_and_fetch_4(volatile void *ptr, unsigned value) return result + value; } +unsigned __sync_bool_compare_and_swap_4(volatile void *ptr, + unsigned oldValue, unsigned newValue) +{ + register volatile unsigned* mem = reinterpret_cast(ptr); + unsigned before; + __asm__ __volatile__("lock; cmpxchg{l} {%1,%2|%1,%2}" + : "=a"(before) + : "q"(newValue), "m"(*mem), "0"(oldValue) + : "memory"); + return before == oldValue; +} + void __sync_synchronize() { __asm__ __volatile__("": : : "memory"); diff --git a/simgear/structure/SGAtomic.hxx b/simgear/structure/SGAtomic.hxx index 4029f352..1ff9a44d 100644 --- a/simgear/structure/SGAtomic.hxx +++ b/simgear/structure/SGAtomic.hxx @@ -1,6 +1,6 @@ /* -*-c++-*- * - * Copyright (C) 2005-2006 Mathias Froehlich + * Copyright (C) 2005-2009 Mathias Froehlich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -81,7 +81,25 @@ public: SGGuard lock(mMutex); return mValue; #endif - } + } + + bool compareAndExchange(unsigned oldValue, unsigned newValue) + { +#if defined(SGATOMIC_USE_GCC4_BUILTINS) + return __sync_bool_compare_and_swap(&mValue, oldValue, newValue); +#elif defined(SGATOMIC_USE_MIPOSPRO_BUILTINS) + return __compare_and_swap(&mValue, oldValue, newValue); +#elif defined(SGATOMIC_USE_WIN32_INTERLOCKED) + long volatile* lvPtr = reinterpret_cast(&mValue); + return oldValue == InterlockedCompareExchange(lvPtr, newValue, oldValue); +#else + SGGuard lock(mMutex); + if (mValue != oldValue) + return false; + mValue = newValue; + return true; +#endif + } private: SGAtomic(const SGAtomic&); @@ -91,9 +109,6 @@ private: && !defined(SGATOMIC_USE_MIPOSPRO_BUILTINS) \ && !defined(SGATOMIC_USE_WIN32_INTERLOCKED) mutable SGMutex mMutex; -#endif -#ifdef SGATOMIC_USE_WIN32_INTERLOCKED - __declspec(align(32)) #endif unsigned mValue; }; diff --git a/simgear/structure/SGReferenced.hxx b/simgear/structure/SGReferenced.hxx index 6bce1eb8..6a3038f3 100644 --- a/simgear/structure/SGReferenced.hxx +++ b/simgear/structure/SGReferenced.hxx @@ -20,13 +20,8 @@ #ifndef SGReferenced_HXX #define SGReferenced_HXX -#define USE_OPENTHREADS_ATOMIC -#ifndef USE_OPENTHREADS_ATOMIC #include "SGAtomic.hxx" -#else -#include -#endif /// Base class for all reference counted SimGear objects /// Classes derived from this one are meant to be managed with @@ -54,11 +49,7 @@ public: { if (ref) return 1u < ref->_refcount; else return false; } private: -#ifndef USE_OPENTHREADS_ATOMIC mutable SGAtomic _refcount; -#else - mutable OpenThreads::Atomic _refcount; -#endif }; #endif diff --git a/simgear/structure/SGSharedPtr.hxx b/simgear/structure/SGSharedPtr.hxx index d0f899bd..b99a6a8f 100644 --- a/simgear/structure/SGSharedPtr.hxx +++ b/simgear/structure/SGSharedPtr.hxx @@ -1,6 +1,6 @@ /* -*-c++-*- * - * Copyright (C) 2005-2006 Mathias Froehlich + * Copyright (C) 2005-2009 Mathias Froehlich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -37,11 +37,14 @@ /// to zero and consequently the objects will never be destroyed. /// Always try to use directed graphs where the references away from the /// top node are made with SGSharedPtr's and the back references are done with -/// ordinary pointers. +/// ordinary pointers or SGWeakPtr's. /// There is a very good description of OpenSceneGraphs ref_ptr which is /// pretty much the same than this one at /// http://dburns.dhs.org/OSG/Articles/RefPointers/RefPointers.html +template +class SGWeakPtr; + template class SGSharedPtr { public: @@ -49,19 +52,19 @@ public: {} SGSharedPtr(T* ptr) : _ptr(ptr) { get(_ptr); } - SGSharedPtr(const SGSharedPtr& p) : _ptr(p.ptr()) + SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } template - SGSharedPtr(const SGSharedPtr& p) : _ptr(p.ptr()) + SGSharedPtr(const SGSharedPtr& p) : _ptr(p.get()) { get(_ptr); } ~SGSharedPtr(void) { put(); } SGSharedPtr& operator=(const SGSharedPtr& p) - { assign(p.ptr()); return *this; } + { assign(p.get()); return *this; } template SGSharedPtr& operator=(const SGSharedPtr& p) - { assign(p.ptr()); return *this; } + { assign(p.get()); return *this; } template SGSharedPtr& operator=(U* p) { assign(p); return *this; } @@ -74,26 +77,38 @@ public: { return _ptr; } T* ptr(void) const { return _ptr; } + T* get(void) const + { return _ptr; } + T* release() + { T* tmp = _ptr; _ptr = 0; T::put(tmp); return tmp; } bool isShared(void) const - { return SGReferenced::shared(_ptr); } + { return T::shared(_ptr); } unsigned getNumRefs(void) const - { return SGReferenced::count(_ptr); } + { return T::count(_ptr); } bool valid(void) const { return _ptr; } + void clear() + { put(); } + void swap(SGSharedPtr& sharedPtr) + { T* tmp = _ptr; _ptr = sharedPtr._ptr; sharedPtr._ptr = tmp; } + private: void assign(T* p) { get(p); put(); _ptr = p; } void get(const T* p) const - { SGReferenced::get(p); } + { T::get(p); } void put(void) - { if (!SGReferenced::put(_ptr)) { delete _ptr; _ptr = 0; } } + { if (!T::put(_ptr)) { delete _ptr; _ptr = 0; } } // The reference itself. T* _ptr; + + template + friend class SGWeakPtr; }; #endif diff --git a/simgear/structure/SGWeakPtr.hxx b/simgear/structure/SGWeakPtr.hxx new file mode 100644 index 00000000..c84ae371 --- /dev/null +++ b/simgear/structure/SGWeakPtr.hxx @@ -0,0 +1,75 @@ +// Copyright (C) 2004-2009 Mathias Froehlich - Mathias.Froehlich@web.de +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef SGWeakPtr_HXX +#define SGWeakPtr_HXX + +#include "SGWeakReferenced.hxx" + +template +class SGWeakPtr { +public: + SGWeakPtr(void) + { } + SGWeakPtr(const SGWeakPtr& p) : mWeakData(p.mWeakData) + { } + template + SGWeakPtr(const SGSharedPtr& p) + { SGSharedPtr sharedPtr = p; assign(sharedPtr.get()); } + template + SGWeakPtr(const SGWeakPtr& p) + { SGSharedPtr sharedPtr = p.lock(); assign(sharedPtr.get()); } + ~SGWeakPtr(void) + { } + + template + SGWeakPtr& operator=(const SGSharedPtr& p) + { SGSharedPtr sharedPtr = p; assign(sharedPtr.get()); return *this; } + template + SGWeakPtr& operator=(const SGWeakPtr& p) + { SGSharedPtr sharedPtr = p.lock(); assign(sharedPtr.get()); return *this; } + SGWeakPtr& operator=(const SGWeakPtr& p) + { mWeakData = p.mWeakData; return *this; } + + SGSharedPtr lock(void) const + { + if (!mWeakData) + return SGSharedPtr(); + SGSharedPtr sharedPtr; + sharedPtr.assignNonRef(mWeakData->getPointer()); + return sharedPtr; + } + + void clear() + { mWeakData = 0; } + void swap(SGWeakPtr& weakPtr) + { mWeakData.swap(weakPtr.mWeakData); } + +private: + void assign(T* p) + { + if (p) + mWeakData = p->mWeakData; + else + mWeakData = 0; + } + + // The indirect reference itself. + SGSharedPtr mWeakData; +}; + +#endif diff --git a/simgear/structure/SGWeakReferenced.hxx b/simgear/structure/SGWeakReferenced.hxx new file mode 100644 index 00000000..8108300e --- /dev/null +++ b/simgear/structure/SGWeakReferenced.hxx @@ -0,0 +1,103 @@ +// Copyright (C) 2004-2009 Mathias Froehlich - Mathias.Froehlich@web.de +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef SGWeakReferenced_HXX +#define SGWeakReferenced_HXX + +#include "SGReferenced.hxx" +#include "SGSharedPtr.hxx" + +template +class SGWeakPtr; + +class SGWeakReferenced { +public: + /// The object backref and the reference count for this object need to be + /// there in any case. Also these are per object and shall not be copied nor + /// assigned. + /// The reference count for this object is stored in a secondary object that + /// is shared with all weak pointers to this current object. This way we + /// have an atomic decision using the reference count of this current object + /// if the backref is still valid. At the time where the atomic count is + /// equal to zero the object is considered dead. + SGWeakReferenced(void) : + mWeakData(new WeakData(this)) + {} + SGWeakReferenced(const SGWeakReferenced& weakReferenced) : + mWeakData(new WeakData(this)) + {} + ~SGWeakReferenced(void) + { mWeakData->mWeakReferenced = 0; } + + /// Do not copy the weak backward references ... + SGWeakReferenced& operator=(const SGWeakReferenced&) + { return *this; } + + /// The usual operations on weak pointers. + /// The interface should stay the same then what we have in Referenced. + static unsigned get(const SGWeakReferenced* ref) + { if (ref) return ++(ref->mWeakData->mRefcount); else return 0u; } + static unsigned put(const SGWeakReferenced* ref) + { if (ref) return --(ref->mWeakData->mRefcount); else return ~0u; } + static unsigned count(const SGWeakReferenced* ref) + { if (ref) return ref->mWeakData->mRefcount; else return 0u; } + +private: + /// Support for weak references, not increasing the reference count + /// that is done through that small helper class which holds an uncounted + /// reference which is zeroed out on destruction of the current object + class WeakData : public SGReferenced { + public: + WeakData(SGWeakReferenced* weakReferenced) : + mRefcount(0u), + mWeakReferenced(weakReferenced) + { } + + template + T* getPointer() + { + // Try to increment the reference count if the count is greater + // then zero. Since it should only be incremented iff it is nonzero, we + // need to check that value and try to do an atomic test and set. If this + // fails, try again. The usual lockless algorithm ... + unsigned count; + do { + count = mRefcount; + if (count == 0) + return 0; + } while (!mRefcount.compareAndExchange(count, count + 1)); + // We know that as long as the refcount is not zero, the pointer still + // points to valid data. So it is safe to work on it. + return static_cast(mWeakReferenced); + } + + SGAtomic mRefcount; + SGWeakReferenced* mWeakReferenced; + + private: + WeakData(void); + WeakData(const WeakData&); + WeakData& operator=(const WeakData&); + }; + + SGSharedPtr mWeakData; + + template + friend class SGWeakPtr; +}; + +#endif -- 2.39.5