c++-gtk-utils
shared_handle.h
Go to the documentation of this file.
1 /* Copyright (C) 2004 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_SHARED_HANDLE_H
40 #define CGU_SHARED_HANDLE_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 // SharedLockHandle class
45 /* #define CGU_SHARED_LOCK_HANDLE_USE_MUTEX 1 */
46 
47 #include <exception>
48 #include <new>
49 #include <functional> // for std::less and std::hash<T*>
50 #include <utility> // for std::swap
51 #include <cstddef> // for std::size_t
52 #include <cstdlib>
53 
54 #include <glib.h>
55 
56 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
57 #include <c++-gtk-utils/mutex.h>
58 #endif
59 
61 
62 /**
63  * @addtogroup handles handles and smart pointers
64  */
65 
66 namespace Cgu {
67 
68 /**
69  * @class SharedHandle shared_handle.h c++-gtk-utils/shared_handle.h
70  * @brief This is a generic class for managing the lifetime of objects
71  * allocated on freestore.
72  * @ingroup handles
73  * @sa SharedLockHandle
74  * @sa ScopedHandle
75  * @sa SharedHandleError
76  * @sa GcharSharedHandle
77  * @sa GerrorSharedHandle
78  * @sa StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy
79  *
80  * The SharedHandle class is similar to the SharedPtr class (it keeps
81  * a reference count and deletes the handled object when the count
82  * reaches 0), but it does not have pointer semantics. Accordingly,
83  * it can be used to manage the memory of arrays and other objects
84  * allocated on the heap.
85  *
86  * Because it is useful with arrays, by default it deallocates memory
87  * using C++ delete[]. However, if a SharedHandle object is passed a
88  * function object type as a second template argument when
89  * instantiated, it will use that function object to delete memory.
90  * This enables it to handle the memory of any object, such as objects
91  * to be deleted using std::free() or Glib's g_free(), g_list_free()
92  * or g_slice_free(). Instances (such as @ref GcharScopedHandleAnchor
93  * "GcharScopedHandle", @ref GcharSharedHandleAnchor
94  * "GcharSharedHandle", @ref GerrorSharedHandleAnchor
95  * "GerrorSharedHandle" and @ref GerrorScopedHandleAnchor
96  * "GerrorScopedHandle") typdef'ed for particular deleters can
97  * conveniently manage objects of any kind.
98  *
99  * To reflect the fact that it is just a handle for a pointer, it has
100  * different instantiation semantics from a SharedPtr object. A
101  * SharedPtr object is instantiated using this syntax:
102  *
103  * @code SharedPtr<ObjType> sh_ptr(new ObjType); @endcode
104  *
105  * A SharedHandle is instantiated using this syntax (note that the
106  * instantiated handle is for type T* and not T):
107  *
108  * @code SharedHandle<ObjType*> sh_handle(new ObjType[n]); @endcode
109  *
110  *
111  * Apart from the operatorT() type conversion operator (which returns
112  * the underlying pointer), the only other method to obtain the
113  * underlying pointer is the get() method. If the object referenced
114  * is an array allocated on the heap, to use indexing you could either
115  * do this:
116  *
117  * @code
118  * using namespace Cgu;
119  * SharedHandle<char*> handle(new char[10]);
120  * handle.get()[0] = 'a';
121  * std::cout << handle.get()[0] << std::endl;
122  * @endcode
123  *
124  * or this:
125  *
126  * @code
127  * using namespace Cgu;
128  * SharedHandle<char*> handle(new char[10]);
129  * handle[0] = 'a';
130  * std::cout << handle[0] << std::endl;
131  * @endcode
132  *
133  * There is also a SharedLockHandle class, which has a thread-safe
134  * reference count, and a ScopedHandle class, which deletes its object
135  * as soon as it goes out of scope. A ScopedHandle class can be
136  * viewed as a SharedHandle which cannot be assigned to or used as the
137  * argument to a copy constructor and therefore which cannot have a
138  * reference count of more than 1. It is used where, if you wanted
139  * pointer semantics, you might use a const std::auto_ptr<>.
140  *
141  * SharedHandle objects can be instantiated for pointers to constant
142  * objects (such as SharedHandle<const char*>), provided the deleter
143  * functor will take such pointers.
144  *
145  * This library provides StandardArrayDelete, CFree, GFree,
146  * GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter
147  * functors, which can be used as the second template parameter of the
148  * SharedHandle class. As mentioned above, StandardArrayDelete is the
149  * default, and some typedef'ed instances of SharedHandle for gchar
150  * (with the GFree deleter) and for GError (with the GerrorFree
151  * deleter) are provided.
152  *
153  * @b Comparison @b with @b std::shared_ptr
154  *
155  * Although the semantics of std::shared_ptr in C++11 are not
156  * particularly suited to managing either arrays or C objects with
157  * accessor functions (such as in glib), most of the things that can
158  * be done by this class can be done by using std::shared_ptr with a
159  * specialised deleter. However, this class is retained in the
160  * c++-gtk-utils library not only to retain compatibility with series
161  * 1.2 of the library, but also to cater for some cases not met (or
162  * not so easily met) by std::shared_ptr:
163  *
164  * (i) The Cgu::SharedHandle class takes its deleter as a template
165  * parameter, which means that typedefs can be used to enable handles
166  * for particular deleters to be easily created (and as mentioned,
167  * this library provides a number of pre-formed deleter functors and
168  * typedefs for them). With std::shared_ptr, custom deleters must be
169  * passed to the shared_ptr constructor on every occasion a shared_ptr
170  * is constructed to manage a new object (and they cannot be templated
171  * as a typedef).
172  *
173  * (ii) Glib memory slices provide an efficient small object allocator
174  * (they are likely to be significantly more efficient than global
175  * operator new()/new[](), which generally hand off to malloc(), and
176  * whilst malloc() is good for large block allocations it is generally
177  * poor as a small object allocator). Internal Cgu::SharedHandle
178  * allocation using glib memory slices can be achieved by compiling
179  * the library with the --with-glib-memory-slices-no-compat
180  * configuration option.
181  *
182  * (iii) If glib memory slices are not used (which do not throw),
183  * constructing a shared pointer for a new managed object (or calling
184  * reset() for a new managed object) might throw if internal
185  * allocation fails. Although by default the Cgu::SharedHandle
186  * implementation will delete the new managed object in such a case,
187  * it also provides an alternative constructor and reset() method
188  * which instead enable the new object to be accessed via the thrown
189  * exception object so that user code can decide what to do;
190  * std::shared_ptr deletes the new object in every case.
191  *
192  * (iv) A user can explicitly state whether the shared handle object
193  * is to have atomic increment and decrement-and-test with respect to
194  * the reference count so that the reference count is thread safe
195  * ('no' in the case of Cgu::SharedHandle, and 'yes' in the case of
196  * Cgu::SharedLockHandle). Using atomic functions is unnecessary if
197  * the managed object concerned is only addressed in one thread (and
198  * might cause unwanted cache flushing in certain circumstances).
199  * std::shared_ptr will generally always use atomic functions with
200  * respect to its reference count in a multi-threaded program.
201  *
202  * In favour of std::shared_ptr, it has an associated std::weak_ptr
203  * class, which Cgu::SharedHandle does not (there is a
204  * Cgu::GobjWeakHandle class, but that is cognate with Cgu::GobjHandle
205  * and is only usable with GObjects).
206  *
207  * If the library is compiled with the
208  * --with-glib-memory-slices-no-compat configuration option, as
209  * mentioned Cgu::SharedHandle constructs its reference counting
210  * internals using glib memory slices. Although it is safe in a
211  * multi-threaded program if glib < 2.32 is installed to construct a
212  * static SharedHandle object in global namespace (that is, prior to
213  * g_thread_init() being called) by means of the default constructor
214  * and/or a pointer argument of NULL, it is not safe if constructed
215  * with a non-NULL pointer value. If glib >= 2.32 is installed,
216  * global objects with memory slices are safe in all
217  * circumstances. (Having said that, it would be highly unusual to
218  * have global SharedHandle objects.)
219  */
220 
221 /********************* here are some deleter classes *******************/
222 
223 /**
224  * @class StandardArrayDelete shared_handle.h c++-gtk-utils/shared_handle.h
225  * @brief A deleter functor for use as the second (Dealloc) template
226  * parameter of the SharedHandle, SharedLockHandle or ScopedHandle
227  * template classes, which calls the C++ delete[] expression.
228  * @ingroup handles
229  * @details This functor enables those classes to manage arrays
230  * created with the new expression. It is the default type of the
231  * second template paramenter of those classes.
232  */
233 template <class T> class StandardArrayDelete {
234 public:
235  void operator()(T obj) {
236  delete[] obj;
237  }
238 };
239 
240 /**
241  * @class CFree shared_handle.h c++-gtk-utils/shared_handle.h
242  * @brief A deleter functor for use as the second (Dealloc) template
243  * parameter of the SharedHandle, SharedLockHandle or ScopedHandle
244  * template classes, which calls std::free.
245  * @ingroup handles
246  * @details This functor enables those classes to manage memory
247  * allocated with std::malloc(), std::calloc() and std::realloc().
248  */
249 class CFree {
250 public:
251  void operator()(const void* obj) {
252  std::free(const_cast<void*>(obj));
253  }
254 };
255 
256 /**
257  * @class GFree shared_handle.h c++-gtk-utils/shared_handle.h
258  * @brief A deleter functor for use as the second (Dealloc) template
259  * parameter of the SharedHandle, SharedLockHandle or ScopedHandle
260  * template classes, which calls glib's g_free().
261  * @ingroup handles
262  * @details This functor enables those classes to manage memory
263  * allocated by glib or gtk+ functions which requires to be freed with
264  * g_free(). It is used in the typedefs @ref GcharSharedHandleAnchor
265  * "GcharSharedHandle" and @ref GcharScopedHandleAnchor
266  * "GcharScopedHandle".
267  */
268 class GFree {
269 public:
270  void operator()(const void* obj) {
271  g_free(const_cast<void*>(obj));
272  }
273 };
274 
275 /**
276  * @class GSliceFree shared_handle.h c++-gtk-utils/shared_handle.h
277  * @brief A deleter functor for use as the second (Dealloc) template
278  * parameter of the SharedHandle, SharedLockHandle or ScopedHandle
279  * template classes, which calls glib's g_slice_free1().
280  * @ingroup handles
281  *
282  * @details This functor enables those classes to manage a memory
283  * block allocated using glib memory slices. The managed memory block
284  * to be deleted by the GSliceFree functor must have the same size as
285  * the size of the object for which the functor is instantiated by
286  * pointer, as for example as allocated with the g_slice_new,
287  * g_slice_new0 or g_slice_dup macros (in other words, the GSliceFree
288  * template parameter must match the argument passed to those macros):
289  * see the example below. Use GSliceFreeSize where it is necessary or
290  * more convenient to have the size of the block to be freed as the
291  * template parameter. Use GSliceDestroy where the memory holds a C++
292  * object constructed in the memory by the global placement new
293  * expression.
294  *
295  * The type of the template argument for the functor is a pointer to
296  * the managed type: it is the same as the first template argument of
297  * the relevant SharedHandle, SharedLockHandle or ScopedHandle object.
298  * For example:
299  *
300  * @code
301  * using namespace Cgu;
302  * SharedHandle<MyStruct*, GSliceFree<MyStruct*> > h(g_slice_new(MyStruct));
303  * ...
304  * @endcode
305  *
306  * The availability of this functor is not dependent on the library
307  * having been installed with the --with-glib-memory-slices-compat or
308  * --with-glib-memory-slices-no-compat configuration option (see @ref
309  * Memory for further details of those options).
310  */
311 template <class T> class GSliceFree {
312 public:
313  void operator()(T obj) {
314  g_slice_free1(sizeof(*obj), (void*)obj);
315  }
316 };
317 
318 /**
319  * @class GSliceDestroy shared_handle.h c++-gtk-utils/shared_handle.h
320  * @brief A deleter functor for use as the second (Dealloc) template
321  * parameter of the SharedHandle, SharedLockHandle or ScopedHandle
322  * template classes, which calls glib's g_slice_free1(), but before
323  * doing so also explicitly calls the destructor of a C++ object
324  * constructed in the memory.
325  * @ingroup handles
326  *
327  * @details The managed memory block to be deleted by the
328  * GSliceDestroy functor must have the same size as the size of the
329  * object for which the functor is instantiated by pointer, as for
330  * example as allocated with the g_slice_new or g_slice_new0 macros
331  * (in other words, the GSliceDestroy template parameter must match
332  * the argument passed to those macros), and the memory block must
333  * have had that object constructed in it with the global placement
334  * new expression: see the example below. Sometimes it is more
335  * convenient to implement C++ objects in glib memory slices that way,
336  * rather than to have custom new and delete member operators of the
337  * classes concerned which use glib's g_slice_*(). However, a
338  * SharedHandle class with a GSliceDestroy deleter is not as easy to
339  * use as the SharedPtr class, as SharedHandle has no operator*() nor
340  * operator->() method (the get() method would have to be used to
341  * obtain the underlying pointer).
342  *
343  * One consequence of the static sizing (and so typing) of memory
344  * slices is that a GSliceDestroy object instantiated for the
345  * management of a particular class must not be used by a
346  * SharedHandle, SharedLockHandle or ScopedHandle object which
347  * attempts to manage a class derived from it. This comes back to the
348  * point that the GSliceDestroy template parameter must match the
349  * argument passed to the g_slice_new or g_slice_new0 macros.
350  *
351  * The type of the template argument for the functor is a pointer to
352  * the managed type: it is the same as the first template argument of
353  * the relevant SharedHandle, SharedLockHandle or ScopedHandle object.
354  * For example, to construct a SharedHandle managing an object of type
355  * MyClass to be constructed in a glib memory slice in an exception
356  * safe way:
357  *
358  * @code
359  * using namespace Cgu;
360  * SharedHandle<MyClass*, GSliceDestroy<MyClass*> > h; // won't throw
361  * { // scope block for p variable
362  * MyClass* p = g_slice_new(MyClass);
363  * try {new(p) MyClass;} // MyClass constructor might throw
364  * catch(...) {
365  * g_slice_free(MyClass, p);
366  * throw;
367  * }
368  * h.reset(p); // might throw but if so cleans up
369  * }
370  * ...
371  * @endcode
372  *
373  * The availability of this functor is not dependent on the library
374  * having been installed with the --with-glib-memory-slices-compat or
375  * --with-glib-memory-slices-no-compat configuration option (see @ref
376  * Memory for further details of those options).
377  */
378 template <class T> class GSliceDestroy {
379  template <class U> void destroy(U& obj) {obj.~U();}
380 public:
381  void operator()(T obj) {
382  destroy(*obj);
383  g_slice_free1(sizeof(*obj), (void*)obj);
384  }
385 };
386 
387 /**
388  * @class GSliceFreeSize shared_handle.h c++-gtk-utils/shared_handle.h
389  * @brief A deleter functor for use as the second (Dealloc) template
390  * parameter of the SharedHandle, SharedLockHandle or ScopedHandle
391  * template classes, which calls glib's g_slice_free1().
392  * @ingroup handles
393  *
394  * @details This functor enables those classes to manage memory
395  * allocated with g_slice_alloc(), g_slice_alloc0() or g_slice_copy().
396  * It is an alternative to using GSliceFree where, instead of the
397  * template parameter being a pointer to a particular managed type,
398  * the size of the memory block to be freed is passed, so enabling it
399  * to be more conveniently used to free memory containing arrays of
400  * built-in types or of PODSs. Use GSliceDestroy where the memory
401  * holds a C++ object constructed in the memory by the global
402  * placement new expression.
403  *
404  * The type of the template argument for the functor is an integer
405  * type (gsize) and is the size of the block to be managed. For
406  * example:
407  *
408  * @code
409  * using namespace Cgu;
410  * SharedHandle<char*, GSliceFreeSize<10> > h(static_cast<char*>(g_slice_alloc(10)));
411  * ...
412  * @endcode
413  *
414  * The availability of this functor is not dependent on the library
415  * having been installed with the --with-glib-memory-slices-compat or
416  * --with-glib-memory-slices-no-compat configuration option (see @ref
417  * Memory for further details of those options).
418  */
419 template <gsize block_size> class GSliceFreeSize {
420 public:
421  void operator()(const void* obj) {
422  g_slice_free1(block_size, const_cast<void*>(obj));
423  }
424 };
425 
426 /*
427  * we could provide a functor class for
428  * g_slice_free_chain_with_offset() such as:
429  *
430  * template <class T, gsize offset> class GSliceFreeChain {
431  * public:
432  * void operator()(T obj) {
433  * g_slice_free_chain_with_offset(sizeof(*obj), (void*)obj, offset);
434  * }
435  * };
436  *
437  * However, this is not going to be particularly useful because the
438  * G_STRUCT_OFFSET macro and/or C's offsetof macro, needed to provide
439  * the value for the offset parameter, do not work for other than
440  * PODSs. g_slice_free_chain_with_offset() is intended for internal
441  * implementations and in the event of a user wanting such memory
442  * management it is best achieved by having custom new[] and delete[]
443  * member operators of the class concerned which use glib's
444  * g_slice_*() directly.
445  */
446 
447 /********************* define some typedefs for Glib ******************/
448 
449 template <class T, class Dealloc> class SharedHandle;
450 template <class T, class Dealloc> class ScopedHandle;
451 
452 /**
453  * @typedef GcharSharedHandle.
454  * @brief A handle comprising a typed instance of the SharedHandle
455  * class for gchar* arrays and strings
456  * @anchor GcharSharedHandleAnchor
457  * @ingroup handles
458  * \#include <c++-gtk-utils/shared_handle.h>
459  */
461 
462 /**
463  * @typedef GcharScopedHandle.
464  * @brief A handle comprising a typed instance of the ScopedHandle
465  * class for gchar* arrays and strings
466  * @anchor GcharScopedHandleAnchor
467  * @ingroup handles
468  * \#include <c++-gtk-utils/shared_handle.h>
469 */
471 
472 
473 /******************* now the handle class definitions *****************/
474 
475 /**
476  * @class SharedHandleError shared_handle.h c++-gtk-utils/shared_handle.h
477  * @brief This is an exception struct thrown as an alternative to
478  * deleting a managed object when internal memory allocation for
479  * SharedHandle or SharedLockHandle fails in their reset() method or
480  * in their constructor which takes a pointer.
481  * @sa SharedHandle SharedLockHandle SharedHandleAllocFail
482  * @ingroup handles
483  *
484  * This is an exception struct thrown as an alternative to deleting a
485  * managed object when SharedHandle<T>::SharedHandle(T),
486  * SharedLockHandle<T>::SharedLockHandle(T), SharedHandle<T>::reset(T)
487  * or SharedLockHandle<T>::reset(T) would otherwise throw
488  * std::bad_alloc. To make those methods do that,
489  * Cgu::SharedHandleAllocFail::leave is passed as their second
490  * argument.
491  *
492  * If the exception is thrown, the struct has a member 'obj' of type
493  * T, which is a pointer to the object or array originally passed to
494  * those methods, so the user can deal with it appropriately. This
495  * enables the result of the new expression to be passed directly as
496  * the argument to those methods without giving rise to a resource
497  * leak, as in:
498  *
499  * @code
500  * using namespace Cgu;
501  * SharedHandle<T*> s; // doesn't throw
502  * try {
503  * s.reset(new T[2], SharedHandleAllocFail::leave); // both T allocation and reset() might throw
504  * }
505  * catch (std::bad_alloc&) {
506  * ...
507  * }
508  * catch (SharedHandleError<T*>& e) {
509  * e.obj[0].do_something();
510  * e.obj[1].do_something();
511  * ...
512  * }
513  * ...
514  * @endcode
515  *
516  * As above, a catch block will need to deal with std::bad_alloc (if
517  * the call to the new expression when creating the T object fails)
518  * as well as SharedHandleError (if the call to the new expression in
519  * the reset() method fails after a valid T object has been
520  * constructed).
521  */
522 
523 template <class T> struct SharedHandleError: public std::exception {
524  T obj;
525  virtual const char* what() const throw() {return "SharedHandleError\n";}
526  SharedHandleError(T p): obj(p) {}
527 };
528 
529 /**
530  * enum Cgu::SharedHandleAllocFail::Leave
531  * The enumerator Cgu::SharedHandleAllocFail::leave is passed as the
532  * second argument of the reset() method of SharedHandle or
533  * SharedLockHandle in order to prevent the method deleting the object
534  * passed to it if reset() fails internally because of memory
535  * exhaustion.
536  * @ingroup handles
537  */
538 namespace SharedHandleAllocFail {
539  enum Leave {leave};
540 }
541 
542 template <class T, class Dealloc = StandardArrayDelete<T>> class SharedHandle {
543 
544  Dealloc deleter;
545 
546 #ifndef DOXYGEN_PARSING
547  struct RefItems {
548  unsigned int* ref_count_p;
549  T obj;
550  } ref_items;
551 #endif
552 
553  void unreference() {
554  if (!ref_items.ref_count_p) return;
555  --(*ref_items.ref_count_p);
556  if (*ref_items.ref_count_p == 0) {
557 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
558  g_slice_free(unsigned int, ref_items.ref_count_p);
559 #else
560  delete ref_items.ref_count_p;
561 #endif
562  deleter(ref_items.obj);
563  }
564  }
565 
566  void reference() {
567  if (!ref_items.ref_count_p) return;
568  ++(*ref_items.ref_count_p);
569  }
570 
571 public:
572 /**
573  * Constructor taking an unmanaged object.
574  * @param ptr The object which the SharedHandle is to manage (if
575  * any).
576  * @exception std::bad_alloc This constructor will not throw if the
577  * 'ptr' argument has a NULL value (the default), otherwise it might
578  * throw std::bad_alloc if memory is exhausted and the system throws
579  * in that case. If such an exception is thrown, this constructor is
580  * exception safe (it does not leak resources), but as well as
581  * cleaning itself up this constructor will also delete the managed
582  * object passed to it to avoid a memory leak. If such automatic
583  * deletion is not wanted in that case, use the version of this
584  * constructor taking a Cgu::SharedHandleAllocFail::Leave tag argument.
585  * @note std::bad_alloc will not be thrown if the library has been
586  * installed using the --with-glib-memory-slices-no-compat
587  * configuration option: instead glib will terminate the program if it
588  * is unable to obtain memory from the operating system.
589  */
590  explicit SharedHandle(T ptr = 0) {
591 
592  if ((ref_items.obj = ptr)) { // not NULL
593 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
594  ref_items.ref_count_p = g_slice_new(unsigned int);
595  *ref_items.ref_count_p = 1;
596 #else
597  try {
598  ref_items.ref_count_p = new unsigned int(1);
599  }
600  catch (...) {
601  deleter(ptr); // if allocating the int referenced by ref_items.ref_count_p
602  // has failed then delete the object to be referenced to
603  // avoid a memory leak
604  throw;
605  }
606 #endif
607  }
608  else ref_items.ref_count_p = 0;
609  }
610 
611 /**
612  * Constructor taking an unmanaged object.
613  * @param ptr The object which the SharedHandle is to manage
614  * @param tag Passing the tag emumerator
615  * Cgu::SharedHandleAllocFail::leave causes this constructor not to
616  * delete the new managed object passed as the 'ptr' argument in the
617  * event of internal allocation in this method failing because of
618  * memory exhaustion (in that event, Cgu::SharedHandleError will be
619  * thrown).
620  * @exception Cgu::SharedHandleError This constructor might throw
621  * Cgu::SharedHandleError if memory is exhausted and the system would
622  * otherwise throw std::bad_alloc in that case. This constructor is
623  * exception safe (it does not leak resources), and if such an
624  * exception is thrown it will clean itself up, but it will not
625  * attempt to delete the new managed object passed to it. Access to
626  * the object passed to the 'ptr' argument can be obtained via the
627  * thrown Cgu::SharedHandleError object.
628  * @note 1. On systems with over-commit/lazy-commit combined with
629  * virtual memory (swap), it is rarely useful to check for memory
630  * exhaustion, so in those cases this version of the constructor will
631  * not be useful.
632  * @note 2. If the library has been installed using the
633  * --with-glib-memory-slices-no-compat configuration option this
634  * version of the constructor will also not be useful: instead glib
635  * will terminate the program if it is unable to obtain memory from
636  * the operating system.
637  */
639 
640  if ((ref_items.obj = ptr)) { // not NULL
641 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
642  ref_items.ref_count_p = g_slice_new(unsigned int);
643  *ref_items.ref_count_p = 1;
644 #else
645  try {
646  ref_items.ref_count_p = new unsigned int(1);
647  }
648  catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly
649  throw SharedHandleError<T>(ptr);
650  }
651 #endif
652  }
653  else ref_items.ref_count_p = 0;
654  }
655 
656 /**
657  * Causes the SharedHandle to cease to manage its managed object (if
658  * any), deleting it if this is the last SharedHandle object managing
659  * it. If the argument passed is not NULL, the SharedHandle object
660  * will manage the new object passed (which must not be managed by any
661  * other SharedHandle object). This method is exception safe, but see
662  * the comments below on std::bad_alloc.
663  * @param ptr NULL (the default), or a new unmanaged object to manage.
664  * @exception std::bad_alloc This method will not throw if the 'ptr'
665  * argument has a NULL value (the default) and the destructor of a
666  * managed object does not throw, otherwise it might throw
667  * std::bad_alloc if memory is exhausted and the system throws in that
668  * case. Note that if such an exception is thrown then this method
669  * will do nothing (it is strongly exception safe and will continue to
670  * manage the object it was managing prior to the call), except that
671  * it will delete the new managed object passed to it to avoid a
672  * memory leak. If such automatic deletion in the event of such an
673  * exception is not wanted, use the reset() method taking a
674  * Cgu::SharedHandleAllocFail::Leave tag type as its second argument.
675  * @note std::bad_alloc will not be thrown if the library has been
676  * installed using the --with-glib-memory-slices-no-compat
677  * configuration option: instead glib will terminate the program if it
678  * is unable to obtain memory from the operating system.
679  */
680  void reset(T ptr = 0) {
681  SharedHandle tmp(ptr);
682  std::swap(ref_items, tmp.ref_items);
683  }
684 
685 /**
686  * Causes the SharedHandle to cease to manage its managed object (if
687  * any), deleting it if this is the last SharedHandle object managing
688  * it. The SharedHandle object will manage the new object passed
689  * (which must not be managed by any other SharedHandle object). This
690  * method is exception safe, but see the comments below on
691  * Cgu::SharedHandleError.
692  * @param ptr A new unmanaged object to manage (if no new object is to
693  * be managed, use the version of reset() taking a default value of
694  * NULL).
695  * @param tag Passing the tag emumerator
696  * Cgu::SharedHandleAllocFail::leave causes this method not to delete
697  * the new managed object passed as the 'ptr' argument in the event of
698  * internal allocation in this method failing because of memory
699  * exhaustion (in that event, Cgu::SharedHandleError will be thrown).
700  * @exception Cgu::SharedHandleError This method might throw
701  * Cgu::SharedHandleError if memory is exhausted and the system would
702  * otherwise throw std::bad_alloc in that case. Note that if such an
703  * exception is thrown then this method will do nothing (it is
704  * strongly exception safe and will continue to manage the object it
705  * was managing prior to the call), and it will not attempt to delete
706  * the new managed object passed to it. Access to the object passed
707  * to the 'ptr' argument can be obtained via the thrown
708  * Cgu::SharedHandleError object.
709  * @note 1. On systems with over-commit/lazy-commit combined with
710  * virtual memory (swap), it is rarely useful to check for memory
711  * exhaustion, so in those cases this version of the reset() method
712  * will not be useful.
713  * @note 2. If the library has been installed using the
714  * --with-glib-memory-slices-no-compat configuration option this
715  * version of the reset() method will also not be useful: instead glib
716  * will terminate the program if it is unable to obtain memory from
717  * the operating system.
718  */
720  SharedHandle tmp(ptr, tag);
721  std::swap(ref_items, tmp.ref_items);
722  }
723 
724  /**
725  * The copy constructor does not throw.
726  * @param sh_hand The handle to be copied.
727  */
728  SharedHandle(const SharedHandle& sh_hand) {
729  ref_items = sh_hand.ref_items;
730  reference();
731  }
732 
733  /**
734  * The move constructor does not throw. It has move semantics.
735  * @param sh_hand The handle to be moved.
736  */
738  ref_items = sh_hand.ref_items;
739  sh_hand.ref_items.ref_count_p = 0;
740  sh_hand.ref_items.obj = 0;
741  }
742 
743  /**
744  * This method (and so copy or move assignment) does not throw unless
745  * the destructor of a managed object throws.
746  * @param sh_hand the assignor.
747  * @return The SharedHandle object after assignment.
748  */
749  // having a value type as the argument, rather than reference to const
750  // and then initialising a tmp object, gives the compiler more scope
751  // for optimisation, and also caters for r-values without a separate
752  // overload
754  std::swap(ref_items, sh_hand.ref_items);
755  return *this;
756  }
757 
758  /**
759  * This method does not throw.
760  * @return A pointer to the handled object (or NULL if none is
761  * handled).
762  */
763  T get() const {return ref_items.obj;}
764 
765  /**
766  * This method does not throw.
767  * @return A pointer to the handled object (or NULL if none is
768  * handled).
769  */
770  operator T() const {return ref_items.obj;}
771 
772  /**
773  * This method does not throw.
774  * @return The number of SharedHandle objects referencing the managed
775  * object (or 0 if none is managed by this SharedHandle).
776  */
777  unsigned int get_refcount() const {return (ref_items.ref_count_p) ? *ref_items.ref_count_p : 0;}
778 
779  /**
780  * The destructor does not throw unless the destructor of a handled
781  * object throws - that should never happen.
782  */
783  ~SharedHandle() {unreference();}
784 };
785 
786 /**
787  * @class ScopedHandle shared_handle.h c++-gtk-utils/shared_handle.h
788  * @brief This is a generic scoped class for managing the lifetime of objects
789  * allocated on freestore.
790  * @ingroup handles
791  * @sa SharedHandle SharedLockHandle SharedHandleError
792  * @sa StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy
793  *
794  * This class deletes its object as soon as it goes out of scope. It
795  * can be viewed as a SharedHandle which cannot be assigned to or used
796  * as the argument to a copy constructor and therefore which cannot
797  * have a reference count of more than 1.
798  *
799  * ScopedHandle objects can be instantiated for pointers to constant
800  * objects (such as ScopedHandle<const char*>), provided the deleter
801  * functor will take such pointers.
802  *
803  * This library provides StandardArrayDelete, CFree, GFree,
804  * GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter
805  * functors, which can be used as the second template parameter of the
806  * ScopedHandle class. StandardArrayDelete is the default, and some
807  * typedef'ed instances of ScopedHandle for gchar (with the GFree
808  * deleter) and for GError (with the GerrorFree deleter) are provided:
809  * @ref GcharScopedHandleAnchor "GcharScopedHandle" and @ref
810  * GerrorScopedHandleAnchor "GerrorScopedHandle")
811  *
812  * @b Comparison @b with @b std::unique_ptr
813  *
814  * Although the semantics of std::unique_ptr in C++11 are not
815  * particularly suited to managing C objects with accessor functions
816  * (such as in glib), most of the things that can be done by this
817  * class can be done by using std::unique_ptr with a specialised
818  * deleter. However, this class is retained in the c++-gtk-utils
819  * library not only to retain compatibility with series 1.2 of the
820  * library, but also to cater for some cases not so easily met by
821  * std::unique_ptr:
822  *
823  * (i) The Cgu::ScopedHandle class takes its deleter as a template
824  * parameter, which means that typedefs can be used to enable handles
825  * for particular deleters to be easily created (and as mentioned,
826  * this library provides a number of pre-formed deleter functors and
827  * typedefs for them). With std::unique_ptr, custom deleters must be
828  * passed to the unique_ptr constructor on every occasion a unique_ptr
829  * is constructed to manage a new object (and they cannot be templated
830  * as a typedef).
831  *
832  * (ii) This class provides non-move enforcement without making a
833  * const instance of it. A const std::unique_ptr cannot be moved from
834  * or to, but then it cannot have release() or reset() called for it
835  * either.
836  */
837 
838 template <class T, class Dealloc = StandardArrayDelete<T>> class ScopedHandle {
839  Dealloc deleter;
840  T obj;
841 public:
842 /**
843  * This class cannot be copied. The copy constructor is deleted.
844  */
845  ScopedHandle(const ScopedHandle&) = delete;
846 
847 /**
848  * This class cannot be copied. The assignment operator is deleted.
849  */
850  ScopedHandle& operator=(const ScopedHandle&) = delete;
851 
852 /**
853  * The constructor does not throw.
854  * @param ptr The object which the ScopedHandle is to manage (if
855  * any).
856  *
857  * ScopedHandle objects can be instantiated for pointers to constant
858  * objects (such as SharedHandle<const char*>), provided the deleter
859  * functor will take such pointers.
860  */
861  explicit ScopedHandle(T ptr = 0): obj(ptr) {}
862 
863 /**
864  * Causes the ScopedHandle to delete its managed object (if any), and
865  * if the argument passed is not NULL, the ScopedHandle object will
866  * manage the new object passed (which must not be managed by any
867  * other ScopedHandle object). This method does not throw (assuming
868  * the destructor of a managed object does not throw).
869  * @param ptr NULL (the default), or a new unmanaged object to manage.
870  */
871  void reset(T ptr = 0) {
872  std::swap(obj, ptr);
873  if (ptr) deleter(ptr); // ptr now points to the original managed object
874  }
875 
876 /**
877  * Causes the ScopedHandle to cease to manage the handled object, but
878  * does not delete that object. This method does not throw.
879  * @return A pointer to the previously handled object (or NULL if none
880  * was handled).
881  */
882  T release() {T tmp = obj; obj = 0; return tmp;}
883 
884 /**
885  * This method does not throw.
886  * @return A pointer to the handled object (or NULL if none is
887  * handled).
888  */
889  T get() const {return obj;}
890 
891 /**
892  * This method does not throw.
893  * @return A pointer to the handled object (or NULL if none is
894  * handled).
895  */
896  operator T() const {return obj;}
897 
898 /**
899  * The destructor does not throw unless the destructor of a handled
900  * object throws - that should never happen.
901  */
902  ~ScopedHandle() {if (obj) deleter(obj);}
903 };
904 
905 
906 /**
907  * @class SharedLockHandle shared_handle.h c++-gtk-utils/shared_handle.h
908  * @brief This is a generic class for managing the lifetime of objects
909  * allocated on freestore, with a thread safe reference count..
910  * @ingroup handles
911  * @sa SharedHandle ScopedHandle SharedHandleError
912  * @sa StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy
913  *
914  * Class SharedLockHandle is a version of the SharedHandle class which
915  * includes synchronization so that it can handle objects accessed in
916  * multiple threads (although the word Lock is in the title, by
917  * default it uses glib atomic functions to access the reference count
918  * rather than a mutex, so the overhead should be very small). Note
919  * that only the reference count is protected, so this is thread safe
920  * in the sense in which a raw pointer is thread safe. A shared
921  * handle accessed in one thread referencing a particular object is
922  * thread safe as against another shared handle accessing the same
923  * object in a different thread. It is thus suitable for use in
924  * different standard C++ containers which exist in different threads
925  * but which contain shared objects by reference. But:
926  *
927  * 1. If the referenced object is to be modified in one thread and
928  * read or modified in another thread an appropriate mutex for the
929  * referenced object is required (unless that referenced object
930  * does its own locking).
931  *
932  * 2. If the same instance of shared handle is to be modified in one
933  * thread (by assigning to the handle so that it references a
934  * different object, or by moving from it), and copied (assigned
935  * from or used as the argument of a copy constructor), accessed,
936  * destroyed or modified in another thread, a mutex for that
937  * instance of shared handle is required.
938  *
939  * 3. Objects referenced by shared handles which are objects for
940  * which POSIX provides no guarantees (in the main, those which
941  * are not built-in types), such as strings and similar
942  * containers, may not support concurrent reads in different
943  * threads. That depends on the library implementation concerned.
944  * If that is the case, a mutex for the referenced object will
945  * also be required when reading any given instance of such an
946  * object in more than one thread by dereferencing any shared
947  * handles referencing it (and indeed, when not using shared
948  * handles at all).
949  *
950  * SharedLockHandle objects can be instantiated for pointers to
951  * constant objects (such as SharedLockHandle<const char*>), provided
952  * the deleter functor will take such pointers.
953  *
954  * This library provides StandardArrayDelete, CFree, GFree,
955  * GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter
956  * functors, which can be used as the second template parameter of the
957  * SharedLockHandle class. StandardArrayDelete is the default.
958  *
959  * As mentioned, by default glib atomic functions are used to provide
960  * thread-safe manipulation of the reference count. However, the
961  * symbol CGU_SHARED_LOCK_HANDLE_USE_MUTEX can be defined so that the
962  * library uses mutexes instead, which might be useful for some
963  * debugging purposes. Note that if CGU_SHARED_LOCK_HANDLE_USE_MUTEX
964  * is to be defined, this is best done by textually amending the
965  * shared_handle.h header file before the library is compiled. This
966  * will ensure that everything in the program and the library which
967  * includes the shared_handle.h header is guaranteed to see the same
968  * definitions so that the C++ standard's one-definition-rule is
969  * complied with.
970  *
971  * @b Comparison @b with @b std::shared_ptr
972  *
973  * Although the semantics of std::shared_ptr in C++11 are not
974  * particularly suited to managing either arrays or C objects with
975  * accessor functions (such as in glib), most of the things that can
976  * be done by this class can be done by using std::shared_ptr with a
977  * specialised deleter. However, this class is retained in the
978  * c++-gtk-utils library not only to retain compatibility with series
979  * 1.2 of the library, but also to cater for some cases not met (or
980  * not so easily met) by std::shared_ptr:
981  *
982  * (i) The Cgu::SharedLockHandle class takes its deleter as a template
983  * parameter, which means that typedefs can be used to enable handles
984  * for particular deleters to be easily created (and as mentioned,
985  * this library provides a number of pre-formed deleter functors and
986  * typedefs for them). With std::shared_ptr, custom deleters must be
987  * passed to the shared_ptr constructor on every occasion a shared_ptr
988  * is constructed to manage a new object (and they cannot be templated
989  * as a typedef).
990  *
991  * (ii) Glib memory slices provide an efficient small object allocator
992  * (they are likely to be significantly more efficient than global
993  * operator new()/new[](), which generally hand off to malloc(), and
994  * whilst malloc() is good for large block allocations it is generally
995  * poor as a small object allocator). Internal Cgu::SharedLockHandle
996  * allocation using glib memory slices can be achieved by compiling
997  * the library with the --with-glib-memory-slices-no-compat
998  * configuration option.
999  *
1000  * (iii) If glib memory slices are not used (which do not throw),
1001  * constructing a shared pointer for a new managed object (or calling
1002  * reset() for a new managed object) might throw if internal
1003  * allocation fails. Although by default the Cgu::SharedLockHandle
1004  * implementation will delete the new managed object in such a case,
1005  * it also provides an alternative constructor and reset() method
1006  * which instead enable the new object to be accessed via the thrown
1007  * exception object so that user code can decide what to do;
1008  * std::shared_ptr deletes the new object in every case.
1009  *
1010  * (iv) A user can explicitly state whether the shared handle object
1011  * is to have atomic increment and decrement-and-test with respect to
1012  * the reference count so that the reference count is thread safe
1013  * ('no' in the case of Cgu::SharedHandle, and 'yes' in the case of
1014  * Cgu::SharedLockHandle). Using atomic functions is unnecessary if
1015  * the managed object concerned is only addressed in one thread (and
1016  * might cause unwanted cache flushing in certain circumstances).
1017  * std::shared_ptr will generally always use atomic functions with
1018  * respect to its reference count in a multi-threaded program.
1019  *
1020  * In favour of std::shared_ptr, it has an associated std::weak_ptr
1021  * class, which Cgu::SharedLockHandle does not (there is a
1022  * Cgu::GobjWeakHandle class, but that is cognate with Cgu::GobjHandle
1023  * and is only usable with GObjects). In addition shared_ptr objects
1024  * have some atomic store, load and exchange functions provided for
1025  * them which enable concurrent modifications of the same instance of
1026  * shared_ptr in different threads to have defined results.
1027  *
1028  * If the library is compiled with the
1029  * --with-glib-memory-slices-no-compat configuration option, as
1030  * mentioned Cgu::SharedLockHandle constructs its reference counting
1031  * internals using glib memory slices. Although it is safe in a
1032  * multi-threaded program if glib < 2.32 is installed to construct a
1033  * static SharedLockHandle object in global namespace (that is, prior
1034  * to g_thread_init() being called) by means of the default
1035  * constructor and/or a pointer argument of NULL, it is not safe if
1036  * constructed with a non-NULL pointer value. If glib >= 2.32 is
1037  * installed, global objects with memory slices are safe in all
1038  * circumstances. (Having said that, it would be highly unusual to
1039  * have global SharedLockHandle objects.)
1040  */
1041 
1042 template <class T, class Dealloc = StandardArrayDelete<T>> class SharedLockHandle {
1043 
1044  Dealloc deleter;
1045 
1046 #ifndef DOXYGEN_PARSING
1047  struct RefItems {
1048 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1049  Thread::Mutex* mutex_p;
1050  unsigned int* ref_count_p;
1051 #else
1052  gint* ref_count_p;
1053 #endif
1054  T obj;
1055  } ref_items;
1056 #endif
1057 
1058  // SharedLockHandle<T, Dealloc>::unreference() does not throw exceptions
1059  // because Thread::Mutex::~Mutex(), Thread::Mutex::lock() and Thread::Mutex::unlock()
1060  // do not throw
1061  void unreference() {
1062  // we can (and should) check whether ref_items.ref_count_p is NULL without
1063  // a lock, because that member is specific to this SharedLockHandle object.
1064  // Only the integer pointed to by it is shared amongst SharedLockHandle
1065  // objects and requires locking
1066  if (!ref_items.ref_count_p) return;
1067 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1068  ref_items.mutex_p->lock();
1069  --(*ref_items.ref_count_p);
1070  if (*ref_items.ref_count_p == 0) {
1071 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
1072  g_slice_free(unsigned int, ref_items.ref_count_p);
1073 # else
1074  delete ref_items.ref_count_p;
1075 # endif
1076  ref_items.mutex_p->unlock();
1077  delete ref_items.mutex_p;
1078  deleter(ref_items.obj);
1079  }
1080  else ref_items.mutex_p->unlock();
1081 #else
1082  if (g_atomic_int_dec_and_test(ref_items.ref_count_p)) {
1083 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
1084  g_slice_free(gint, ref_items.ref_count_p);
1085 # else
1086  delete ref_items.ref_count_p;
1087 # endif
1088  deleter(ref_items.obj);
1089  }
1090 #endif
1091  }
1092 
1093  // SharedLockHandle<T, Dealloc>::reference() does not throw exceptions because
1094  // Thread::Mutex::Lock::Lock() and Thread::Mutex::Lock::~Lock() do not throw
1095  void reference() {
1096  // we can (and should) check whether ref_items.ref_count_p is NULL without
1097  // a lock, because that member is specific to this SharedLockHandle object.
1098  // Only the integer pointed to by it is shared amongst SharedLockHandle
1099  // objects and requires locking
1100  if (!ref_items.ref_count_p) return;
1101 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1102  Thread::Mutex::Lock lock(*ref_items.mutex_p);
1103  ++(*ref_items.ref_count_p);
1104 #else
1105  g_atomic_int_inc(ref_items.ref_count_p);
1106 #endif
1107  }
1108 
1109 public:
1110 /**
1111  * Constructor taking an unmanaged object.
1112  * @param ptr The object which the SharedLockHandle is to manage (if
1113  * any).
1114  * @exception std::bad_alloc This constructor will not throw if the
1115  * 'ptr' argument has a NULL value (the default), otherwise it might
1116  * throw std::bad_alloc if memory is exhausted and the system throws
1117  * in that case. If such an exception is thrown, this constructor is
1118  * exception safe (it does not leak resources), but as well as
1119  * cleaning itself up this constructor will also delete the managed
1120  * object passed to it to avoid a memory leak. If such automatic
1121  * deletion is not wanted in that case, use the version of this
1122  * constructor taking a Cgu::SharedHandleAllocFail::Leave tag
1123  * argument.
1124  * @note 1. std::bad_alloc will not be thrown if the library has been
1125  * installed using the --with-glib-memory-slices-no-compat
1126  * configuration option: instead glib will terminate the program if it
1127  * is unable to obtain memory from the operating system.
1128  * @note 2. By default, glib atomic functions are used to provide
1129  * thread-safe manipulation of the reference count. However, the
1130  * header file shared_handle.h can be textually amended before the
1131  * library is compiled to define the symbol
1132  * CGU_SHARED_LOCK_HANDLE_USE_MUTEX so as to use mutexes instead,
1133  * which might be useful for some debugging purposes. Were that to be
1134  * done, Cgu::Thread::MutexError might be thrown by this constructor
1135  * if initialization of the mutex fails.
1136  */
1137  explicit SharedLockHandle(T ptr = 0) {
1138 
1139  if ((ref_items.obj = ptr)) { // not NULL
1140 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1141  try {
1142  ref_items.mutex_p = new Thread::Mutex;
1143  }
1144  catch (...) {
1145  deleter(ptr); // if allocating the object referenced by ref_items.mutex_p
1146  // has failed then delete the object to be referenced to
1147  // avoid a memory leak
1148  throw;
1149  }
1150 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
1151  ref_items.ref_count_p = g_slice_new(unsigned int);
1152  *ref_items.ref_count_p = 1;
1153 # else
1154  try {
1155  ref_items.ref_count_p = new unsigned int(1);
1156  }
1157  catch (...) {
1158  delete ref_items.mutex_p;
1159  deleter(ptr);
1160  throw;
1161  }
1162 # endif
1163 #else
1164 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
1165  ref_items.ref_count_p = g_slice_new(gint);
1166  *ref_items.ref_count_p = 1;
1167 # else
1168  try {
1169  ref_items.ref_count_p = new gint(1);
1170  }
1171  catch (...) {
1172  deleter(ptr); // if allocating the int referenced by ref_items.ref_count_p
1173  // has failed then delete the object to be referenced to
1174  // avoid a memory leak
1175  throw;
1176  }
1177 # endif
1178 #endif
1179  }
1180  else {
1181 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1182  ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
1183 #endif
1184  ref_items.ref_count_p = 0;
1185  }
1186  }
1187 
1188  /**
1189  * Constructor taking an unmanaged object.
1190  * @param ptr The object which the SharedLockHandle is to manage.
1191  * @param tag Passing the tag emumerator
1192  * Cgu::SharedHandleAllocFail::leave causes this constructor not to
1193  * delete the new managed object passed as the 'ptr' argument in the
1194  * event of internal allocation in this method failing because of
1195  * memory exhaustion (in that event, Cgu::SharedHandleError will be
1196  * thrown).
1197  * @exception Cgu::SharedHandleError This constructor might throw
1198  * Cgu::SharedHandleError if memory is exhausted and the system would
1199  * otherwise throw std::bad_alloc in that case. This constructor is
1200  * exception safe (it does not leak resources), and if such an
1201  * exception is thrown it will clean itself up, but it will not
1202  * attempt to delete the new managed object passed to it. Access to
1203  * the object passed to the 'ptr' argument can be obtained via the
1204  * thrown Cgu::SharedHandleError object.
1205  * @note 1. On systems with over-commit/lazy-commit combined with
1206  * virtual memory (swap), it is rarely useful to check for memory
1207  * exhaustion, so in those cases this version of the constructor will
1208  * not be useful.
1209  * @note 2. If the library has been installed using the
1210  * --with-glib-memory-slices-no-compat configuration option this
1211  * version of the constructor will also not be useful: instead glib
1212  * will terminate the program if it is unable to obtain memory from
1213  * the operating system.
1214  * @note 3. By default, glib atomic functions are used to provide
1215  * thread-safe manipulation of the reference count. However, the
1216  * header file shared_handle.h can be textually amended before the
1217  * library is compiled to define the symbol
1218  * CGU_SHARED_LOCK_HANDLE_USE_MUTEX so as to use mutexes instead,
1219  * which might be useful for some debugging purposes. Were that to be
1220  * done, Cgu::SharedHandleError might be thrown by this constructor if
1221  * initialization of the mutex fails (even if the
1222  * --with-glib-memory-slices-no-compat configuration option is
1223  * chosen).
1224  */
1226 
1227  if ((ref_items.obj = ptr)) { // not NULL
1228 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1229  try {
1230  ref_items.mutex_p = new Thread::Mutex;
1231  }
1232  catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly
1233  throw SharedHandleError<T>(ptr);
1234  }
1235  catch (Thread::MutexError&) { // as we are not rethrowing, make NPTL friendly
1236  throw SharedHandleError<T>(ptr);
1237  }
1238 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
1239  ref_items.ref_count_p = g_slice_new(unsigned int);
1240  *ref_items.ref_count_p = 1;
1241 # else
1242  try {
1243  ref_items.ref_count_p = new unsigned int(1);
1244  }
1245  catch (std::bad_alloc&) {
1246  delete ref_items.mutex_p;
1247  throw SharedHandleError<T>(ptr);
1248  }
1249 # endif
1250 #else
1251 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT
1252  ref_items.ref_count_p = g_slice_new(gint);
1253  *ref_items.ref_count_p = 1;
1254 # else
1255  try {
1256  ref_items.ref_count_p = new gint(1);
1257  }
1258  catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly
1259  throw SharedHandleError<T>(ptr);
1260  }
1261 # endif
1262 #endif
1263  }
1264  else {
1265 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1266  ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
1267 #endif
1268  ref_items.ref_count_p = 0;
1269  }
1270  }
1271 
1272 /**
1273  * Causes the SharedLockHandle to cease to manage its managed object
1274  * (if any), deleting it if this is the last ShareLockHandle object
1275  * managing it. If the argument passed is not NULL, the
1276  * SharedLockHandle object will manage the new object passed (which
1277  * must not be managed by any other SharedLockHandle object).
1278  * @param ptr NULL (the default), or a new unmanaged object to manage.
1279  * @exception std::bad_alloc This method will not throw if the 'ptr'
1280  * argument has a NULL value (the default) and the destructor of a
1281  * managed object does not throw, otherwise it might throw
1282  * std::bad_alloc if memory is exhausted and the system throws in that
1283  * case. Note that if such an exception is thrown then this method
1284  * will do nothing (it is strongly exception safe and will continue to
1285  * manage the object it was managing prior to the call), except that
1286  * it will delete the new managed object passed to it to avoid a
1287  * memory leak. If such automatic deletion in the event of such an
1288  * exception is not wanted, use the reset() method taking a
1289  * Cgu::SharedHandleAllocFail::Leave tag type as its second argument.
1290  * @note 1. std::bad_alloc will not be thrown if the library has been
1291  * installed using the --with-glib-memory-slices-no-compat
1292  * configuration option: instead glib will terminate the program if it
1293  * is unable to obtain memory from the operating system.
1294  * @note 2. By default, glib atomic functions are used to provide
1295  * thread-safe manipulation of the reference count. However, the
1296  * header file shared_handle.h can be textually amended before the
1297  * library is compiled to define the symbol
1298  * CGU_SHARED_LOCK_HANDLE_USE_MUTEX so as to use mutexes instead,
1299  * which might be useful for some debugging purposes. Were that to be
1300  * done, Cgu::Thread::MutexError might be thrown by this method if
1301  * initialization of the mutex fails.
1302  * @note 3. A SharedLockHandle object protects its reference count but
1303  * not the managed object or its other internals. The reset() method
1304  * should not be called by one thread in respect of a particular
1305  * SharedLockHandle object while another thread may be operating on,
1306  * copying or dereferencing the same instance of SharedLockHandle. It
1307  * is thread-safe as against another instance of SharedLockHandle
1308  * managing the same object.
1309  */
1310  void reset(T ptr = 0) {
1311  SharedLockHandle tmp(ptr);
1312  std::swap(ref_items, tmp.ref_items);
1313  }
1314 
1315 /**
1316  * Causes the SharedLockHandle to cease to manage its managed object
1317  * (if any), deleting it if this is the last ShareLockHandle object
1318  * managing it. The SharedLockHandle object will manage the new
1319  * object passed (which must not be managed by any other
1320  * SharedLockHandle object). This method is exception safe, but see
1321  * the comments below on Cgu::SharedHandleError.
1322  * @param ptr A new unmanaged object to manage (if no new object is to
1323  * be managed, use the version of reset() taking a default value of
1324  * NULL).
1325  * @param tag Passing the tag emumerator
1326  * Cgu::SharedHandleAllocFail::leave causes this method not to delete
1327  * the new managed object passed as the 'ptr' argument in the event of
1328  * internal allocation in this method failing because of memory
1329  * exhaustion (in that event, Cgu::SharedHandleError will be thrown).
1330  * @exception Cgu::SharedHandleError This method might throw
1331  * Cgu::SharedHandleError if memory is exhausted and the system would
1332  * otherwise throw std::bad_alloc in that case. Note that if such an
1333  * exception is thrown then this method will do nothing (it is
1334  * strongly exception safe and will continue to manage the object it
1335  * was managing prior to the call), and it will not attempt to delete
1336  * the new managed object passed to it (if any). Access to the object
1337  * passed to the 'ptr' argument can be obtained via the thrown
1338  * Cgu::SharedHandleError object.
1339  * @note 1. A SharedLockHandle object protects its reference count but
1340  * not the managed object or its other internals. The reset() method
1341  * should not be called by one thread in respect of a particular
1342  * SharedLockHandle object while another thread may be operating on,
1343  * copying or dereferencing the same instance of SharedLockHandle. It
1344  * is thread-safe as against another instance of SharedLockHandle
1345  * managing the same object.
1346  * @note 2. On systems with over-commit/lazy-commit combined with
1347  * virtual memory (swap), it is rarely useful to check for memory
1348  * exhaustion, so in those cases this version of the reset() method
1349  * will not be useful.
1350  * @note 3. If the library has been installed using the
1351  * --with-glib-memory-slices-no-compat configuration option this
1352  * version of the reset() method will also not be useful: instead glib
1353  * will terminate the program if it is unable to obtain memory from
1354  * the operating system.
1355  * @note 4. By default, glib atomic functions are used to provide
1356  * thread-safe manipulation of the reference count. However, the
1357  * header file shared_handle.h can be textually amended before the
1358  * library is compiled to define the symbol
1359  * CGU_SHARED_LOCK_HANDLE_USE_MUTEX so as to use mutexes instead,
1360  * which might be useful for some debugging purposes. Were that to be
1361  * done, Cgu::SharedHandleError might be thrown by this method if
1362  * initialization of the mutex fails (even if the
1363  * --with-glib-memory-slices-no-compat configuration option is
1364  * chosen).
1365  */
1367  SharedLockHandle tmp(ptr, tag);
1368  std::swap(ref_items, tmp.ref_items);
1369  }
1370 
1371  /**
1372  * The copy constructor does not throw.
1373  * @param sh_hand The handle to be copied.
1374  */
1376  ref_items = sh_hand.ref_items;
1377  reference();
1378  }
1379 
1380  /**
1381  * The move constructor does not throw. It has move semantics.
1382  * @param sh_hand The handle to be moved.
1383  */
1385  ref_items = sh_hand.ref_items;
1386 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1387  sh_hand.ref_items.mutex_p = 0; // make sure the value is valid as we may assign it
1388 #endif
1389  sh_hand.ref_items.ref_count_p = 0;
1390  sh_hand.ref_items.obj = 0;
1391  }
1392 
1393  /**
1394  * This method (and so copy or move assignment) does not throw unless
1395  * the destructor of a managed object throws.
1396  * @param sh_hand the assignor.
1397  * @return The SharedLockHandle object after assignment.
1398  */
1399  // having a value type as the argument, rather than reference to const
1400  // and then initialising a tmp object, gives the compiler more scope
1401  // for optimisation
1403  std::swap(ref_items, sh_hand.ref_items);
1404  return *this;
1405  }
1406 
1407  /**
1408  * This method does not throw.
1409  * @return A pointer to the handled object (or NULL if none is
1410  * handled).
1411  */
1412  T get() const {return ref_items.obj;}
1413 
1414  /**
1415  * This method does not throw.
1416  * @return A pointer to the handled object (or NULL if none is
1417  * handled).
1418  */
1419  operator T() const {return ref_items.obj;}
1420 
1421  /**
1422  * This method does not throw.
1423  * @return The number of SharedLockHandle objects referencing the
1424  * managed object (or 0 if none is managed by this SharedLockHandle).
1425  * @note The return value may not be valid if another thread has
1426  * changed the reference count before the value returned by this
1427  * method is acted on. It is provided as a utility, but may not be
1428  * meaningful, depending on the intended usage.
1429  */
1430  unsigned int get_refcount() const {
1431  if (!ref_items.ref_count_p) return 0;
1432 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX
1433  Thread::Mutex::Lock lock(*ref_items.mutex_p);
1434  return *ref_items.ref_count_p;
1435 #else
1436  return g_atomic_int_get(ref_items.ref_count_p);
1437 #endif
1438  }
1439 
1440  /**
1441  * The destructor does not throw unless the destructor of a handled
1442  * object throws - that should never happen.
1443  */
1444  ~SharedLockHandle() {unreference();}
1445 };
1446 
1447 #if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING)
1448 
1449 // we can use built-in operator == when comparing pointers referencing
1450 // different objects of the same type
1451 /**
1452  * @ingroup handles
1453  *
1454  * This comparison operator does not throw. It compares the addresses
1455  * of the managed objects.
1456  *
1457  * Since 2.0.0-rc2
1458  */
1459 template <class T, class Dealloc>
1461  return (s1.get() == s2.get());
1462 }
1463 
1464 /**
1465  * @ingroup handles
1466  *
1467  * This comparison operator does not throw. It compares the addresses
1468  * of the managed objects.
1469  *
1470  * Since 2.0.0-rc2
1471  */
1472 template <class T, class Dealloc>
1474  return !(s1 == s2);
1475 }
1476 
1477 // we must use std::less rather than the < built-in operator for
1478 // pointers to objects not within the same array or object: "For
1479 // templates greater, less, greater_equal, and less_equal, the
1480 // specializations for any pointer type yield a total order, even if
1481 // the built-in operators <, >, <=, >= do not." (para 20.3.3/8).
1482 /**
1483  * @ingroup handles
1484  *
1485  * This comparison operator does not throw. It compares the addresses
1486  * of the managed objects.
1487  *
1488  * Since 2.0.0-rc2
1489  */
1490 template <class T, class Dealloc>
1491 bool operator<(const SharedHandle<T, Dealloc>& s1, const SharedHandle<T, Dealloc>& s2) {
1492  return std::less<T>()(s1.get(), s2.get());
1493 }
1494 
1495 /**
1496  * @ingroup handles
1497  *
1498  * This comparison operator does not throw. It compares the addresses
1499  * of the managed objects.
1500  *
1501  * Since 2.0.0-rc2
1502  */
1503 template <class T, class Dealloc>
1505  return (s1.get() == s2.get());
1506 }
1507 
1508 /**
1509  * @ingroup handles
1510  *
1511  * This comparison operator does not throw. It compares the addresses
1512  * of the managed objects.
1513  *
1514  * Since 2.0.0-rc2
1515  */
1516 template <class T, class Dealloc>
1518  return !(s1 == s2);
1519 }
1520 
1521 /**
1522  * @ingroup handles
1523  *
1524  * This comparison operator does not throw. It compares the addresses
1525  * of the managed objects.
1526  *
1527  * Since 2.0.0-rc2
1528  */
1529 template <class T, class Dealloc>
1530 bool operator<(const SharedLockHandle<T, Dealloc>& s1, const SharedLockHandle<T, Dealloc>& s2) {
1531  return std::less<T>()(s1.get(), s2.get());
1532 }
1533 
1534 #endif // CGU_USE_SMART_PTR_COMPARISON
1535 
1536 } // namespace Cgu
1537 
1538 // doxygen produces long filenames that tar can't handle:
1539 // we have generic documentation for std::hash specialisations
1540 // in doxygen.main.in
1541 #if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING)
1542 /* These structs allow SharedHandle and SharedLockHandle objects to be
1543  keys in unordered associative containers */
1544 namespace std {
1545 template <class T, class Dealloc>
1546 struct hash<Cgu::SharedHandle<T, Dealloc>> {
1547  typedef std::size_t result_type;
1548  typedef Cgu::SharedHandle<T, Dealloc> argument_type;
1549  result_type operator()(const argument_type& s) const {
1550  // this is fine: std::hash structs do not normally contain data and
1551  // std::hash<T*> certainly won't, so we don't have overhead constructing
1552  // std::hash<T*> on the fly
1553  return std::hash<T>()(s.get());
1554  }
1555 };
1556 template <class T, class Dealloc>
1557 struct hash<Cgu::SharedLockHandle<T, Dealloc>> {
1558  typedef std::size_t result_type;
1559  typedef Cgu::SharedLockHandle<T, Dealloc> argument_type;
1560  result_type operator()(const argument_type& s) const {
1561  // this is fine: std::hash structs do not normally contain data and
1562  // std::hash<T*> certainly won't, so we don't have overhead constructing
1563  // std::hash<T*> on the fly
1564  return std::hash<T>()(s.get());
1565  }
1566 };
1567 } // namespace std
1568 #endif // CGU_USE_SMART_PTR_COMPARISON
1569 
1570 #endif