c++-gtk-utils
intrusive_ptr.h
Go to the documentation of this file.
1 /* Copyright (C) 2006 to 2013 Chris Vine
2 
3 The library comprised in this file or of which this file is part is
4 distributed by Chris Vine under the GNU Lesser General Public
5 License as follows:
6 
7  This library is free software; you can redistribute it and/or
8  modify it under the terms of the GNU Lesser General Public License
9  as published by the Free Software Foundation; either version 2.1 of
10  the License, or (at your option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License, version 2.1, for more details.
16 
17  You should have received a copy of the GNU Lesser General Public
18  License, version 2.1, along with this library (see the file LGPL.TXT
19  which came with this source code package in the c++-gtk-utils
20  sub-directory); if not, write to the Free Software Foundation, Inc.,
21  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 
23 However, it is not intended that the object code of a program whose
24 source code instantiates a template from this file or uses macros or
25 inline functions (of any length) should by reason only of that
26 instantiation or use be subject to the restrictions of use in the GNU
27 Lesser General Public License. With that in mind, the words "and
28 macros, inline functions and instantiations of templates (of any
29 length)" shall be treated as substituted for the words "and small
30 macros and small inline functions (ten lines or less in length)" in
31 the fourth paragraph of section 5 of that licence. This does not
32 affect any other reason why object code may be subject to the
33 restrictions in that licence (nor for the avoidance of doubt does it
34 affect the application of section 2 of that licence to modifications
35 of the source code in this file).
36 
37 */
38 
39 #ifndef CGU_INTRUSIVE_PTR_H
40 #define CGU_INTRUSIVE_PTR_H
41 
42 // define this if, instead of GLIB atomic funcions/memory barriers,
43 // you want to use a (slower) mutex to lock the reference count in the
44 // IntrusiveLockCounter class
45 /* #define CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX 1 */
46 
47 #include <utility> // for std::move and std::swap
48 #include <functional> // for std::less and std::hash<T*>
49 #include <cstddef> // for std::size_t
50 
51 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
52 #include <c++-gtk-utils/mutex.h>
53 #else
54 #include <glib.h>
55 #endif
56 
58 
59 /**
60  * @addtogroup handles handles and smart pointers
61  */
62 
63 namespace Cgu {
64 
65 /**
66  * @class IntrusivePtr intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
67  * @brief This is a smart pointer for managing objects allocated on
68  * freestore which maintain their own reference count.
69  * @ingroup handles
70  *
71  * @details This is a class which manages objects which maintain their
72  * own reference count. It requires that the referenced object has
73  * two functions called ref() and unref(), which increment and
74  * decrement the reference count respectively. The IntrusiveCounter
75  * or IntrusiveLockCounter class can be inherited from to do this, but
76  * they do not have to be used. The IntrusiveLockCounter class is the
77  * same as the IntrusiveCounter class, except that it locks the
78  * reference count when it is incremented or decremented in order that
79  * IntrusivePtr objects in different threads can access the same
80  * object. (But only the reference count is locked, not the methods
81  * of the referenced object.)
82  *
83  * All the constructors (including the constructor which takes a raw
84  * pointer) increment the reference count, and the destructor
85  * decrements it and expects the referenced object to be deleted when
86  * the last IntrusivePtr referencing a particular object is destroyed.
87  * The IntrusiveCounter and IntrusiveLockCounter classes behave in
88  * this way. (This is different from the behaviour of GobjHandle
89  * smart pointers, which are constrained by the GObject reference
90  * counting system which begins with a reference count of 1 rather
91  * than 0, and of course different from normal shared pointer
92  * implementations for the same reason. The advantage of the approach
93  * with IntrusivePtr is that an already-managed object may safely be
94  * passed to the constructor taking a raw pointer without any
95  * additional steps being necessary.)
96  */
97 
98 template <class T> class IntrusivePtr {
99 
100  T* obj_p;
101 
102  void unreference() {
103  if (obj_p) obj_p->unref();
104  }
105 
106  void reference() {
107  if (obj_p) obj_p->ref();
108  }
109 
110 public:
111  /**
112  * This constructor does not throw unless the managed object's ref()
113  * function throws (which would not happen with any sane
114  * implementation, and does not happen with the IntrusiveCounter and
115  * IntrusiveLockCounter classes).
116  * @param ptr The object which the IntrusivePtr is to manage (if
117  * any).
118  */
119  explicit IntrusivePtr(T* ptr = 0) {
120  obj_p = ptr;
121  reference();
122  }
123 
124  /**
125  * This copy constructor does not throw unless the managed object's
126  * ref() function throws (which would not happen with any sane
127  * implementation, and does not happen with the IntrusiveCounter and
128  * IntrusiveLockCounter classes).
129  * @param intr_ptr The intrusive pointer to be copied.
130  */
131  IntrusivePtr(const IntrusivePtr& intr_ptr) {
132  obj_p = intr_ptr.obj_p;
133  reference();
134  }
135 
136  /**
137  * The move constructor does not throw. It has move semantics.
138  * @param intr_ptr The instrusive pointer to be moved.
139  */
140  IntrusivePtr(IntrusivePtr&& intr_ptr) noexcept {
141  obj_p = intr_ptr.obj_p;
142  intr_ptr.obj_p = 0;
143  }
144 
145  template <class U> friend class IntrusivePtr;
146 
147  /**
148  * A version of the copy constructor which enables pointer type
149  * conversion (assuming the type passed is implicitly type
150  * convertible to the managed type, such as a derived type). This
151  * copy constructor does not throw unless the managed object's ref()
152  * function throws (which would not happen with any sane
153  * implementation, and does not happen with the IntrusiveCounter and
154  * IntrusiveLockCounter classes).
155  * @param intr_ptr The intrusive pointer to be copied.
156  */
157  template <class U> IntrusivePtr(const IntrusivePtr<U>& intr_ptr) {
158  obj_p = intr_ptr.obj_p;
159  reference();
160  }
161 
162  /**
163  * A version of the move constructor which enables pointer type
164  * conversion (assuming the type passed is implicitly type
165  * convertible to the managed type, such as a derived type). This
166  * move constructor does not throw.
167  * @param intr_ptr The intrusive pointer to be moved.
168  */
169  template <class U> IntrusivePtr(IntrusivePtr<U>&& intr_ptr) noexcept {
170  obj_p = intr_ptr.obj_p;
171  intr_ptr.obj_p = 0;
172  }
173 
174  /**
175  * This method (and so copy or move assignment) does not throw unless
176  * the unref() function or destructor of a managed object throws.
177  * @param intr_ptr The assignee.
178  * @return The IntrusivePtr object after assignment.
179  */
180  // having a value type as the argument, rather than reference to const
181  // and then initialising a tmp object, gives the compiler more scope
182  // for optimisation, and also caters for r-values without a separate
183  // overload
185  std::swap(obj_p, intr_ptr.obj_p);
186  return *this;
187  }
188 
189  /**
190  * A version of the assignment operator which enables pointer type
191  * conversion (assuming the type passed is implicitly type
192  * convertible to the managed type, such as a derived type). This
193  * method does not throw unless the unref() function or destructor of
194  * a managed object throws.
195  * @param intr_ptr The assignee.
196  * @return The IntrusivePtr object after assignment.
197  */
198  template <class U> IntrusivePtr& operator=(const IntrusivePtr<U>& intr_ptr) {
199  return operator=(IntrusivePtr(intr_ptr));
200  }
201 
202  /**
203  * A version of the operator for move assignment which enables
204  * pointer type conversion (assuming the type passed is implicitly
205  * type convertible to the managed type, such as a derived type).
206  * This method does not throw unless the unref() function or
207  * destructor of a managed object throws.
208  * @param intr_ptr The intrusive pointer to be moved.
209  * @return The IntrusivePtr object after the move operation.
210  */
211  template <class U> IntrusivePtr& operator=(IntrusivePtr<U>&& intr_ptr) {
212  return operator=(IntrusivePtr(std::move(intr_ptr)));
213  }
214 
215 /**
216  * This method does not throw.
217  * @return A pointer to the managed object (or NULL if none is
218  * handled).
219  */
220  T* get() const noexcept {return obj_p;}
221 
222  /**
223  * This method does not throw.
224  * @return A reference to the managed object.
225  */
226  T& operator*() const noexcept {return *obj_p;}
227 
228  /**
229  * This method does not throw.
230  * @return A pointer to the managed object (or NULL if none is
231  * handled).
232  */
233  T* operator->() const noexcept {return obj_p;}
234 
235  /**
236  * Causes the intrusive pointer to cease to manage its managed
237  * object, deleting it if this is the last intrusive pointer managing
238  * it. If the argument passed is not NULL, the intrusive pointer
239  * will manage the new object passed. This method does not throw
240  * unless the the unref() function or destructor of a managed object
241  * throws.
242  * @param ptr NULL (the default), or a new object to manage.
243  */
244  void reset(T* ptr = 0) {
245  IntrusivePtr tmp(ptr);
246  std::swap(obj_p, tmp.obj_p);
247  }
248 
249  /**
250  * The destructor does not throw unless the destructor of a managed
251  * object throws - that should never happen.
252  */
253  ~IntrusivePtr() {unreference();}
254 };
255 
256 /**
257  * @class IntrusiveCounter intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
258  * @brief This is a counter class providing the ref() and unref()
259  * functions required by IntrusivePtr.
260  * @ingroup handles
261  * @sa IntrusiveLockCounter.
262  *
263  * This is a counter class providing the ref() and unref() functions
264  * required by IntrusivePtr. It is intended to be inherited from by
265  * classes which are to be managed by such a smart pointer.
266  */
267 
269  unsigned int count;
270 
271 public:
272  // we should not be able to copy objects of this class
273  // - objects, if constructed on the heap, should be passed
274  // via an IntrusivePtr object. An object of a derived class
275  // might still copy itself via its copy constructor and take
276  // along a new base IntrusiveCounter object constructed via
277  // the default constructor, and thus have a correct reference
278  // count of 0, but derived classes should not try to provide
279  // their own assignment operators.
280 /**
281  * This class cannot be copied. The copy constructor is deleted.
282  */
284 /**
285  * This class cannot be copied. The assignment operator is deleted.
286  */
288 
289 /**
290  * Increments the reference count. This method does not throw.
291  */
292  void ref() noexcept {++count;}
293 
294 /**
295  * Decrements the reference count, and if the count reaches 0 deletes
296  * itself (ie the managed object). This method does not throw unless
297  * the destructor of a derived class throws - that should never
298  * happen.
299  */
300  void unref() {
301  --count;
302  if (count == 0) delete this;
303  }
304 
305 /**
306  * The constructor does not throw.
307  */
308  IntrusiveCounter() noexcept: count(0) {}
309 
310 /**
311  * This destructor does not throw, unless the destructor of a derived
312  * class throws - that should never happen.
313  */
314  virtual ~IntrusiveCounter() {}
315 };
316 
317 /**
318  * @class IntrusiveLockCounter intrusive_ptr.h c++-gtk-utils/intrusive_ptr.h
319  * @brief This is a counter class providing the ref() and unref()
320  * functions required by IntrusivePtr, with a thread safe reference
321  * count..
322  * @ingroup handles
323  * @sa IntrusiveCounter.
324  *
325  * This is a counter class providing the ref() and unref() functions
326  * required by IntrusivePtr. It is intended to be inherited from by
327  * classes which are to be managed by such a smart pointer, and
328  * includes synchronization so that such an inheriting class object
329  * can be accessed by different IntrusivePtr objects in different
330  * threads (although the word Lock is in the title, by default it uses
331  * glib atomic functions to access the reference count rather than a
332  * mutex, so the overhead should be very small). Note that only the
333  * reference count is protected, so this is thread safe in the sense
334  * in which a raw pointer is thread safe.
335  *
336  * As mentioned, by default glib atomic functions are used to provide
337  * thread-safe manipulation of the reference count. However, the
338  * symbol CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX can be defined so that
339  * the library uses mutexes instead, which might be useful for some
340  * debugging purposes. Note that if
341  * CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX is to be defined, this is best
342  * done by textually amending the intrusive_ptr.h header file before
343  * the library is compiled. This will ensure that everything in the
344  * program and the library which includes the intrusive_ptr.h header
345  * is guaranteed to see the same definitions so that the C++
346  * standard's one-definition-rule is complied with.
347  */
348 
350 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
351  unsigned int count;
352  Thread::Mutex mutex;
353 #else
354  gint count;
355 #endif
356 
357 public:
358  // we should not be able to copy objects of this class
359  // - objects, if constructed on the heap, should be passed
360  // via an IntrusivePtr object. An object of a derived class
361  // might still copy itself via its copy constructor and take
362  // along a new base IntrusiveLockCounter object constructed via
363  // the default constructor, and thus have a correct reference
364  // count of 0, but derived classes should not try to provide
365  // their own assignment operators.
366 /**
367  * This class cannot be copied. The copy constructor is deleted.
368  */
370 /**
371  * This class cannot be copied. The assignment operator is deleted.
372  */
374 
375 /**
376  * Increments the reference count. This method does not throw.
377  */
378  void ref() noexcept {
379 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
380  Thread::Mutex::Lock lock(mutex);
381  ++count;
382 #else
383  g_atomic_int_inc(&count);
384 #endif
385  }
386 
387 /**
388  * Decrements the reference count, and if the count reaches 0 deletes
389  * itself (ie the managed object). This method does not throw unless
390  * the destructor of a derived class throws - that should never
391  * happen.
392  */
393  void unref() {
394 #ifdef CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
395  mutex.lock();
396  --count;
397  if (count == 0) {
398  mutex.unlock();
399  delete this;
400  }
401  else mutex.unlock();
402 #else
403  if (g_atomic_int_dec_and_test(&count)) {
404  delete this;
405  }
406 #endif
407  }
408 
409 /**
410  * By default, glib atomic functions are used to provide thread-safe
411  * manipulation of the reference count. However, the header file
412  * intrusive_ptr.h can be textually amended before the library is
413  * compiled to define the symbol CGU_INTRUSIVE_LOCK_COUNTER_USE_MUTEX
414  * so as to use mutexes instead, which might be useful for some
415  * debugging purposes. Were that to be done, Cgu::Thread::MutexError
416  * might be thrown by this constructor if initialization of the mutex
417  * fails.
418  *
419  * Otherwise, this constructor does not throw.
420  */
421  IntrusiveLockCounter(): count(0) {}
422 
423 /**
424  * This destructor does not throw, unless the destructor of a derived
425  * class throws - that should never happen.
426  */
428 };
429 
430 #if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING)
431 
432 // we can use built-in operator == when comparing pointers referencing
433 // different objects of the same type
434 /**
435  * @ingroup handles
436  *
437  * This comparison operator does not throw. It compares the addresses
438  * of the managed objects.
439  *
440  * Since 2.0.0-rc2
441  */
442 template <class T>
443 bool operator==(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) noexcept {
444  return (s1.get() == s2.get());
445 }
446 
447 /**
448  * @ingroup handles
449  *
450  * This comparison operator does not throw. It compares the addresses
451  * of the managed objects.
452  *
453  * Since 2.0.0-rc2
454  */
455 template <class T>
456 bool operator!=(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) noexcept {
457  return !(s1 == s2);
458 }
459 
460 // we must use std::less rather than the < built-in operator for
461 // pointers to objects not within the same array or object: "For
462 // templates greater, less, greater_equal, and less_equal, the
463 // specializations for any pointer type yield a total order, even if
464 // the built-in operators <, >, <=, >= do not." (para 20.3.3/8).
465 /**
466  * @ingroup handles
467  *
468  * This comparison operator does not throw unless std::less applied to
469  * pointer types throws (which it would not do with any sane
470  * implementation). It compares the addresses of the managed objects.
471  *
472  * Since 2.0.0-rc2
473  */
474 template <class T>
475 bool operator<(const IntrusivePtr<T>& s1, const IntrusivePtr<T>& s2) {
476  return std::less<T*>()(s1.get(), s2.get());
477 }
478 
479 #endif // CGU_USE_SMART_PTR_COMPARISON
480 
481 } // namespace Cgu
482 
483 // doxygen produces long filenames that tar can't handle:
484 // we have generic documentation for std::hash specialisations
485 // in doxygen.main.in
486 #if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING)
487 /* This struct allows InstrusivePtr objects to be keys in unordered
488  associative containers */
489 namespace std {
490 template <class T>
491 struct hash<Cgu::IntrusivePtr<T>> {
492  typedef std::size_t result_type;
493  typedef Cgu::IntrusivePtr<T> argument_type;
494  result_type operator()(const argument_type& s) const {
495  // this is fine: std::hash structs do not normally contain data and
496  // std::hash<T*> certainly won't, so we don't have overhead constructing
497  // std::hash<T*> on the fly
498  return std::hash<T*>()(s.get());
499  }
500 };
501 } // namespace std
502 #endif // CGU_USE_SMART_PTR_COMPARISON
503 
504 #endif
Cgu::IntrusiveLockCounter::ref
void ref() noexcept
Definition: intrusive_ptr.h:378
Cgu
Definition: application.h:44
Cgu::IntrusivePtr::operator*
T & operator*() const noexcept
Definition: intrusive_ptr.h:226
Cgu::Thread::Mutex::lock
int lock() noexcept
Definition: mutex.h:147
Cgu::IntrusiveCounter::operator=
IntrusiveCounter & operator=(const IntrusiveCounter &)=delete
Cgu::IntrusiveLockCounter::~IntrusiveLockCounter
virtual ~IntrusiveLockCounter()
Definition: intrusive_ptr.h:427
Cgu::Thread::Mutex::unlock
int unlock() noexcept
Definition: mutex.h:170
Cgu::IntrusiveCounter
This is a counter class providing the ref() and unref() functions required by IntrusivePtr.
Definition: intrusive_ptr.h:268
Cgu::IntrusiveCounter::IntrusiveCounter
IntrusiveCounter(const IntrusiveCounter &)=delete
Cgu::IntrusiveLockCounter::IntrusiveLockCounter
IntrusiveLockCounter(const IntrusiveLockCounter &)=delete
Cgu::IntrusivePtr
This is a smart pointer for managing objects allocated on freestore which maintain their own referenc...
Definition: intrusive_ptr.h:98
Cgu::IntrusiveLockCounter::unref
void unref()
Definition: intrusive_ptr.h:393
Cgu::IntrusivePtr::operator=
IntrusivePtr & operator=(IntrusivePtr< U > &&intr_ptr)
Definition: intrusive_ptr.h:211
Cgu::IntrusivePtr::IntrusivePtr
IntrusivePtr(IntrusivePtr< U > &&intr_ptr) noexcept
Definition: intrusive_ptr.h:169
Cgu::operator<
bool operator<(const GobjHandle< T > &h1, const GobjHandle< T > &h2)
Definition: gobj_handle.h:632
Cgu::operator!=
bool operator!=(const GobjHandle< T > &h1, const GobjHandle< T > &h2) noexcept
Definition: gobj_handle.h:613
Cgu::swap
void swap(Cgu::AsyncQueue< T, Container > &q1, Cgu::AsyncQueue< T, Container > &q2)
Definition: async_queue.h:1493
Cgu::IntrusivePtr::IntrusivePtr
IntrusivePtr(T *ptr=0)
Definition: intrusive_ptr.h:119
Cgu::IntrusivePtr::IntrusivePtr
IntrusivePtr(const IntrusivePtr &intr_ptr)
Definition: intrusive_ptr.h:131
Cgu::IntrusiveCounter::ref
void ref() noexcept
Definition: intrusive_ptr.h:292
Cgu::IntrusivePtr::operator->
T * operator->() const noexcept
Definition: intrusive_ptr.h:233
Cgu::IntrusivePtr::operator=
IntrusivePtr & operator=(IntrusivePtr intr_ptr)
Definition: intrusive_ptr.h:184
Cgu::IntrusivePtr::IntrusivePtr
IntrusivePtr(const IntrusivePtr< U > &intr_ptr)
Definition: intrusive_ptr.h:157
Cgu::operator==
bool operator==(const GobjHandle< T > &h1, const GobjHandle< T > &h2) noexcept
Definition: gobj_handle.h:600
Cgu::IntrusivePtr::operator=
IntrusivePtr & operator=(const IntrusivePtr< U > &intr_ptr)
Definition: intrusive_ptr.h:198
Cgu::Thread::Mutex::Lock
A scoped locking class for exception safe Mutex locking.
Definition: mutex.h:207
Cgu::IntrusivePtr::get
T * get() const noexcept
Definition: intrusive_ptr.h:220
Cgu::IntrusivePtr::IntrusivePtr
friend class IntrusivePtr
Definition: intrusive_ptr.h:145
hash
A specialization of std::hash for Cgu::Callback::FunctorArg, Cgu::Callback::SafeFunctorArg,...
Cgu::IntrusiveCounter::~IntrusiveCounter
virtual ~IntrusiveCounter()
Definition: intrusive_ptr.h:314
Cgu::IntrusiveLockCounter
This is a counter class providing the ref() and unref() functions required by IntrusivePtr,...
Definition: intrusive_ptr.h:349
Cgu::IntrusivePtr::IntrusivePtr
IntrusivePtr(IntrusivePtr &&intr_ptr) noexcept
Definition: intrusive_ptr.h:140
mutex.h
Provides wrapper classes for pthread mutexes and condition variables, and scoped locking classes for ...
Cgu::IntrusiveCounter::unref
void unref()
Definition: intrusive_ptr.h:300
Cgu::IntrusiveCounter::IntrusiveCounter
IntrusiveCounter() noexcept
Definition: intrusive_ptr.h:308
Cgu::IntrusiveLockCounter::operator=
IntrusiveLockCounter & operator=(const IntrusiveLockCounter &)=delete
Cgu::IntrusivePtr::~IntrusivePtr
~IntrusivePtr()
Definition: intrusive_ptr.h:253
Cgu::Thread::Mutex
A wrapper class for pthread mutexes.
Definition: mutex.h:117
cgu_config.h
Cgu::IntrusiveLockCounter::IntrusiveLockCounter
IntrusiveLockCounter()
Definition: intrusive_ptr.h:421
Cgu::IntrusivePtr::reset
void reset(T *ptr=0)
Definition: intrusive_ptr.h:244