libsq3 2007.10.18

refcount.hpp

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