muta_ref.H

00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2002 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
00018  */
00019 
00020 #ifndef ARCHON_UTILITIES_MUTA_REF_H
00021 #define ARCHON_UTILITIES_MUTA_REF_H
00022 
00023 #include <archon/util/exception.H>
00024 #include <archon/util/atomic.H>
00025 
00026 namespace Archon
00027 {
00028   namespace Utilities
00029   {
00030     using namespace std;
00031 
00075     template<typename T>
00076     struct MutaRef
00077     {
00078       typedef T ObjectType;
00079 
00111       void reset(T *q = 0)
00112       {
00113         _reset(q);
00114       }
00115 
00147       void mutate()
00148       {
00149         if(!p) return;
00150 
00151         /*
00152          * Since this MutaRef variable exists, the reference count of
00153          * the referenced object cannot be zero. It cannot _become_
00154          * zero since concurrent access to a MutaRef variable is
00155          * forbidden.
00156          *
00157          * If we find the object to be in the leaked state we know
00158          * it will remain in the leaked state (zero was not an
00159          * option.) If we find that the object has a reference count
00160          * of 1 we known that this MutaRef variable is the only
00161          * counted reference to the object, thus no new counted
00162          * references can be made to the object during the execution
00163          * of this function. Neither can the object enter the leaked
00164          * state.
00165          *
00166          * In summary, both -1 and 1 are stable states individually.
00167          *
00168          * Please note that the inverse of the test is not stable
00169          * (state 1 may be entered after the test but before the clone
00170          * operation), thus, the cloning may be carried out in vain.
00171          */
00172         if(p->refCount == -1 || p->refCount == 1) return;
00173 
00174         _clone();
00175       }
00176 
00202       void leak()
00203       {
00204         if(!p) return;
00205 
00206         /*
00207          * Since this MutaRef variable exists, the reference count of
00208          * the referenced object cannot be zero. It cannot become zero
00209          * since concurrent access to a MutaRef variable is forbidden.
00210          *
00211          * If we find that the object is in the leaked state we know
00212          * that it will remain in the leaked state (zero was not an
00213          * option.) So, in this case we are done.
00214          */
00215         if(p->refCount == -1) return;
00216 
00217         /*
00218          * At this point we know that the reference count is at least
00219          * 1. If we find it to be 1 we know that it will remain on
00220          * 1. Thus, failure of the following test is enough to
00221          * guarantee that the referenced object is unshared.
00222          *
00223          * We also have a guarantee that the referenced object is
00224          * unshared after calling clone().
00225          */
00226         if(p->refCount != 1) _clone();
00227 
00228         /*
00229          * At this point we know for sure that the reference count is
00230          * equal to 1 and will remain equal to 1 untill the assignment
00231          * below modifies it.
00232          */
00233         p->refCount = -1;
00234       }
00235 
00239       MutaRef(): p(0) {}
00240 
00244       MutaRef(const MutaRef &r): p(_copy(r.p)) {}
00245 
00246       ~MutaRef()
00247       {
00248         _break();
00249       }
00250 
00254       MutaRef &operator=(const MutaRef &r)
00255       {
00256         _reset(r.p);
00257         return *this;
00258       }
00259 
00260       T *operator->() const
00261       {
00262         if(p) return p;
00263         ARCHON_THROW1(StateException, "Dereferencing null");
00264       }
00265 
00266       operator bool() const { return p; }
00267 
00268     private:
00269       T *p;
00270 
00271       void _clone()
00272       {
00273         _break(dynamic_cast<T *>(p->refClone()));
00274         ++p->refCount;
00275       }
00276 
00292       static T *_copy(T *q)
00293       {
00294         if(!q) return 0;
00295 
00296         if(q->refCount == -1) q = dynamic_cast<T *>(q->refClone());
00297 
00298         /*
00299          * It is important that the incrementation below never happens
00300          * on an object in the leaked state.
00301          *
00302          * A transition to the leaked state can only occur from a
00303          * state where the reference count is 1. Thus to become -1 it
00304          * must fist become 1.
00305          *
00306          * When the reference count reaches 1 there must be
00307          * exactly 1 other MutaRef variables pointing to the same
00308          * object as this MutaRef variable (this MutaRef varibale
00309          * does not yet count.)
00310          *
00311          * This rules out the cases where the reference count is zero
00312          * at entry and the those where we did a clone operation,
00313          * since in those cases no other MutaRef variable could exist
00314          * (we are busy creating the first one.)
00315          *
00316          * Consequently this case must be one where we are
00317          * constructing this MutaRef variable as a copy of another.
00318          *
00319          * Since the construction of this MutaRef variable is not yet
00320          * complete, a transition to the leaked state must be
00321          * performed by calling leak() on some other MutaRef variable.
00322          *
00323          * Since there was only 1 other MutaRef variable, the one on
00324          * which leak() is called must be the same one that we are
00325          * copying.
00326          *
00327          * Now, such concurrent access is illegal and thus impossible.
00328          *
00329          * In other words, we know that the reference count cannot be
00330          * -1 when the following incrementation occurs.
00331          */
00332         ++q->refCount;
00333 
00334         return q;
00335       }
00336 
00349       void _break(T *q = 0)
00350       {
00351         /*
00352          * If we find the object to be in the leaked state, there is
00353          * only one reference to the object which must be the
00354          * reference we are about to destroy. Since this reference is
00355          * protected against access from other threads, the reference
00356          * count will remain on -1 untill the object is destoyed
00357          * below.
00358          *
00359          * On the other hand, if we find the object not to be in the
00360          * leaked state, we know that the reference count will be at
00361          * least 1 and that it will continue to be at least 1 until
00362          * modified by this thread below. We know this because the
00363          * refernce count was not 0 at entry and cannot become 0 prior
00364          * to the modification below since this variable represented
00365          * one reference at entry and access from other threads is
00366          * forbidden. Further more, a transition to the leaked state
00367          * can only be initiated when the reference count is equal to
00368          * 1. If the reference count is 1 or does become 1 the object
00369          * can no longer be accessed by other threads and thus, a
00370          * transition to the leaked state is impossible.
00371          */
00372         if(p && (p->refCount == -1 || p->refCount.decAndZeroTest()))
00373           p->refDelete();
00374 
00375         p = q;
00376       }
00377 
00378       void _reset(T *q)
00379       {
00380         if(p == q) return;
00381         _break(_copy(q));
00382       }
00383     };
00384 
00385     struct MutaRefObjectBase
00386     {
00387     protected:
00393       virtual MutaRefObjectBase *refClone() const = 0;
00394 
00400       virtual void refDelete() throw()
00401       {
00402         delete this;
00403       }
00404 
00405       MutaRefObjectBase(): refCount(0) {}
00406 
00410       virtual ~MutaRefObjectBase() {}
00411 
00412     private:
00413       template<typename T> friend struct MutaRef;
00414 
00415       /*
00416        * <pre>
00417        *
00418        * -1 Leaked object
00419        *  0 Fresh unreferenced object (or abandoned object)
00420        * >0 Referenced object
00421        *
00422        *   Fresh    Referenced    Leaked    Abandoned
00423        *
00424        *                     ,--> (-1)
00425        *    (0) ---> (>0) --<
00426        *                     `------------>  (0)
00427        *
00428        * </pre>
00429        *
00430        * The abandoned state will only exist shortly since it triggers
00431        * the deletion of the object.
00432        */
00433       mutable Atomic refCount;
00434     };
00435 
00444     template<typename T>
00445     struct MutaRefObject: MutaRefObjectBase
00446     {
00447       MutaRefObject(T v): value(v) {}
00448 
00449       MutaRefObject *refClone() const { return new MutaRefObject(value); }
00450 
00451       T value;
00452     };
00453   }
00454 }
00455 
00456 #endif // ARCHON_UTILITIES_MUTA_REF_H

Generated on Sun Jul 30 22:55:45 2006 for Archon by  doxygen 1.4.4