libsq3  2007.10.18
refcount.hpp
1 #ifndef s11n_net_refcount_REFCOUNT_HPP_INCLUDED
2 #define s11n_net_refcount_REFCOUNT_HPP_INCLUDED 1
3 // reminders to self:
4 // - think about lazyassptr<T> which lazily instantiates its
5 // pointee. That would take a Constructor functor, providing symmetry
6 // with rcptr<>.
7 
8 #include <map>
9 
10 
11 /**
12  The refcount namespace encapsulates code for a reference-counted
13  smart pointer. It is capable of tracking and destroying objects and
14  arbitrary pointers (including void pointers) and destroying them
15  using a user-defined finalizer functor. This allows, e.g., the
16  reference-counted sharing of memory allocated via malloc() or by
17  third-party functions such as dlopen() or sqlite3_open().
18 
19  This code is not generic, industrial-strength reference counting
20  and is as much an experiment as anything else.
21 
22  Author: stephan at s11n dot net
23 
24  License: Public Domain
25 */
26 namespace refcount {
27 
28 
29  /**
30  A no-op "destructor" for use with rcptr.
31  */
33  {
34  /** Assigs t to 0 without deleting t. */
35  template <typename T>
36  void operator()( T * & t )
37  {
38  t = 0;
39  }
40  };
41 
42  /**
43  The default destructor/cleanup functor for use with
44  rcptr<>.
45  */
47  {
48  /**
49  Calls delete t and assigns t to 0.
50 
51  Specialized dtors need not call delete, but should
52  assign t to 0, as this simplifies some client code.
53 
54  T must be non-CVP-qualified and for this
55  implementation (delete t) must be legal.
56  */
57  template <typename T>
58  void operator()( T * & t )
59  {
60  delete t;
61  t = 0;
62  }
63  };
64 
65  /**
66  All classes in this namespace are "internal details" of the
67  classes in the refcount namespace, and should not be
68  directly used by client code.
69  */
70  namespace Detail
71  {
72  /**
73  Internal detail for dereferencing pointers.
74  */
75  template <typename T>
76  struct ref_type
77  {
78  /** Same as (T&). */
79  typedef T & type;
80  /** Returns *t. */
81  static type deref( T *t ) { return *t; }
82  };
83  /**
84  Internal detail for dereferencing pointers.
85  */
86  template <>
87  struct ref_type<void>
88  {
89  /** Same as (void*&). */
90  typedef void * & type;
91  /** Returns xx. */
92  static type deref( type xx ) { return xx; }
93  };
94  } // namespace Detail
95 
96  /**
97  A bare-bones non-intrusive reference-counted pointer type
98  with the ability for the client to specify a
99  finalization/destruction functor for the pointed-to type.
100 
101  HandleT must be a non-CVP-qualified type. As a special
102  case, if HandleT is void then some code in this class will
103  work a bit differently, notably the operator*(), because we
104  cannot form a reference to void. Void is supported because
105  (void *) is commonly used for opaque handles (e.g. libdl)
106  or multibyte string pointers (e.g. libsqlite3).
107 
108  FinalizerT must be a type compatible with the
109  plain_delete_finalizer interface. A default-constructed
110  instance of that FinalizerT type will be created to
111  "finalize" an object when the reference count for that
112  object drops to zero. The exact behaviour of the FinalizerT
113  is not specified here, but semantically it must "finalize"
114  the object passed to it. The default finalizer simply
115  deletes the object, whereas a more advanced finalizer might
116  push the object into a garbage collection pool. For
117  purposes of this class, after finalization of an object,
118  client code (and this type) should no longer use the object
119  - it is considered to be destroyed.
120 
121  This type does not currently have any built-in support for
122  copy-on-write, so all copies are extremely shallow.
123 
124  Notes of Utmost Significance to Potential Users:
125 
126  - Implicit conversions to/from HandleT are not implemented
127  after much deliberation on the subject. Clients will *have*
128  to know they're using this class, as opposed to a plain
129  pointer type. This is safest for everyone, IMO.
130 
131  - Don't mix plain and rcptr-hosted pointers, as the rcptr
132  wrappers own the pointers and will clean them up, leaving
133  any unadorned pointers dangling.
134 
135  - Thread safety: no special guarantees, along with lots of
136  caveats and potential gotchas.
137 
138  - Don't mix different smart pointer types, not even
139  rcptrs with the same HandleT type but different
140  FinalizerT types. This will almost certainly bring about
141  the incorrect finalization of a pointer.
142 
143  - The usage of a finalizer functor means that this type can
144  be used with arbitrary types, regardless of whether the
145  delete operation is legal or not on them. For example, the
146  client code for which this class was written uses a functor
147  to finalize sqlite3 database handles using the
148  sqlite3_close() function.
149 
150 
151 
152  Design notes:
153 
154  - While originally based off of the presentation of
155  rc-pointers in Meyers' "More Effective C++", Item 29, i
156  believe his approach to storing the reference count in his
157  RCIPtr class is flawed, as it allows multiple rc-pointers
158  to delete the same pointer. Consider:
159 
160 \code
161  typedef RCIPtr<MyType> myPtrType;
162  MyType * t = new MyType;
163  myPtrType x(t);
164  myPtrType y(t);
165 \endcode
166 
167  In theory, his presentation (admittedly 10+ years old now)
168  would cause a double-delete for that case. In this model,
169  that case is handled as if we had constructed y using y(x)
170  instead of y(t), so both x and y share the reference count.
171 
172  - The reference count is stored in a static-space std::map,
173  and that map is specific to this type and its combination
174  of HandleT/FinalizerT types. If we made the map only
175  specific to the HandleT, then we would get
176  strange/undesired behaviour when we did:
177 
178 \code
179  rcptr<T1,finalizerT1> p1( new T1 );
180  rcptr<T2,finalizerT2> p2( p1.get() );
181 \endcode
182 
183  because the actual finalizer used would be the one for
184  which the rcptr is destroyed *last*. Since destruction
185  order is not always determinate, this mixture would be a
186  bad idea. Note that it is still illegal to add the same
187  pointer to multiple different shared pointer types. The
188  above example, while illegal, will at least cause
189  determinate behaviour: a double finalization (but the order
190  is still unspecified in the general case)!
191 
192 
193  Fundamental differences between rcptr and
194  boost::shared_ptr:
195 
196  - rcptr::take() allows client to take ownership of a
197  pointer away from rcptr. According to the shared_ptr FAQ,
198  this isn't technically feasible in that class due to their
199  handling of the user-defined finalizer.
200 
201  - rcptr has no explicit support for multi-threading.
202 
203  - shared_ptr does not handle the following code "correctly"
204  (IMO):
205 
206  typedef boost::shared_ptr<AStruct> SP;
207  SP sp1( new AStruct );
208  SP sp2( sp1.get() );
209  // sp1.use_count() is 1, not 2
210  // This causes a double deletion
211 
212  rcptr handles that case transparently.
213 
214  */
215  template <typename HandleT,
216  typename FinalizerT = plain_delete_finalizer>
217  class rcptr
218  {
219  public:
220  /**
221  The basic type of object pointed to.
222  */
223  typedef HandleT type;
224  /**
225  The basic pointer type.
226  */
227  typedef type * pointer_type;
228  /** The type of functor used to clean up pointer_type objects. */
229  typedef FinalizerT finalizer_type;
230  private:
231  mutable pointer_type m_ptr;
232  typedef int counter_type;
233  typedef std::map<pointer_type,counter_type> map_type;
234  /** Returns a shared map holding the reference
235  counts for all instances of pointer_type
236  tracked by this class. This is not
237  post-main() safe.
238  */
239  static map_type & map()
240  {
241  static map_type bob;
242  return bob;
243  }
244 
245  /**
246  Decrements the reference count to ptr. If the
247  count goes to 0, an instance of finalizer_type is
248  used to "destruct" ptr. On success, the current
249  reference count is returned. If 0 is returned,
250  ptr should be considered invalid (though this
251  actually depends on finalizer_type's
252  implementation, the semantics are that destruction
253  leaves us with an unusable object). On error
254  (passed a null ptr), a number less than 0 is
255  returned.
256  */
257  static counter_type decrement( pointer_type & ptr )
258  {
259  if( ! ptr ) return false;
260  typename map_type::iterator it = map().find(ptr);
261  if( map().end() == it ) return false;
262  if( 0 == (*it).second ) return 0; // can happen, e.g., if take() is called.
263  counter_type rc = --(*it).second;
264  if ( 0 == rc )
265  {
266  map().erase( it );
267  finalizer_type()( ptr );
268  }
269  return rc;
270  }
271 
272  /**
273  If ! ptr, does nothing, else it increases the
274  reference count for ptr by one. Returns the current
275  reference count (guaranteed to be 1 or higher) on
276  success, or a negative number if passed a null ptr.
277  */
278  static counter_type increment( pointer_type & ptr )
279  {
280  if( ! ptr ) return -1;
281  return ++(map()[ptr]);
282  }
283 
284 // bool safety_first()
285 // {
286 // if( ! this->m_ptr ) return false;
287 // if( 0 == this->ref_count() )
288 // { // dangling pointer, it seems
289 // this->m_ptr = 0;
290 // }
291 // return 0 != this->m_ptr;
292 // }
293  public:
294  /**
295  Transfers ownership of h, or allows h to
296  participate in ownership with other rcptr
297  objects pointing at h.
298  */
299  explicit rcptr( pointer_type h ) : m_ptr(h)
300  {
301  this->increment( this->m_ptr );
302  }
303  /**
304  rhs and this object will both manage the same
305  underlying pointer.
306  */
307  rcptr( rcptr const & rhs ) : m_ptr(rhs.m_ptr)
308  {
309  this->increment( this->m_ptr );
310  }
311  /**
312  First disowns any connected pointer (using
313  take(0)), then rhs and this object will
314  both manage the same underlying pointer. If
315  by chance rhs.get() == this->get() when
316  this function starts then this function
317  does nothing and has no side effects.
318  */
319  rcptr & operator=( rcptr const & rhs )
320  {
321  if( rhs.m_ptr == this->m_ptr ) return *this;
322  this->decrement( this->m_ptr );
323  this->m_ptr = rhs.m_ptr;
324  this->increment( this->m_ptr );
325  return *this;
326  }
327  /**
328  An empty shared pointer, useful only as a target of
329  assigment or take().
330  */
331  rcptr() : m_ptr(0)
332  {}
333 
334  /**
335  Efficiently swaps this object and rhs, such that they
336  swap ownership of their underlying pointers.
337  This does not require fiddling with the reference
338  counts, so it is much faster than using an rcptr
339  copy to perform a swap.
340  */
341  void swap( rcptr & rhs )
342  {
343  if( this->m_ptr != rhs )
344  {
345  pointer_type x = this->m_ptr;
346  this->m_ptr = rhs.m_ptr;
347  rhs.m_ptr = x;
348  }
349  }
350 
351 
352  /**
353  See decrement();
354  */
356  {
357  this->decrement( this->m_ptr );
358  }
359 
360  /** Returns (this->m_ptr == rhs.m_ptr). */
361  bool operator==( rcptr const & rhs ) const
362  {
363  return this->m_ptr == rhs.m_ptr;
364  }
365  /** Returns (this->m_ptr != rhs.m_ptr). */
366  bool operator!=( rcptr const & rhs ) const
367  {
368  return this->m_ptr != rhs.m_ptr;
369  }
370 
371  /**
372  Returns this->get() < rhs.get(). Implemented so that
373  this type can be used as keys in STL containers.
374  */
375  bool operator<( rcptr const & rhs ) const
376  {
377  return this->m_ptr < rhs.m_ptr;
378  }
379 
380  /** Returns this object's underlying pointer, which
381  may be 0. This does not transfer ownership. This
382  object still owns (or participates in the
383  ownership of) the returned pointer.
384  */
385  pointer_type get() const { return this->m_ptr; }
386 
387  /**
388  Gives ownership of p to this object (or a
389  collection of like-types rcptr objects). Drops
390  ownership of this->get() before making the
391  takeover. If (this->get() == p) then this function
392  does nothing.
393  */
394  void take( pointer_type p )
395  {
396  if( p == this->m_ptr ) return;
397  this->decrement( this->m_ptr );
398  this->increment( this->m_ptr = p );
399  }
400 
401  /**
402  Transfers ownership of this->get() to the caller.
403 
404  ALL rcptr<> objects which point to that object
405  (except this rcptr) STILL point to that object,
406  but they will not activate the destructor functor
407  when they die, so they are safe as long as they
408  remain unsued or are destroyed before the "raw"
409  pointer returned from this function is destroyed.
410  */
412  {
413  if( this->m_ptr )
414  {
415  pointer_type t = this->m_ptr;
416  this->map().erase( this->m_ptr );
417  this->m_ptr = 0;
418  return t;
419  }
420  return this->m_ptr;
421  }
422 
423  /**
424  The same as this->get().
425  */
426  pointer_type operator->() const { return this->m_ptr; }
427 
428 
429  /**
430  reference_type is the same as (T&) unless T is void,
431  in which case it is the same as (void*&) because
432  (void&) is not legal.
433  */
435 
436  /**
437  The same as *(this->get()). Behaviour is undefined
438  if (!this->get()). We would throw an exception, but
439  this code is specifically intended for use on
440  platforms where exceptions are not allowed or not
441  supported (e.g. some embedded platforms).
442 
443  SPECIAL CASE: rcptr::type is void
444 
445  If rcptr::type is void then this function returns a
446  refernce to a pointer instead of a reference. This
447  is to allow this type to work with (void*) handle
448  types, such as handles returned from dlopen() or
449  memory returned from malloc(). Finalizers for such
450  handles could call dlclose() or free(), as
451  appropriate.
452  */
454  {
455  return Detail::ref_type<type>::deref( this->m_ptr );
456  }
457  /**
458  Returns the number of references to this object's pointer,
459  or zero if no pointer is bound. This function should be
460  considered a debugging/informational function, and not
461  a "feature" of this type.
462 
463  Complexity = that of a std::map lookup.
464  */
465  size_t ref_count() const
466  {
467  if( ! this->m_ptr ) return 0;
468  typename map_type::iterator it = map().find(this->m_ptr);
469  return ( map().end() == it ) ? 0 : (*it).second;
470  }
471 
472  /**
473  Returns the same as (!this->get()).
474  */
475  bool empty() const { return 0 == this->m_ptr; }
476 
477 // Adding deep copy support requires a copy ctor/functor for our
478 // pointee type, but this type should/must be usable with opaque
479 // pointer handles as well as object pointers (e.g. sqlite3 db handles
480 // and ncurses WINDOW handles). But if you did want to implement
481 // copy(), here's how you might go about doing it...
482 // /**
483 // Makes a copy of p using (new type(*p)) and
484 // transfers ownership of that copy to this
485 // object. Further copies of this object will point to
486 // that copy unless/until copy() is called on
487 // them. This function is intended to simplify
488 // implementation of copy-on-write. If p is null then
489 // this object points to null.
490 
491 // To force an rcptr to copy its current pointer, simply
492 // call ptr.copy( ptr.get() ).
493 // */
494 // void copy( pointer_type p )
495 // {
496 // pointer_type x = this->m_ptr;
497 // if( p )
498 // {
499 // this->m_ptr = new type(*p);
500 // this->increment( this->m_ptr );
501 // }
502 // else
503 // {
504 // this->m_ptr = 0;
505 // }
506 // this->decrement(x);
507 // }
508 // Some things to consider:
509 // bool m_shareable;
510 // bool shareable() const { return this->m_shareable; }
511 // void shareable( bool s ) { this->m_shareable = s; }
512 // bool shared() const { return this->ref_count() > 1; }
513 
514 
515  };
516 
517 
518 } // namespaces
519 
520 
521 #endif // s11n_net_refcount_REFCOUNT_HPP_INCLUDED
The refcount namespace encapsulates code for a reference-counted smart pointer.
Definition: refcount.hpp:26
rcptr(rcptr const &rhs)
rhs and this object will both manage the same underlying pointer.
Definition: refcount.hpp:307
bool operator==(rcptr const &rhs) const
Returns (this->m_ptr == rhs.m_ptr).
Definition: refcount.hpp:361
type * pointer_type
The basic pointer type.
Definition: refcount.hpp:227
void *& type
Same as (void*&).
Definition: refcount.hpp:90
bool operator<(rcptr const &rhs) const
Returns this->get() < rhs.get().
Definition: refcount.hpp:375
void operator()(T *&t)
Assigs t to 0 without deleting t.
Definition: refcount.hpp:36
rcptr & operator=(rcptr const &rhs)
First disowns any connected pointer (using take(0)), then rhs and this object will both manage the sa...
Definition: refcount.hpp:319
void operator()(T *&t)
Calls delete t and assigns t to 0.
Definition: refcount.hpp:58
rcptr()
An empty shared pointer, useful only as a target of assigment or take().
Definition: refcount.hpp:331
void take(pointer_type p)
Gives ownership of p to this object (or a collection of like-types rcptr objects).
Definition: refcount.hpp:394
pointer_type take()
Transfers ownership of this->get() to the caller.
Definition: refcount.hpp:411
The default destructor/cleanup functor for use with rcptr<>.
Definition: refcount.hpp:46
bool operator!=(rcptr const &rhs) const
Returns (this->m_ptr != rhs.m_ptr).
Definition: refcount.hpp:366
A no-op "destructor" for use with rcptr.
Definition: refcount.hpp:32
static type deref(type xx)
Returns xx.
Definition: refcount.hpp:92
void swap(rcptr &rhs)
Efficiently swaps this object and rhs, such that they swap ownership of their underlying pointers.
Definition: refcount.hpp:341
Detail::ref_type< type >::type reference_type
reference_type is the same as (T&) unless T is void, in which case it is the same as (void*&) because...
Definition: refcount.hpp:434
pointer_type get() const
Returns this object's underlying pointer, which may be 0.
Definition: refcount.hpp:385
HandleT type
The basic type of object pointed to.
Definition: refcount.hpp:223
FinalizerT finalizer_type
The type of functor used to clean up pointer_type objects.
Definition: refcount.hpp:229
size_t ref_count() const
Returns the number of references to this object's pointer, or zero if no pointer is bound.
Definition: refcount.hpp:465
static type deref(T *t)
Returns *t.
Definition: refcount.hpp:81
reference_type operator *() const
The same as *(this->get()).
Definition: refcount.hpp:453
~rcptr()
See decrement();.
Definition: refcount.hpp:355
Internal detail for dereferencing pointers.
Definition: refcount.hpp:76
T & type
Same as (T&).
Definition: refcount.hpp:79
rcptr(pointer_type h)
Transfers ownership of h, or allows h to participate in ownership with other rcptr objects pointing a...
Definition: refcount.hpp:299
bool empty() const
Returns the same as (!this->get()).
Definition: refcount.hpp:475
pointer_type operator->() const
The same as this->get().
Definition: refcount.hpp:426