refcount.hpp

Go to the documentation of this file.
00001 #ifndef s11n_net_s11n_refcount_REFCOUNT_HPP_INCLUDED
00002 #define s11n_net_s11n_refcount_REFCOUNT_HPP_INCLUDED 1
00003 // reminders to self:
00004 // - think about lazyassptr<T> which lazily instantiates its
00005 // pointee. That would take a Constructor functor, providing symmetry
00006 // with rcptr<>.
00007 
00008 #include <map>
00009 
00010 
00011 namespace s11n {
00012 /**
00013    The refcount namespace encapsulates code for a reference-counted
00014    smart pointer. It is capable of tracking and destroying objects and
00015    arbitrary pointers (including void pointers) and destroying them
00016    using a user-defined finalizer functor. This allows, e.g., the
00017    reference-counted sharing of memory allocated via malloc() or by
00018    third-party functions such as dlopen() or sqlite3_open().
00019 
00020    This code is not generic, industrial-strength reference counting
00021    and is as much an experiment as anything else.
00022 
00023    Author: stephan at s11n dot net
00024 
00025    License: Public Domain
00026 */
00027 namespace refcount {
00028 
00029 
00030     /**
00031        A no-op "destructor" for use with rcptr.
00032     */
00033     struct no_delete_finalizer
00034     {
00035         /** Assigs t to 0 without deleting t. */
00036         template <typename T>
00037         void operator()( T * & t )
00038         {
00039             t = 0;
00040         }
00041     };
00042 
00043     /**
00044        The default destructor/cleanup functor for use with
00045        rcptr<>.
00046     */
00047     struct plain_delete_finalizer
00048     {
00049         /**
00050            Calls delete t and assigns t to 0.
00051                
00052            Specialized dtors need not call delete, but should
00053            assign t to 0, as this simplifies some client code.
00054 
00055            T must be non-CVP-qualified and for this
00056            implementation (delete t) must be legal.
00057         */
00058         template <typename T>
00059         void operator()( T * & t )
00060         {
00061             delete t;
00062             t = 0;
00063         }
00064     };
00065 
00066     /**
00067        All classes in this namespace are "internal details" of the
00068        classes in the refcount namespace, and should not be
00069        directly used by client code.
00070     */
00071     namespace Detail
00072     {
00073         /**
00074            Internal detail for dereferencing pointers.
00075         */
00076         template <typename T>
00077         struct ref_type
00078         {
00079             /** Same as (T&). */
00080             typedef T & type;
00081             /** Returns *t. */
00082             static type deref( T *t ) { return *t; }
00083         };
00084         /**
00085            Internal detail for dereferencing pointers.
00086         */
00087         template <>
00088         struct ref_type<void>
00089         {
00090             /** Same as (void*&). */
00091             typedef void * & type;
00092             /** Returns xx. */
00093             static type deref( type xx ) { return xx; }
00094         };
00095     } // namespace Detail
00096 
00097     /**
00098        A bare-bones non-intrusive reference-counted pointer type
00099        with the ability for the client to specify a
00100        finalization/destruction functor for the pointed-to type.
00101 
00102        HandleT must be a non-CVP-qualified type. As a special
00103        case, if HandleT is void then some code in this class will
00104        work a bit differently, notably the operator*(), because we
00105        cannot form a reference to void. Void is supported because
00106        (void *) is commonly used for opaque handles (e.g. libdl)
00107        or multibyte string pointers (e.g. libsqlite3).
00108 
00109        FinalizerT must be a type compatible with the
00110        plain_delete_finalizer interface. A default-constructed
00111        instance of that FinalizerT type will be created to
00112        "finalize" an object when the reference count for that
00113        object drops to zero. The exact behaviour of the FinalizerT
00114        is not specified here, but semantically it must "finalize"
00115        the object passed to it. The default finalizer simply
00116        deletes the object, whereas a more advanced finalizer might
00117        push the object into a garbage collection pool. For
00118        purposes of this class, after finalization of an object,
00119        client code (and this type) should no longer use the object
00120        - it is considered to be destroyed.
00121 
00122        This type does not currently have any built-in support for
00123        copy-on-write, so all copies are extremely shallow.
00124 
00125        Notes of Utmost Significance to Potential Users:
00126 
00127        - Implicit conversions to/from HandleT are not implemented
00128        after much deliberation on the subject. Clients will *have*
00129        to know they're using this class, as opposed to a plain
00130        pointer type. This is safest for everyone, IMO.
00131 
00132        - Don't mix plain and rcptr-hosted pointers, as the rcptr
00133        wrappers own the pointers and will clean them up, leaving
00134        any unadorned pointers dangling.
00135 
00136        - Thread safety: no special guarantees, along with lots of
00137        caveats and potential gotchas.
00138 
00139        - Don't mix different smart pointer types, not even
00140        rcptrs with the same HandleT type but different
00141        FinalizerT types. This will almost certainly bring about
00142        the incorrect finalization of a pointer.
00143 
00144        - The usage of a finalizer functor means that this type can
00145        be used with arbitrary types, regardless of whether the
00146        delete operation is legal or not on them. For example, the
00147        client code for which this class was written uses a functor
00148        to finalize sqlite3 database handles using the
00149        sqlite3_close() function.
00150        
00151 
00152 
00153        Design notes:
00154 
00155        - While originally based off of the presentation of
00156        rc-pointers in Meyers' "More Effective C++", Item 29, i
00157        believe his approach to storing the reference count in his
00158        RCIPtr class is flawed, as it allows multiple rc-pointers
00159        to delete the same pointer. Consider:
00160 
00161 \code
00162        typedef RCIPtr<MyType> myPtrType;
00163        MyType * t = new MyType;
00164        myPtrType x(t);
00165        myPtrType y(t);
00166 \endcode
00167 
00168        In theory, his presentation (admittedly 10+ years old now)
00169        would cause a double-delete for that case. In this model,
00170        that case is handled as if we had constructed y using y(x)
00171        instead of y(t), so both x and y share the reference count.
00172 
00173        - The reference count is stored in a static-space std::map,
00174        and that map is specific to this type and its combination
00175        of HandleT/FinalizerT types. If we made the map only
00176        specific to the HandleT, then we would get
00177        strange/undesired behaviour when we did:
00178 
00179 \code
00180        rcptr<T1,finalizerT1> p1( new T1 );
00181        rcptr<T2,finalizerT2> p2( p1.get() );
00182 \endcode
00183 
00184            because the actual finalizer used would be the one for
00185            which the rcptr is destroyed *last*. Since destruction
00186            order is not always determinate, this mixture would be a
00187            bad idea. Note that it is still illegal to add the same
00188            pointer to multiple different shared pointer types. The
00189            above example, while illegal, will at least cause
00190            determinate behaviour: a double finalization (but the order
00191        is still unspecified in the general case)!
00192 
00193     */
00194     template <typename HandleT,
00195           typename FinalizerT = plain_delete_finalizer>
00196     class rcptr
00197     {
00198     public:
00199         /**
00200            The basic type of object pointed to.
00201         */
00202         typedef HandleT type;
00203         /**
00204            The basic pointer type.
00205         */
00206         typedef type * pointer_type;
00207         /** The type of functor used to clean up pointer_type objects. */
00208         typedef FinalizerT finalizer_type;
00209     private:
00210         mutable pointer_type m_ptr;
00211         typedef int counter_type;
00212         typedef std::map<pointer_type,counter_type> map_type;
00213         /** Returns a shared map holding the reference
00214             counts for all instances of pointer_type
00215             tracked by this class. This is not
00216             post-main() safe.
00217         */
00218         static map_type & map()
00219         {
00220             static map_type bob;
00221             return bob;
00222         }
00223 
00224         /** 
00225             Decrements the reference count to ptr.  If the
00226             count goes to 0, an instance of finalizer_type is
00227             used to "destruct" ptr.  On success, the current
00228             reference count is returned.  If 0 is returned,
00229             ptr should be considered invalid (though this
00230             actually depends on finalizer_type's
00231             implementation, the semantics are that destruction
00232             leaves us with an unusable object).  On error
00233             (passed a null ptr), a number less than 0 is
00234             returned.
00235         */
00236         static counter_type decrement( pointer_type & ptr )
00237         {
00238             if( ! ptr ) return false;
00239             typename map_type::iterator it = map().find(ptr);
00240             if( map().end() == it ) return false;
00241             if( 0 == (*it).second ) return 0; // can happen, e.g., if take() is called.
00242             counter_type rc = --(*it).second;
00243             if ( 0 == rc )
00244             {
00245                 map().erase( it );
00246                 finalizer_type()( ptr );
00247             }
00248             return rc;
00249         }
00250 
00251         /**
00252            If ! ptr, does nothing, else it increases the
00253            reference count for ptr by one. Returns the current
00254            reference count (guaranteed to be 1 or higher) on
00255            success, or a negative number if passed a null ptr.
00256         */
00257         static counter_type increment( pointer_type & ptr )
00258         {
00259             if( ! ptr ) return -1;
00260             return ++(map()[ptr]);
00261         }
00262     public:
00263         /**
00264            Transfers ownership of h, or allows h to
00265            participate in ownership with other rcptr
00266            objects pointing at h.
00267         */
00268         explicit rcptr( pointer_type h ) : m_ptr(h)
00269         {
00270             this->increment( this->m_ptr );
00271         }
00272         /**
00273            rhs and this object will both manage the same
00274            underlying pointer.
00275         */
00276         rcptr( rcptr const & rhs ) : m_ptr(rhs.m_ptr)
00277         {
00278             this->increment( this->m_ptr );
00279         }
00280         /**
00281            First disowns any connected pointer (using
00282            take(0)), then rhs and this object will
00283            both manage the same underlying pointer. If
00284            by chance rhs.get() == this->get() when
00285            this function starts then this function
00286            does nothing and has no side effects.
00287         */
00288         rcptr & operator=( rcptr const & rhs )
00289         {
00290             if( rhs.m_ptr == this->m_ptr ) return *this;
00291             this->decrement( this->m_ptr );
00292             this->m_ptr = rhs.m_ptr;
00293             this->increment( this->m_ptr );
00294             return *this;
00295         }
00296         /**
00297            An empty shared pointer, useful only as a target of
00298            assigment or take().
00299         */
00300         rcptr() : m_ptr(0)
00301         {}
00302 
00303         /**
00304            Efficiently swaps this object and rhs, such that they
00305            swap ownership of their underlying pointers.
00306            This does not require fiddling with the reference
00307            counts, so it is much faster than using an rcptr
00308            copy to perform a swap.
00309         */
00310         void swap( rcptr & rhs )
00311         {
00312             if( this->m_ptr != rhs )
00313             {
00314                 pointer_type x = this->m_ptr;
00315                 this->m_ptr = rhs.m_ptr;
00316                 rhs.m_ptr = x;
00317             }
00318         }
00319 
00320 
00321         /**
00322            See decrement();
00323         */
00324         ~rcptr()
00325         {
00326             this->decrement( this->m_ptr );
00327         }
00328 
00329         /** Returns (this->m_ptr == rhs.m_ptr). */
00330         bool operator==( rcptr const & rhs ) const
00331         {
00332             return this->m_ptr == rhs.m_ptr;
00333         }
00334         /** Returns (this->m_ptr != rhs.m_ptr). */
00335         bool operator!=( rcptr const & rhs ) const
00336         {
00337             return this->m_ptr != rhs.m_ptr;
00338         }
00339 
00340         /**
00341            Returns this->get() < rhs.get(). Implemented so that
00342            this type can be used as keys in STL containers.
00343         */
00344         bool operator<( rcptr const & rhs ) const
00345         {
00346             return this->m_ptr < rhs.m_ptr;
00347         }
00348 
00349         /** Returns this object's underlying pointer, which
00350             may be 0. This does not transfer ownership. This
00351             object still owns (or participates in the
00352             ownership of) the returned pointer.
00353         */
00354         pointer_type get() const { return this->m_ptr; }
00355 
00356         /**
00357            Gives ownership of p to this object (or a
00358            collection of like-types rcptr objects). Drops
00359            ownership of this->get() before making the
00360            takeover. If (this->get() == p) then this function
00361            does nothing.
00362         */
00363         void take( pointer_type p )
00364         {
00365             if( p == this->m_ptr ) return;
00366             this->decrement( this->m_ptr );
00367             this->increment( this->m_ptr = p );
00368         }
00369 
00370         /**
00371            Transfers ownership of this->get() to the caller.
00372 
00373            ALL rcptr<> objects which point to that object
00374            (except this rcptr) STILL point to that object,
00375            but they will not activate the destructor functor
00376            when they die, so they are safe as long as they
00377            remain unsued or are destroyed before the "raw"
00378            pointer returned from this function is destroyed.
00379         */
00380         pointer_type take()
00381         {
00382             if( this->m_ptr )
00383             {
00384                 pointer_type t = this->m_ptr;
00385                 this->map().erase( this->m_ptr );
00386                 this->m_ptr = 0;
00387                 return t;
00388             }
00389             return this->m_ptr;
00390         }
00391 
00392         /**
00393            The same as this->get().
00394         */
00395         pointer_type operator->() const { return this->m_ptr; }
00396 
00397 
00398         /**
00399            reference_type is the same as (T&) unless T is void,
00400            in which case it is the same as (void*&) because
00401            (void&) is not legal.
00402         */
00403         typedef typename Detail::ref_type<type>::type reference_type;
00404 
00405         /**
00406            The same as *(this->get()). Behaviour is undefined
00407            if (!this->get()). We would throw an exception, but
00408            this code is specifically intended for use on
00409            platforms where exceptions are not allowed or not
00410            supported (e.g. some embedded platforms).
00411 
00412            SPECIAL CASE: rcptr::type is void
00413 
00414            If rcptr::type is void then this function returns a
00415            refernce to a pointer instead of a reference. This
00416            is to allow this type to work with (void*) handle
00417            types, such as handles returned from dlopen() or
00418            memory returned from malloc(). Finalizers for such
00419            handles could call dlclose() or free(), as
00420            appropriate.
00421         */
00422         reference_type operator*() const
00423         {
00424             return Detail::ref_type<type>::deref( this->m_ptr );
00425         }
00426         /**
00427            Returns the number of references to this object's pointer,
00428            or zero if no pointer is bound. This function should be
00429            considered a debugging/informational function, and not
00430            a "feature" of this type.
00431 
00432            Complexity = that of a std::map lookup.
00433         */
00434         size_t ref_count() const
00435         {
00436             if( ! this->m_ptr ) return 0;
00437             typename map_type::iterator it = map().find(this->m_ptr);
00438             return ( map().end() == it )  ? 0 : (*it).second;
00439         }
00440 
00441         /**
00442            Returns the same as (!this->get()).
00443         */
00444         bool empty() const { return 0 == this->m_ptr; }
00445 
00446 // Adding deep copy support requires a copy ctor/functor for our
00447 // pointee type, but this type should/must be usable with opaque
00448 // pointer handles as well as object pointers (e.g. sqlite3 db handles
00449 // and ncurses WINDOW handles). But if you did want to implement
00450 // copy(), here's how you might go about doing it...
00451 //      /**
00452 //         Makes a copy of p using (new type(*p)) and
00453 //         transfers ownership of that copy to this
00454 //         object. Further copies of this object will point to
00455 //         that copy unless/until copy() is called on
00456 //         them. This function is intended to simplify
00457 //         implementation of copy-on-write. If p is null then
00458 //         this object points to null.
00459 
00460 //         To force an rcptr to copy its current pointer, simply
00461 //         call ptr.copy( ptr.get() ).
00462 //      */
00463 //      void copy( pointer_type p )
00464 //      {
00465 //          pointer_type x = this->m_ptr;
00466 //          if( p )
00467 //          {
00468 //              this->m_ptr = new type(*p);
00469 //              this->increment( this->m_ptr );
00470 //          }
00471 //          else
00472 //          {
00473 //              this->m_ptr = 0;
00474 //          }
00475 //          this->decrement(x);
00476 //      }
00477 // Some things to consider:
00478 //      bool m_shareable;
00479 //      bool shareable() const { return this->m_shareable; }
00480 //      void shareable( bool s ) { this->m_shareable = s; }
00481 //      bool shared() const { return this->ref_count() > 1; }
00482 
00483 
00484     };
00485 
00486 
00487 }} // namespaces
00488 
00489 
00490 #endif // s11n_net_s11n_refcount_REFCOUNT_HPP_INCLUDED

Generated on Wed Jun 4 21:45:18 2008 for libs11n by  doxygen 1.5.3