c++-gtk-utils
|
00001 /* Copyright (C) 2004 to 2012 Chris Vine 00002 00003 The library comprised in this file or of which this file is part is 00004 distributed by Chris Vine under the GNU Lesser General Public 00005 License as follows: 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Lesser General Public License 00009 as published by the Free Software Foundation; either version 2.1 of 00010 the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, but 00013 WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Lesser General Public License, version 2.1, for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public 00018 License, version 2.1, along with this library (see the file LGPL.TXT 00019 which came with this source code package in the c++-gtk-utils 00020 sub-directory); if not, write to the Free Software Foundation, Inc., 00021 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00022 00023 However, it is not intended that the object code of a program whose 00024 source code instantiates a template from this file or uses macros or 00025 inline functions (of any length) should by reason only of that 00026 instantiation or use be subject to the restrictions of use in the GNU 00027 Lesser General Public License. With that in mind, the words "and 00028 macros, inline functions and instantiations of templates (of any 00029 length)" shall be treated as substituted for the words "and small 00030 macros and small inline functions (ten lines or less in length)" in 00031 the fourth paragraph of section 5 of that licence. This does not 00032 affect any other reason why object code may be subject to the 00033 restrictions in that licence (nor for the avoidance of doubt does it 00034 affect the application of section 2 of that licence to modifications 00035 of the source code in this file). 00036 00037 */ 00038 00039 #ifndef CGU_SHARED_HANDLE_H 00040 #define CGU_SHARED_HANDLE_H 00041 00042 // define this if, instead of GLIB atomic funcions/memory barriers, 00043 // you want to use a (slower) mutex to lock the reference count in the 00044 // SharedLockHandle class (however, if wanted, this is best left for 00045 // definition in the user code) 00046 /* #define CGU_SHARED_LOCK_HANDLE_USE_MUTEX 1 */ 00047 00048 #include <exception> 00049 #include <new> 00050 #include <functional> // for std::less and std::hash<T*> 00051 #include <utility> // for std::swap 00052 #include <cstddef> // for std::size_t 00053 #include <cstdlib> 00054 00055 #include <glib.h> 00056 00057 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 00058 #include <c++-gtk-utils/mutex.h> 00059 #endif 00060 00061 #include <c++-gtk-utils/cgu_config.h> 00062 00063 /** 00064 * @addtogroup handles handles and smart pointers 00065 */ 00066 00067 namespace Cgu { 00068 00069 /** 00070 * @class SharedHandle shared_handle.h c++-gtk-utils/shared_handle.h 00071 * @brief This is a generic class for managing the lifetime of objects 00072 * allocated on freestore. 00073 * @ingroup handles 00074 * @sa SharedLockHandle 00075 * @sa ScopedHandle 00076 * @sa SharedHandleError 00077 * @sa GcharSharedHandle 00078 * @sa GerrorSharedHandle 00079 * @sa StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy 00080 * 00081 * The SharedHandle class is similar to the SharedPtr class (it keeps 00082 * a reference count and deletes the handled object when the count 00083 * reaches 0), but it does not have pointer semantics. Accordingly, 00084 * it can be used to manage the memory of arrays and other objects 00085 * allocated on the heap. 00086 * 00087 * Because it is useful with arrays, by default it deallocates memory 00088 * using C++ delete[]. However, if a SharedHandle object is passed a 00089 * function object type as a second template argument when 00090 * instantiated, it will use that function object to delete memory. 00091 * This enables it to handle the memory of any object, such as objects 00092 * to be deleted using std::free() or Glib's g_free(), g_list_free() 00093 * or g_slice_free(). Instances (such as @ref GcharScopedHandleAnchor 00094 * "GcharScopedHandle", @ref GcharSharedHandleAnchor 00095 * "GcharSharedHandle", @ref GerrorSharedHandleAnchor 00096 * "GerrorSharedHandle" and @ref GerrorScopedHandleAnchor 00097 * "GerrorScopedHandle") typdef'ed for particular deleters can 00098 * conveniently manage objects of any kind. 00099 * 00100 * To reflect the fact that it is just a handle for a pointer, it has 00101 * different instantiation semantics from a SharedPtr object. A 00102 * SharedPtr object is instantiated using this syntax: 00103 * 00104 * @code SharedPtr<ObjType> sh_ptr(new ObjType); @endcode 00105 * 00106 * A SharedHandle is instantiated using this syntax (note that the 00107 * instantiated handle is for type T* and not T): 00108 * 00109 * @code SharedHandle<ObjType*> sh_handle(new ObjType[n]); @endcode 00110 * 00111 * 00112 * Apart from the operatorT() type conversion operator (which returns 00113 * the underlying pointer), the only other method to obtain the 00114 * underlying pointer is the get() method. If the object referenced 00115 * is an array allocated on the heap, to use indexing you could either 00116 * do this: 00117 * 00118 * @code 00119 * using namespace Cgu; 00120 * SharedHandle<char*> handle(new char[10]); 00121 * handle.get()[0] = 'a'; 00122 * std::cout << handle.get()[0] << std::endl; 00123 * @endcode 00124 * 00125 * or this: 00126 * 00127 * @code 00128 * using namespace Cgu; 00129 * SharedHandle<char*> handle(new char[10]); 00130 * handle[0] = 'a'; 00131 * std::cout << handle[0] << std::endl; 00132 * @endcode 00133 * 00134 * There is also a SharedLockHandle class, which has a thread-safe 00135 * reference count, and a ScopedHandle class, which deletes its object 00136 * as soon as it goes out of scope. A ScopedHandle class can be 00137 * viewed as a SharedHandle which cannot be assigned to or used as the 00138 * argument to a copy constructor and therefore which cannot have a 00139 * reference count of more than 1. It is used where, if you wanted 00140 * pointer semantics, you might use a const std::auto_ptr<>. 00141 * 00142 * SharedHandle objects can be instantiated for pointers to constant 00143 * objects (such as SharedHandle<const char*>), provided the deleter 00144 * functor will take such pointers. 00145 * 00146 * This library provides StandardArrayDelete, CFree, GFree, 00147 * GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter 00148 * functors, which can be used as the second template parameter of the 00149 * SharedHandle class. As mentioned above, StandardArrayDelete is the 00150 * default, and some typedef'ed instances of SharedHandle for gchar 00151 * (with the GFree deleter) and for GError (with the GerrorFree 00152 * deleter) are provided. 00153 * 00154 * @b Comparison @b with @b std::shared_ptr 00155 * 00156 * Although the semantics of std::shared_ptr in C++11 are not 00157 * particularly suited to managing either arrays or C objects with 00158 * accessor functions (such as in glib), most of the things that can 00159 * be done by this class can be done by using std::shared_ptr with a 00160 * specialised deleter. However, this class is retained in the 00161 * c++-gtk-utils library not only to retain compatibility with series 00162 * 1.2 of the library, but also to cater for some cases not met (or 00163 * not so easily met) by std::shared_ptr: 00164 * 00165 * (i) The Cgu::SharedHandle class takes its deleter as a template 00166 * parameter, which means that typedefs can be used to enable handles 00167 * for particular deleters to be easily created (and as mentioned, 00168 * this library provides a number of pre-formed deleter functors and 00169 * typedefs for them). With std::shared_ptr, custom deleters must be 00170 * passed to the shared_ptr constructor on every occasion a shared_ptr 00171 * is constructed to manage a new object (and they cannot be templated 00172 * as a typedef). 00173 * 00174 * (ii) Glib memory slices provide an efficient small object allocator 00175 * (they are likely to be significantly more efficient than global 00176 * operator new()/new[](), which generally hand off to malloc(), and 00177 * whilst malloc() is good for large block allocations it is generally 00178 * poor as a small object allocator). Internal Cgu::SharedHandle 00179 * allocation using glib memory slices can be achieved by compiling 00180 * the library with the --with-glib-memory-slices-no-compat 00181 * configuration option. 00182 * 00183 * (iii) If glib memory slices are not used (which do not throw), 00184 * constructing a shared pointer for a new managed object (or calling 00185 * reset() for a new managed object) might throw if internal 00186 * allocation fails. Although by default the Cgu::SharedHandle 00187 * implementation will delete the new managed object in such a case, 00188 * it also provides an alternative constructor and reset() method 00189 * which instead enable the new object to be accessed via the thrown 00190 * exception object so that user code can decide what to do; 00191 * std::shared_ptr deletes the new object in every case. 00192 * 00193 * (iv) A user can explicitly state whether the shared handle object 00194 * is to have atomic increment and decrement-and-test with respect to 00195 * the reference count so that the reference count is thread safe 00196 * ('no' in the case of Cgu::SharedHandle, and 'yes' in the case of 00197 * Cgu::SharedLockHandle). Using atomic functions is unnecessary if 00198 * the managed object concerned is only addressed in one thread (and 00199 * might cause unwanted cache flushing in certain circumstances). 00200 * std::shared_ptr will generally always use atomic functions with 00201 * respect to its reference count in a multi-threaded program. 00202 * 00203 * In favour of std::shared_ptr, it has an associated std::weak_ptr 00204 * class, which Cgu::SharedHandle does not (there is a 00205 * Cgu::GobjWeakHandle class, but that is cognate with Cgu::GobjHandle 00206 * and is only usable with GObjects). 00207 */ 00208 00209 /********************* here are some deleter classes *******************/ 00210 00211 /** 00212 * @class StandardArrayDelete shared_handle.h c++-gtk-utils/shared_handle.h 00213 * @brief A deleter functor for use as the second (Dealloc) template 00214 * parameter of the SharedHandle, SharedLockHandle or ScopedHandle 00215 * template classes, which calls the C++ delete[] expression. 00216 * @ingroup handles 00217 * @details This functor enables those classes to manage arrays 00218 * created with the new expression. It is the default type of the 00219 * second template paramenter of those classes. 00220 */ 00221 template <class T> class StandardArrayDelete { 00222 public: 00223 void operator()(T obj) { 00224 delete[] obj; 00225 } 00226 }; 00227 00228 /** 00229 * @class CFree shared_handle.h c++-gtk-utils/shared_handle.h 00230 * @brief A deleter functor for use as the second (Dealloc) template 00231 * parameter of the SharedHandle, SharedLockHandle or ScopedHandle 00232 * template classes, which calls std::free. 00233 * @ingroup handles 00234 * @details This functor enables those classes to manage memory 00235 * allocated with std::malloc(), std::calloc() and std::realloc(). 00236 */ 00237 class CFree { 00238 public: 00239 void operator()(const void* obj) { 00240 std::free(const_cast<void*>(obj)); 00241 } 00242 }; 00243 00244 /** 00245 * @class GFree shared_handle.h c++-gtk-utils/shared_handle.h 00246 * @brief A deleter functor for use as the second (Dealloc) template 00247 * parameter of the SharedHandle, SharedLockHandle or ScopedHandle 00248 * template classes, which calls glib's g_free(). 00249 * @ingroup handles 00250 * @details This functor enables those classes to manage memory 00251 * allocated by glib or gtk+ functions which requires to be freed with 00252 * g_free(). It is used in the typedefs @ref GcharSharedHandleAnchor 00253 * "GcharSharedHandle" and @ref GcharScopedHandleAnchor 00254 * "GcharScopedHandle". 00255 */ 00256 class GFree { 00257 public: 00258 void operator()(const void* obj) { 00259 g_free(const_cast<void*>(obj)); 00260 } 00261 }; 00262 00263 /** 00264 * @class GSliceFree shared_handle.h c++-gtk-utils/shared_handle.h 00265 * @brief A deleter functor for use as the second (Dealloc) template 00266 * parameter of the SharedHandle, SharedLockHandle or ScopedHandle 00267 * template classes, which calls glib's g_slice_free1(). 00268 * @ingroup handles 00269 * 00270 * @details This functor enables those classes to manage a memory 00271 * block allocated using glib memory slices. The managed memory block 00272 * to be deleted by the GSliceFree functor must have the same size as 00273 * the size of the object for which the functor is instantiated by 00274 * pointer, as for example as allocated with the g_slice_new, 00275 * g_slice_new0 or g_slice_dup macros (in other words, the GSliceFree 00276 * template parameter must match the argument passed to those macros): 00277 * see the example below. Use GSliceFreeSize where it is necessary or 00278 * more convenient to have the size of the block to be freed as the 00279 * template parameter. Use GSliceDestroy where the memory holds a C++ 00280 * object constructed in the memory by the global placement new 00281 * expression. 00282 * 00283 * The type of the template argument for the functor is a pointer to 00284 * the managed type: it is the same as the first template argument of 00285 * the relevant SharedHandle, SharedLockHandle or ScopedHandle object. 00286 * For example: 00287 * 00288 * @code 00289 * using namespace Cgu; 00290 * SharedHandle<MyStruct*, GSliceFree<MyStruct*> > h(g_slice_new(MyStruct)); 00291 * ... 00292 * @endcode 00293 * 00294 * The availability of this functor is not dependent on the library 00295 * having been installed with the --with-glib-memory-slices-compat or 00296 * --with-glib-memory-slices-no-compat configuration option (see @ref 00297 * Memory for further details of those options). 00298 */ 00299 template <class T> class GSliceFree { 00300 public: 00301 void operator()(T obj) { 00302 g_slice_free1(sizeof(*obj), (void*)obj); 00303 } 00304 }; 00305 00306 /** 00307 * @class GSliceDestroy shared_handle.h c++-gtk-utils/shared_handle.h 00308 * @brief A deleter functor for use as the second (Dealloc) template 00309 * parameter of the SharedHandle, SharedLockHandle or ScopedHandle 00310 * template classes, which calls glib's g_slice_free1(), but before 00311 * doing so also explicitly calls the destructor of a C++ object 00312 * constructed in the memory. 00313 * @ingroup handles 00314 * 00315 * @details The managed memory block to be deleted by the 00316 * GSliceDestroy functor must have the same size as the size of the 00317 * object for which the functor is instantiated by pointer, as for 00318 * example as allocated with the g_slice_new or g_slice_new0 macros 00319 * (in other words, the GSliceDestroy template parameter must match 00320 * the argument passed to those macros), and the memory block must 00321 * have had that object constructed in it with the global placement 00322 * new expression: see the example below. Sometimes it is more 00323 * convenient to implement C++ objects in glib memory slices that way, 00324 * rather than to have custom new and delete member operators of the 00325 * classes concerned which use glib's g_slice_*(). However, a 00326 * SharedHandle class with a GSliceDestroy deleter is not as easy to 00327 * use as the SharedPtr class, as SharedHandle has no operator*() nor 00328 * operator->() method (the get() method would have to be used to 00329 * obtain the underlying pointer). 00330 * 00331 * One consequence of the static sizing (and so typing) of memory 00332 * slices is that a GSliceDestroy object instantiated for the 00333 * management of a particular class must not be used by a 00334 * SharedHandle, SharedLockHandle or ScopedHandle object which 00335 * attempts to manage a class derived from it. This comes back to the 00336 * point that the GSliceDestroy template parameter must match the 00337 * argument passed to the g_slice_new or g_slice_new0 macros. 00338 * 00339 * The type of the template argument for the functor is a pointer to 00340 * the managed type: it is the same as the first template argument of 00341 * the relevant SharedHandle, SharedLockHandle or ScopedHandle object. 00342 * For example, to construct a SharedHandle managing an object of type 00343 * MyClass to be constructed in a glib memory slice in an exception 00344 * safe way: 00345 * 00346 * @code 00347 * using namespace Cgu; 00348 * SharedHandle<MyClass*, GSliceDestroy<MyClass*> > h; // won't throw 00349 * { // scope block for p variable 00350 * MyClass* p = g_slice_new(MyClass); 00351 * try {new(p) MyClass;} // MyClass constructor might throw 00352 * catch(...) { 00353 * g_slice_free(MyClass, p); 00354 * throw; 00355 * } 00356 * h.reset(p); // might throw but if so cleans up 00357 * } 00358 * ... 00359 * @endcode 00360 * 00361 * The availability of this functor is not dependent on the library 00362 * having been installed with the --with-glib-memory-slices-compat or 00363 * --with-glib-memory-slices-no-compat configuration option (see @ref 00364 * Memory for further details of those options). 00365 */ 00366 template <class T> class GSliceDestroy { 00367 template <class U> void destroy(U& obj) {obj.~U();} 00368 public: 00369 void operator()(T obj) { 00370 destroy(*obj); 00371 g_slice_free1(sizeof(*obj), (void*)obj); 00372 } 00373 }; 00374 00375 /** 00376 * @class GSliceFreeSize shared_handle.h c++-gtk-utils/shared_handle.h 00377 * @brief A deleter functor for use as the second (Dealloc) template 00378 * parameter of the SharedHandle, SharedLockHandle or ScopedHandle 00379 * template classes, which calls glib's g_slice_free1(). 00380 * @ingroup handles 00381 * 00382 * @details This functor enables those classes to manage memory 00383 * allocated with g_slice_alloc(), g_slice_alloc0() or g_slice_copy(). 00384 * It is an alternative to using GSliceFree where, instead of the 00385 * template parameter being a pointer to a particular managed type, 00386 * the size of the memory block to be freed is passed, so enabling it 00387 * to be more conveniently used to free memory containing arrays of 00388 * built-in types or of PODSs. Use GSliceDestroy where the memory 00389 * holds a C++ object constructed in the memory by the global 00390 * placement new expression. 00391 * 00392 * The type of the template argument for the functor is an integer 00393 * type (gsize) and is the size of the block to be managed. For 00394 * example: 00395 * 00396 * @code 00397 * using namespace Cgu; 00398 * SharedHandle<char*, GSliceFreeSize<10> > h(static_cast<char*>(g_slice_alloc(10))); 00399 * ... 00400 * @endcode 00401 * 00402 * The availability of this functor is not dependent on the library 00403 * having been installed with the --with-glib-memory-slices-compat or 00404 * --with-glib-memory-slices-no-compat configuration option (see @ref 00405 * Memory for further details of those options). 00406 */ 00407 template <gsize block_size> class GSliceFreeSize { 00408 public: 00409 void operator()(const void* obj) { 00410 g_slice_free1(block_size, const_cast<void*>(obj)); 00411 } 00412 }; 00413 00414 /* 00415 * we could provide a functor class for 00416 * g_slice_free_chain_with_offset() such as: 00417 * 00418 * template <class T, gsize offset> class GSliceFreeChain { 00419 * public: 00420 * void operator()(T obj) { 00421 * g_slice_free_chain_with_offset(sizeof(*obj), (void*)obj, offset); 00422 * } 00423 * }; 00424 * 00425 * However, this is not going to be particularly useful because the 00426 * G_STRUCT_OFFSET macro and/or C's offsetof macro, needed to provide 00427 * the value for the offset parameter, do not work for other than 00428 * PODSs. g_slice_free_chain_with_offset() is intended for internal 00429 * implementations and in the event of a user wanting such memory 00430 * management it is best achieved by having custom new[] and delete[] 00431 * member operators of the class concerned which use glib's 00432 * g_slice_*() directly. 00433 */ 00434 00435 /********************* define some typedefs for Glib ******************/ 00436 00437 template <class T, class Dealloc> class SharedHandle; 00438 template <class T, class Dealloc> class ScopedHandle; 00439 00440 /** 00441 * @typedef GcharSharedHandle. 00442 * @brief A handle comprising a typed instance of the SharedHandle 00443 * class for gchar* arrays and strings 00444 * @anchor GcharSharedHandleAnchor 00445 * @ingroup handles 00446 * \#include <c++-gtk-utils/shared_handle.h> 00447 */ 00448 typedef SharedHandle<gchar*, GFree> GcharSharedHandle; 00449 00450 /** 00451 * @typedef GcharScopedHandle. 00452 * @brief A handle comprising a typed instance of the ScopedHandle 00453 * class for gchar* arrays and strings 00454 * @anchor GcharScopedHandleAnchor 00455 * @ingroup handles 00456 * \#include <c++-gtk-utils/shared_handle.h> 00457 */ 00458 typedef ScopedHandle<gchar*, GFree> GcharScopedHandle; 00459 00460 00461 /******************* now the handle class definitions *****************/ 00462 00463 /** 00464 * @class SharedHandleError shared_handle.h c++-gtk-utils/shared_handle.h 00465 * @brief This is an exception struct thrown as an alternative to 00466 * deleting a managed object when internal memory allocation for 00467 * SharedHandle or SharedLockHandle fails in their reset() method or 00468 * in their constructor which takes a pointer. 00469 * @sa SharedHandle SharedLockHandle SharedHandleAllocFail 00470 * @ingroup handles 00471 * 00472 * This is an exception struct thrown as an alternative to deleting a 00473 * managed object when SharedHandle<T>::SharedHandle(T), 00474 * SharedLockHandle<T>::SharedLockHandle(T), SharedHandle<T>::reset(T) 00475 * or SharedLockHandle<T>::reset(T) would otherwise throw 00476 * std::bad_alloc. To make those methods do that, 00477 * Cgu::SharedHandleAllocFail::leave is passed as their second 00478 * argument. 00479 * 00480 * If the exception is thrown, the struct has a member 'obj' of type 00481 * T, which is a pointer to the object or array originally passed to 00482 * those methods, so the user can deal with it appropriately. This 00483 * enables the result of the new expression to be passed directly as 00484 * the argument to those methods without giving rise to a resource 00485 * leak, as in: 00486 * 00487 * @code 00488 * using namespace Cgu; 00489 * SharedHandle<T*> s; // doesn't throw 00490 * try { 00491 * s.reset(new T[2], SharedHandleAllocFail::leave); // both T allocation and reset() might throw 00492 * } 00493 * catch (std::bad_alloc&) { 00494 * ... 00495 * } 00496 * catch (SharedHandleError<T*>& e) { 00497 * e.obj[0].do_something(); 00498 * e.obj[1].do_something(); 00499 * ... 00500 * } 00501 * ... 00502 * @endcode 00503 * 00504 * As above, a catch block will need to deal with std::bad_alloc (if 00505 * the call to the new expression when creating the T object fails) 00506 * as well as SharedHandleError (if the call to the new expression in 00507 * the reset() method fails after a valid T object has been 00508 * constructed). 00509 */ 00510 00511 template <class T> struct SharedHandleError: public std::exception { 00512 T obj; 00513 virtual const char* what() const throw() {return "SharedHandleError\n";} 00514 SharedHandleError(T p): obj(p) {} 00515 }; 00516 00517 /** 00518 * enum Cgu::SharedHandleAllocFail::Leave 00519 * The enumerator Cgu::SharedHandleAllocFail::leave is passed as the 00520 * second argument of the reset() method of SharedHandle or 00521 * SharedLockHandle in order to prevent the method deleting the object 00522 * passed to it if reset() fails internally because of memory 00523 * exhaustion. 00524 * @ingroup handles 00525 */ 00526 namespace SharedHandleAllocFail { 00527 enum Leave {leave}; 00528 } 00529 00530 template <class T, class Dealloc = StandardArrayDelete<T>> class SharedHandle { 00531 00532 Dealloc deleter; 00533 00534 #ifndef DOXYGEN_PARSING 00535 struct RefItems { 00536 unsigned int* ref_count_p; 00537 T obj; 00538 } ref_items; 00539 #endif 00540 00541 void unreference() { 00542 if (!ref_items.ref_count_p) return; 00543 --(*ref_items.ref_count_p); 00544 if (*ref_items.ref_count_p == 0) { 00545 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 00546 g_slice_free(unsigned int, ref_items.ref_count_p); 00547 #else 00548 delete ref_items.ref_count_p; 00549 #endif 00550 deleter(ref_items.obj); 00551 } 00552 } 00553 00554 void reference() { 00555 if (!ref_items.ref_count_p) return; 00556 ++(*ref_items.ref_count_p); 00557 } 00558 00559 public: 00560 /** 00561 * Constructor taking an unmanaged object. 00562 * @param ptr The object which the SharedHandle is to manage (if 00563 * any). 00564 * @exception std::bad_alloc This constructor will not throw if the 00565 * 'ptr' argument has a NULL value (the default), otherwise it might 00566 * throw std::bad_alloc if memory is exhausted and the system throws 00567 * in that case. If such an exception is thrown, this constructor is 00568 * exception safe (it does not leak resources), but as well as 00569 * cleaning itself up this constructor will also delete the managed 00570 * object passed to it to avoid a memory leak. If such automatic 00571 * deletion is not wanted in that case, use the version of this 00572 * constructor taking a Cgu::SharedHandleAllocFail::Leave tag argument. 00573 * @note std::bad_alloc will not be thrown if the library has been 00574 * installed using the --with-glib-memory-slices-no-compat 00575 * configuration option: instead glib will terminate the program if it 00576 * is unable to obtain memory from the operating system. 00577 */ 00578 explicit SharedHandle(T ptr = 0) { 00579 00580 if ((ref_items.obj = ptr)) { // not NULL 00581 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 00582 ref_items.ref_count_p = g_slice_new(unsigned int); 00583 *ref_items.ref_count_p = 1; 00584 #else 00585 try { 00586 ref_items.ref_count_p = new unsigned int(1); 00587 } 00588 catch (...) { 00589 deleter(ptr); // if allocating the int referenced by ref_items.ref_count_p 00590 // has failed then delete the object to be referenced to 00591 // avoid a memory leak 00592 throw; 00593 } 00594 #endif 00595 } 00596 else ref_items.ref_count_p = 0; 00597 } 00598 00599 /** 00600 * Constructor taking an unmanaged object. 00601 * @param ptr The object which the SharedHandle is to manage 00602 * @param tag Passing the tag emumerator 00603 * Cgu::SharedHandleAllocFail::leave causes this constructor not to 00604 * delete the new managed object passed as the 'ptr' argument in the 00605 * event of internal allocation in this method failing because of 00606 * memory exhaustion (in that event, Cgu::SharedHandleError will be 00607 * thrown). 00608 * @exception Cgu::SharedHandleError This constructor might throw 00609 * Cgu::SharedHandleError if memory is exhausted and the system would 00610 * otherwise throw std::bad_alloc in that case. This constructor is 00611 * exception safe (it does not leak resources), and if such an 00612 * exception is thrown it will clean itself up, but it will not 00613 * attempt to delete the new managed object passed to it. Access to 00614 * the object passed to the 'ptr' argument can be obtained via the 00615 * thrown Cgu::SharedHandleError object. 00616 * @note 1. On systems with over-commit/lazy-commit combined with 00617 * virtual memory (swap), it is rarely useful to check for memory 00618 * exhaustion, so in those cases this version of the constructor will 00619 * not be useful. 00620 * @note 2. If the library has been installed using the 00621 * --with-glib-memory-slices-no-compat configuration option this 00622 * version of the constructor will also not be useful: instead glib 00623 * will terminate the program if it is unable to obtain memory from 00624 * the operating system. 00625 */ 00626 SharedHandle(T ptr, Cgu::SharedHandleAllocFail::Leave tag) { 00627 00628 if ((ref_items.obj = ptr)) { // not NULL 00629 #ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 00630 ref_items.ref_count_p = g_slice_new(unsigned int); 00631 *ref_items.ref_count_p = 1; 00632 #else 00633 try { 00634 ref_items.ref_count_p = new unsigned int(1); 00635 } 00636 catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly 00637 throw SharedHandleError<T>(ptr); 00638 } 00639 #endif 00640 } 00641 else ref_items.ref_count_p = 0; 00642 } 00643 00644 /** 00645 * Causes the SharedHandle to cease to manage its managed object (if 00646 * any), deleting it if this is the last SharedHandle object managing 00647 * it. If the argument passed is not NULL, the SharedHandle object 00648 * will manage the new object passed (which must not be managed by any 00649 * other SharedHandle object). This method is exception safe, but see 00650 * the comments below on std::bad_alloc. 00651 * @param ptr NULL (the default), or a new unmanaged object to manage. 00652 * @exception std::bad_alloc This method will not throw if the 'ptr' 00653 * argument has a NULL value (the default) and the destructor of a 00654 * managed object does not throw, otherwise it might throw 00655 * std::bad_alloc if memory is exhausted and the system throws in that 00656 * case. Note that if such an exception is thrown then this method 00657 * will do nothing (it is strongly exception safe and will continue to 00658 * manage the object it was managing prior to the call), except that 00659 * it will delete the new managed object passed to it to avoid a 00660 * memory leak. If such automatic deletion in the event of such an 00661 * exception is not wanted, use the reset() method taking a 00662 * Cgu::SharedHandleAllocFail::Leave tag type as its second argument. 00663 * @note std::bad_alloc will not be thrown if the library has been 00664 * installed using the --with-glib-memory-slices-no-compat 00665 * configuration option: instead glib will terminate the program if it 00666 * is unable to obtain memory from the operating system. 00667 */ 00668 void reset(T ptr = 0) { 00669 SharedHandle tmp(ptr); 00670 std::swap(ref_items, tmp.ref_items); 00671 } 00672 00673 /** 00674 * Causes the SharedHandle to cease to manage its managed object (if 00675 * any), deleting it if this is the last SharedHandle object managing 00676 * it. The SharedHandle object will manage the new object passed 00677 * (which must not be managed by any other SharedHandle object). This 00678 * method is exception safe, but see the comments below on 00679 * Cgu::SharedHandleError. 00680 * @param ptr A new unmanaged object to manage (if no new object is to 00681 * be managed, use the version of reset() taking a default value of 00682 * NULL). 00683 * @param tag Passing the tag emumerator 00684 * Cgu::SharedHandleAllocFail::leave causes this method not to delete 00685 * the new managed object passed as the 'ptr' argument in the event of 00686 * internal allocation in this method failing because of memory 00687 * exhaustion (in that event, Cgu::SharedHandleError will be thrown). 00688 * @exception Cgu::SharedHandleError This method might throw 00689 * Cgu::SharedHandleError if memory is exhausted and the system would 00690 * otherwise throw std::bad_alloc in that case. Note that if such an 00691 * exception is thrown then this method will do nothing (it is 00692 * strongly exception safe and will continue to manage the object it 00693 * was managing prior to the call), and it will not attempt to delete 00694 * the new managed object passed to it. Access to the object passed 00695 * to the 'ptr' argument can be obtained via the thrown 00696 * Cgu::SharedHandleError object. 00697 * @note 1. On systems with over-commit/lazy-commit combined with 00698 * virtual memory (swap), it is rarely useful to check for memory 00699 * exhaustion, so in those cases this version of the reset() method 00700 * will not be useful. 00701 * @note 2. If the library has been installed using the 00702 * --with-glib-memory-slices-no-compat configuration option this 00703 * version of the reset() method will also not be useful: instead glib 00704 * will terminate the program if it is unable to obtain memory from 00705 * the operating system. 00706 */ 00707 void reset(T ptr, Cgu::SharedHandleAllocFail::Leave tag) { 00708 SharedHandle tmp(ptr, tag); 00709 std::swap(ref_items, tmp.ref_items); 00710 } 00711 00712 /** 00713 * The copy constructor does not throw. 00714 * @param sh_hand The handle to be copied. 00715 */ 00716 SharedHandle(const SharedHandle& sh_hand) { 00717 ref_items = sh_hand.ref_items; 00718 reference(); 00719 } 00720 00721 /** 00722 * The move constructor does not throw. It has move semantics. 00723 * @param sh_hand The handle to be moved. 00724 */ 00725 SharedHandle(SharedHandle&& sh_hand) { 00726 ref_items = sh_hand.ref_items; 00727 sh_hand.ref_items.ref_count_p = 0; 00728 sh_hand.ref_items.obj = 0; 00729 } 00730 00731 /** 00732 * This method (and so copy or move assignment) does not throw unless 00733 * the destructor of a managed object throws. 00734 * @param sh_hand the assignor. 00735 * @return The SharedHandle object after assignment. 00736 */ 00737 // having a value type as the argument, rather than reference to const 00738 // and then initialising a tmp object, gives the compiler more scope 00739 // for optimisation, and also caters for r-values without a separate 00740 // overload 00741 SharedHandle& operator=(SharedHandle sh_hand) { 00742 std::swap(ref_items, sh_hand.ref_items); 00743 return *this; 00744 } 00745 00746 /** 00747 * This method does not throw. 00748 * @return A pointer to the handled object (or NULL if none is 00749 * handled). 00750 */ 00751 T get() const {return ref_items.obj;} 00752 00753 /** 00754 * This method does not throw. 00755 * @return A pointer to the handled object (or NULL if none is 00756 * handled). 00757 */ 00758 operator T() const {return ref_items.obj;} 00759 00760 /** 00761 * This method does not throw. 00762 * @return The number of SharedHandle objects referencing the managed 00763 * object (or 0 if none is managed by this SharedHandle). 00764 */ 00765 unsigned int get_refcount() const {return (ref_items.ref_count_p) ? *ref_items.ref_count_p : 0;} 00766 00767 /** 00768 * The destructor does not throw unless the destructor of a handled 00769 * object throws - that should never happen. 00770 */ 00771 ~SharedHandle() {unreference();} 00772 }; 00773 00774 /** 00775 * @class ScopedHandle shared_handle.h c++-gtk-utils/shared_handle.h 00776 * @brief This is a generic scoped class for managing the lifetime of objects 00777 * allocated on freestore. 00778 * @ingroup handles 00779 * @sa SharedHandle SharedLockHandle SharedHandleError 00780 * @sa StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy 00781 * 00782 * This class deletes its object as soon as it goes out of scope. It 00783 * can be viewed as a SharedHandle which cannot be assigned to or used 00784 * as the argument to a copy constructor and therefore which cannot 00785 * have a reference count of more than 1. 00786 * 00787 * ScopedHandle objects can be instantiated for pointers to constant 00788 * objects (such as ScopedHandle<const char*>), provided the deleter 00789 * functor will take such pointers. 00790 * 00791 * This library provides StandardArrayDelete, CFree, GFree, 00792 * GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter 00793 * functors, which can be used as the second template parameter of the 00794 * ScopedHandle class. StandardArrayDelete is the default, and some 00795 * typedef'ed instances of ScopedHandle for gchar (with the GFree 00796 * deleter) and for GError (with the GerrorFree deleter) are provided: 00797 * @ref GcharScopedHandleAnchor "GcharScopedHandle" and @ref 00798 * GerrorScopedHandleAnchor "GerrorScopedHandle") 00799 * 00800 * @b Comparison @b with @b std::unique_ptr 00801 * 00802 * Although the semantics of std::unique_ptr in C++11 are not 00803 * particularly suited to managing C objects with accessor functions 00804 * (such as in glib), most of the things that can be done by this 00805 * class can be done by using std::unique_ptr with a specialised 00806 * deleter. However, this class is retained in the c++-gtk-utils 00807 * library not only to retain compatibility with series 1.2 of the 00808 * library, but also to cater for some cases not so easily met by 00809 * std::unique_ptr: 00810 * 00811 * (i) The Cgu::ScopedHandle class takes its deleter as a template 00812 * parameter, which means that typedefs can be used to enable handles 00813 * for particular deleters to be easily created (and as mentioned, 00814 * this library provides a number of pre-formed deleter functors and 00815 * typedefs for them). With std::unique_ptr, custom deleters must be 00816 * passed to the unique_ptr constructor on every occasion a unique_ptr 00817 * is constructed to manage a new object (and they cannot be templated 00818 * as a typedef). 00819 * 00820 * (ii) This class provides non-move enforcement without making a 00821 * const instance of it. A const std::unique_ptr cannot be moved from 00822 * or to, but then it cannot have release() or reset() called for it 00823 * either. 00824 */ 00825 00826 template <class T, class Dealloc = StandardArrayDelete<T>> class ScopedHandle { 00827 Dealloc deleter; 00828 T obj; 00829 public: 00830 /** 00831 * This class cannot be copied. The copy constructor is deleted. 00832 */ 00833 ScopedHandle(const ScopedHandle&) = delete; 00834 00835 /** 00836 * This class cannot be copied. The assignment operator is deleted. 00837 */ 00838 ScopedHandle& operator=(const ScopedHandle&) = delete; 00839 00840 /** 00841 * The constructor does not throw. 00842 * @param ptr The object which the ScopedHandle is to manage (if 00843 * any). 00844 * 00845 * ScopedHandle objects can be instantiated for pointers to constant 00846 * objects (such as SharedHandle<const char*>), provided the deleter 00847 * functor will take such pointers. 00848 */ 00849 explicit ScopedHandle(T ptr = 0): obj(ptr) {} 00850 00851 /** 00852 * Causes the ScopedHandle to delete its managed object (if any), and 00853 * if the argument passed is not NULL, the ScopedHandle object will 00854 * manage the new object passed (which must not be managed by any 00855 * other ScopedHandle object). This method does not throw (assuming 00856 * the destructor of a managed object does not throw). 00857 * @param ptr NULL (the default), or a new unmanaged object to manage. 00858 */ 00859 void reset(T ptr = 0) { 00860 std::swap(obj, ptr); 00861 if (ptr) deleter(ptr); // ptr now points to the original managed object 00862 } 00863 00864 /** 00865 * Causes the ScopedHandle to cease to manage the handled object, but 00866 * does not delete that object. This method does not throw. 00867 * @return A pointer to the previously handled object (or NULL if none 00868 * was handled). 00869 */ 00870 T release() {T tmp = obj; obj = 0; return tmp;} 00871 00872 /** 00873 * This method does not throw. 00874 * @return A pointer to the handled object (or NULL if none is 00875 * handled). 00876 */ 00877 T get() const {return obj;} 00878 00879 /** 00880 * This method does not throw. 00881 * @return A pointer to the handled object (or NULL if none is 00882 * handled). 00883 */ 00884 operator T() const {return obj;} 00885 00886 /** 00887 * The destructor does not throw unless the destructor of a handled 00888 * object throws - that should never happen. 00889 */ 00890 ~ScopedHandle() {if (obj) deleter(obj);} 00891 }; 00892 00893 00894 /** 00895 * @class SharedLockHandle shared_handle.h c++-gtk-utils/shared_handle.h 00896 * @brief This is a generic class for managing the lifetime of objects 00897 * allocated on freestore, with a thread safe reference count.. 00898 * @ingroup handles 00899 * @sa SharedHandle ScopedHandle SharedHandleError 00900 * @sa StandardArrayDelete CFree GFree GerrorFree GSliceFree GSliceFreeSize GSliceDestroy 00901 * 00902 * Class SharedLockHandle is a version of the SharedHandle class which 00903 * includes locking so that it can be accessed in multiple threads 00904 * (although the word Lock is in the title, by default it uses glib 00905 * atomic functions to access the reference count rather than a mutex, 00906 * so the overhead should be very small). Note that only the 00907 * reference count is protected, so this is thread safe in the sense 00908 * in which a raw pointer is thread safe. A shared handle accessed in 00909 * one thread referencing a particular object is thread safe as 00910 * against another shared handle accessing the same object in a 00911 * different thread. It is thus suitable for use in different Std C++ 00912 * containers which exist in different threads but which contain 00913 * shared objects by reference. But: 00914 * 00915 * 1. If the referenced object is to be modified in one thread and 00916 * read or modified in another thread an appropriate mutex for the 00917 * referenced object is required (unless that referenced object 00918 * does its own locking). 00919 * 00920 * 2. If the same instance of shared handle is to be modified in one 00921 * thread (by assigning to the handle so that it references a 00922 * different object, or by moving from it), and copied (assigned 00923 * from or used as the argument of a copy constructor), accessed, 00924 * destroyed or modified in another thread, a mutex for that 00925 * instance of shared handle is required. 00926 * 00927 * 3. Objects referenced by shared handles which are objects for 00928 * which POSIX provides no guarantees (in the main, those which 00929 * are not built-in types), such as strings and similar 00930 * containers, may not support concurrent reads in different 00931 * threads. That depends on the library implementation concerned. 00932 * If that is the case, a mutex for the referenced object will 00933 * also be required when reading any given instance of such an 00934 * object in more than one thread by dereferencing any shared 00935 * handles referencing it (and indeed, when not using shared 00936 * handles at all). 00937 * 00938 * SharedLockHandle objects can be instantiated for pointers to 00939 * constant objects (such as SharedLockHandle<const char*>), provided 00940 * the deleter functor will take such pointers. 00941 * 00942 * This library provides StandardArrayDelete, CFree, GFree, 00943 * GerrorFree, GSliceFree, GSliceFreeSize and GSliceDestroy deleter 00944 * functors, which can be used as the second template parameter of the 00945 * SharedLockHandle class. StandardArrayDelete is the default. 00946 * 00947 * As mentioned, by default glib atomic functions are used to provide 00948 * thread-safe manipulation of the reference count. However, a 00949 * library user can define the symbol CGU_SHARED_LOCK_HANDLE_USE_MUTEX 00950 * before shared_handle.h is parsed so as to use mutexes instead, 00951 * which might be useful for some debugging purposes. 00952 * 00953 * @b Comparison @b with @b std::shared_ptr 00954 * 00955 * Although the semantics of std::shared_ptr in C++11 are not 00956 * particularly suited to managing either arrays or C objects with 00957 * accessor functions (such as in glib), most of the things that can 00958 * be done by this class can be done by using std::shared_ptr with a 00959 * specialised deleter. However, this class is retained in the 00960 * c++-gtk-utils library not only to retain compatibility with series 00961 * 1.2 of the library, but also to cater for some cases not met (or 00962 * not so easily met) by std::shared_ptr: 00963 * 00964 * (i) The Cgu::SharedLockHandle class takes its deleter as a template 00965 * parameter, which means that typedefs can be used to enable handles 00966 * for particular deleters to be easily created (and as mentioned, 00967 * this library provides a number of pre-formed deleter functors and 00968 * typedefs for them). With std::shared_ptr, custom deleters must be 00969 * passed to the shared_ptr constructor on every occasion a shared_ptr 00970 * is constructed to manage a new object (and they cannot be templated 00971 * as a typedef). 00972 * 00973 * (ii) Glib memory slices provide an efficient small object allocator 00974 * (they are likely to be significantly more efficient than global 00975 * operator new()/new[](), which generally hand off to malloc(), and 00976 * whilst malloc() is good for large block allocations it is generally 00977 * poor as a small object allocator). Internal Cgu::SharedLockHandle 00978 * allocation using glib memory slices can be achieved by compiling 00979 * the library with the --with-glib-memory-slices-no-compat 00980 * configuration option. 00981 * 00982 * (iii) If glib memory slices are not used (which do not throw), 00983 * constructing a shared pointer for a new managed object (or calling 00984 * reset() for a new managed object) might throw if internal 00985 * allocation fails. Although by default the Cgu::SharedLockHandle 00986 * implementation will delete the new managed object in such a case, 00987 * it also provides an alternative constructor and reset() method 00988 * which instead enable the new object to be accessed via the thrown 00989 * exception object so that user code can decide what to do; 00990 * std::shared_ptr deletes the new object in every case. 00991 * 00992 * (iv) A user can explicitly state whether the shared handle object 00993 * is to have atomic increment and decrement-and-test with respect to 00994 * the reference count so that the reference count is thread safe 00995 * ('no' in the case of Cgu::SharedHandle, and 'yes' in the case of 00996 * Cgu::SharedLockHandle). Using atomic functions is unnecessary if 00997 * the managed object concerned is only addressed in one thread (and 00998 * might cause unwanted cache flushing in certain circumstances). 00999 * std::shared_ptr will generally always use atomic functions with 01000 * respect to its reference count in a multi-threaded program. 01001 * 01002 * In favour of std::shared_ptr, it has an associated std::weak_ptr 01003 * class, which Cgu::SharedLockHandle does not (there is a 01004 * Cgu::GobjWeakHandle class, but that is cognate with Cgu::GobjHandle 01005 * and is only usable with GObjects). In addition shared_ptr objects 01006 * have some atomic store, load and exchange functions provided for 01007 * them which enable concurrent modifications of the same instance of 01008 * shared_ptr in different threads to have defined results. 01009 */ 01010 01011 template <class T, class Dealloc = StandardArrayDelete<T>> class SharedLockHandle { 01012 01013 Dealloc deleter; 01014 01015 #ifndef DOXYGEN_PARSING 01016 struct RefItems { 01017 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01018 Thread::Mutex* mutex_p; 01019 unsigned int* ref_count_p; 01020 #else 01021 gint* ref_count_p; 01022 #endif 01023 T obj; 01024 } ref_items; 01025 #endif 01026 01027 // SharedLockHandle<T, Dealloc>::unreference() does not throw exceptions 01028 // because Thread::Mutex::~Mutex(), Thread::Mutex::lock() and Thread::Mutex::unlock() 01029 // do not throw 01030 void unreference() { 01031 // we can (and should) check whether ref_items.ref_count_p is NULL without 01032 // a lock, because that member is specific to this SharedLockHandle object. 01033 // Only the integer pointed to by it is shared amongst SharedLockHandle 01034 // objects and requires locking 01035 if (!ref_items.ref_count_p) return; 01036 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01037 ref_items.mutex_p->lock(); 01038 --(*ref_items.ref_count_p); 01039 if (*ref_items.ref_count_p == 0) { 01040 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 01041 g_slice_free(unsigned int, ref_items.ref_count_p); 01042 # else 01043 delete ref_items.ref_count_p; 01044 # endif 01045 ref_items.mutex_p->unlock(); 01046 delete ref_items.mutex_p; 01047 deleter(ref_items.obj); 01048 } 01049 else ref_items.mutex_p->unlock(); 01050 #else 01051 if (g_atomic_int_dec_and_test(ref_items.ref_count_p)) { 01052 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 01053 g_slice_free(gint, ref_items.ref_count_p); 01054 # else 01055 delete ref_items.ref_count_p; 01056 # endif 01057 deleter(ref_items.obj); 01058 } 01059 #endif 01060 } 01061 01062 // SharedLockHandle<T, Dealloc>::reference() does not throw exceptions because 01063 // Thread::Mutex::Lock::Lock() and Thread::Mutex::Lock::~Lock() do not throw 01064 void reference() { 01065 // we can (and should) check whether ref_items.ref_count_p is NULL without 01066 // a lock, because that member is specific to this SharedLockHandle object. 01067 // Only the integer pointed to by it is shared amongst SharedLockHandle 01068 // objects and requires locking 01069 if (!ref_items.ref_count_p) return; 01070 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01071 Thread::Mutex::Lock lock(*ref_items.mutex_p); 01072 ++(*ref_items.ref_count_p); 01073 #else 01074 g_atomic_int_inc(ref_items.ref_count_p); 01075 #endif 01076 } 01077 01078 public: 01079 /** 01080 * Constructor taking an unmanaged object. 01081 * @param ptr The object which the SharedLockHandle is to manage (if 01082 * any). 01083 * @exception std::bad_alloc This constructor will not throw if the 01084 * 'ptr' argument has a NULL value (the default), otherwise it might 01085 * throw std::bad_alloc if memory is exhausted and the system throws 01086 * in that case. If such an exception is thrown, this constructor is 01087 * exception safe (it does not leak resources), but as well as 01088 * cleaning itself up this constructor will also delete the managed 01089 * object passed to it to avoid a memory leak. If such automatic 01090 * deletion is not wanted in that case, use the version of this 01091 * constructor taking a Cgu::SharedHandleAllocFail::Leave tag 01092 * argument. 01093 * @note 1. std::bad_alloc will not be thrown if the library has been 01094 * installed using the --with-glib-memory-slices-no-compat 01095 * configuration option: instead glib will terminate the program if it 01096 * is unable to obtain memory from the operating system. 01097 * @note 2. By default, glib atomic functions are used to provide 01098 * thread-safe manipulation of the reference count. However, a 01099 * library user can define the symbol CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01100 * before shared_handle.h is parsed so as to use mutexes instead, 01101 * which might be useful for some debugging purposes. Were she to do 01102 * so, Cgu::Thread::MutexError might be thrown by this constructor if 01103 * initialization of the mutex fails, but it is usually not worth 01104 * checking for this. 01105 */ 01106 explicit SharedLockHandle(T ptr = 0) { 01107 01108 if ((ref_items.obj = ptr)) { // not NULL 01109 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01110 try { 01111 ref_items.mutex_p = new Thread::Mutex; 01112 } 01113 catch (...) { 01114 deleter(ptr); // if allocating the object referenced by ref_items.mutex_p 01115 // has failed then delete the object to be referenced to 01116 // avoid a memory leak 01117 throw; 01118 } 01119 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 01120 ref_items.ref_count_p = g_slice_new(unsigned int); 01121 *ref_items.ref_count_p = 1; 01122 # else 01123 try { 01124 ref_items.ref_count_p = new unsigned int(1); 01125 } 01126 catch (...) { 01127 delete ref_items.mutex_p; 01128 deleter(ptr); 01129 throw; 01130 } 01131 # endif 01132 #else 01133 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 01134 ref_items.ref_count_p = g_slice_new(gint); 01135 *ref_items.ref_count_p = 1; 01136 # else 01137 try { 01138 ref_items.ref_count_p = new gint(1); 01139 } 01140 catch (...) { 01141 deleter(ptr); // if allocating the int referenced by ref_items.ref_count_p 01142 // has failed then delete the object to be referenced to 01143 // avoid a memory leak 01144 throw; 01145 } 01146 # endif 01147 #endif 01148 } 01149 else { 01150 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01151 ref_items.mutex_p = 0; // make sure the value is valid as we may assign it 01152 #endif 01153 ref_items.ref_count_p = 0; 01154 } 01155 } 01156 01157 /** 01158 * Constructor taking an unmanaged object. 01159 * @param ptr The object which the SharedLockHandle is to manage. 01160 * @param tag Passing the tag emumerator 01161 * Cgu::SharedHandleAllocFail::leave causes this constructor not to 01162 * delete the new managed object passed as the 'ptr' argument in the 01163 * event of internal allocation in this method failing because of 01164 * memory exhaustion (in that event, Cgu::SharedHandleError will be 01165 * thrown). 01166 * @exception Cgu::SharedHandleError This constructor might throw 01167 * Cgu::SharedHandleError if memory is exhausted and the system would 01168 * otherwise throw std::bad_alloc in that case. This constructor is 01169 * exception safe (it does not leak resources), and if such an 01170 * exception is thrown it will clean itself up, but it will not 01171 * attempt to delete the new managed object passed to it. Access to 01172 * the object passed to the 'ptr' argument can be obtained via the 01173 * thrown Cgu::SharedHandleError object. 01174 * @note 1. On systems with over-commit/lazy-commit combined with 01175 * virtual memory (swap), it is rarely useful to check for memory 01176 * exhaustion, so in those cases this version of the constructor will 01177 * not be useful. 01178 * @note 2. If the library has been installed using the 01179 * --with-glib-memory-slices-no-compat configuration option this 01180 * version of the constructor will also not be useful: instead glib 01181 * will terminate the program if it is unable to obtain memory from 01182 * the operating system. 01183 * @note 3. By default, glib atomic functions are used to provide 01184 * thread-safe manipulation of the reference count. However, a 01185 * library user can define the symbol CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01186 * before shared_handle.h is parsed so as to use mutexes instead, 01187 * which might be useful for some debugging purposes. Were she to do 01188 * so, Cgu::SharedHandleError might be thrown by this constructor if 01189 * initialization of the mutex fails (even if the 01190 * --with-glib-memory-slices-no-compat configuration option is 01191 * chosen), but it is usually not worth checking for such mutex 01192 * initialization failure. 01193 */ 01194 SharedLockHandle(T ptr, Cgu::SharedHandleAllocFail::Leave tag) { 01195 01196 if ((ref_items.obj = ptr)) { // not NULL 01197 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01198 try { 01199 ref_items.mutex_p = new Thread::Mutex; 01200 } 01201 catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly 01202 throw SharedHandleError<T>(ptr); 01203 } 01204 catch (Thread::MutexError&) { // as we are not rethrowing, make NPTL friendly 01205 throw SharedHandleError<T>(ptr); 01206 } 01207 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 01208 ref_items.ref_count_p = g_slice_new(unsigned int); 01209 *ref_items.ref_count_p = 1; 01210 # else 01211 try { 01212 ref_items.ref_count_p = new unsigned int(1); 01213 } 01214 catch (std::bad_alloc&) { 01215 delete ref_items.mutex_p; 01216 throw SharedHandleError<T>(ptr); 01217 } 01218 # endif 01219 #else 01220 # ifdef CGU_USE_GLIB_MEMORY_SLICES_NO_COMPAT 01221 ref_items.ref_count_p = g_slice_new(gint); 01222 *ref_items.ref_count_p = 1; 01223 # else 01224 try { 01225 ref_items.ref_count_p = new gint(1); 01226 } 01227 catch (std::bad_alloc&) { // as we are not rethrowing, make NPTL friendly 01228 throw SharedHandleError<T>(ptr); 01229 } 01230 # endif 01231 #endif 01232 } 01233 else { 01234 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01235 ref_items.mutex_p = 0; // make sure the value is valid as we may assign it 01236 #endif 01237 ref_items.ref_count_p = 0; 01238 } 01239 } 01240 01241 /** 01242 * Causes the SharedLockHandle to cease to manage its managed object 01243 * (if any), deleting it if this is the last ShareLockHandle object 01244 * managing it. If the argument passed is not NULL, the 01245 * SharedLockHandle object will manage the new object passed (which 01246 * must not be managed by any other SharedLockHandle object). 01247 * @param ptr NULL (the default), or a new unmanaged object to manage. 01248 * @exception std::bad_alloc This method will not throw if the 'ptr' 01249 * argument has a NULL value (the default) and the destructor of a 01250 * managed object does not throw, otherwise it might throw 01251 * std::bad_alloc if memory is exhausted and the system throws in that 01252 * case. Note that if such an exception is thrown then this method 01253 * will do nothing (it is strongly exception safe and will continue to 01254 * manage the object it was managing prior to the call), except that 01255 * it will delete the new managed object passed to it to avoid a 01256 * memory leak. If such automatic deletion in the event of such an 01257 * exception is not wanted, use the reset() method taking a 01258 * Cgu::SharedHandleAllocFail::Leave tag type as its second argument. 01259 * @note 1. std::bad_alloc will not be thrown if the library has been 01260 * installed using the --with-glib-memory-slices-no-compat 01261 * configuration option: instead glib will terminate the program if it 01262 * is unable to obtain memory from the operating system. 01263 * @note 2. By default, glib atomic functions are used to provide 01264 * thread-safe manipulation of the reference count. However, a 01265 * library user can define the symbol CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01266 * before shared_handle.h is parsed so as to use mutexes instead, 01267 * which might be useful for some debugging purposes. Were she to do 01268 * so, Cgu::Thread::MutexError might be thrown by this method if 01269 * initialization of the mutex fails, but it is usually not worth 01270 * checking for this. 01271 * @note 3. A SharedLockHandle object protects its reference count but 01272 * not the managed object or its other internals. The reset() method 01273 * should not be called by one thread in respect of a particular 01274 * SharedLockHandle object while another thread may be operating on, 01275 * copying or dereferencing the same instance of SharedLockHandle. It 01276 * is thread-safe as against another instance of SharedLockHandle 01277 * managing the same object. 01278 */ 01279 void reset(T ptr = 0) { 01280 SharedLockHandle tmp(ptr); 01281 std::swap(ref_items, tmp.ref_items); 01282 } 01283 01284 /** 01285 * Causes the SharedLockHandle to cease to manage its managed object 01286 * (if any), deleting it if this is the last ShareLockHandle object 01287 * managing it. The SharedLockHandle object will manage the new 01288 * object passed (which must not be managed by any other 01289 * SharedLockHandle object). This method is exception safe, but see 01290 * the comments below on Cgu::SharedHandleError. 01291 * @param ptr A new unmanaged object to manage (if no new object is to 01292 * be managed, use the version of reset() taking a default value of 01293 * NULL). 01294 * @param tag Passing the tag emumerator 01295 * Cgu::SharedHandleAllocFail::leave causes this method not to delete 01296 * the new managed object passed as the 'ptr' argument in the event of 01297 * internal allocation in this method failing because of memory 01298 * exhaustion (in that event, Cgu::SharedHandleError will be thrown). 01299 * @exception Cgu::SharedHandleError This method might throw 01300 * Cgu::SharedHandleError if memory is exhausted and the system would 01301 * otherwise throw std::bad_alloc in that case. Note that if such an 01302 * exception is thrown then this method will do nothing (it is 01303 * strongly exception safe and will continue to manage the object it 01304 * was managing prior to the call), and it will not attempt to delete 01305 * the new managed object passed to it (if any). Access to the object 01306 * passed to the 'ptr' argument can be obtained via the thrown 01307 * Cgu::SharedHandleError object. 01308 * @note 1. A SharedLockHandle object protects its reference count but 01309 * not the managed object or its other internals. The reset() method 01310 * should not be called by one thread in respect of a particular 01311 * SharedLockHandle object while another thread may be operating on, 01312 * copying or dereferencing the same instance of SharedLockHandle. It 01313 * is thread-safe as against another instance of SharedLockHandle 01314 * managing the same object. 01315 * @note 2. On systems with over-commit/lazy-commit combined with 01316 * virtual memory (swap), it is rarely useful to check for memory 01317 * exhaustion, so in those cases this version of the reset() method 01318 * will not be useful. 01319 * @note 3. If the library has been installed using the 01320 * --with-glib-memory-slices-no-compat configuration option this 01321 * version of the reset() method will also not be useful: instead glib 01322 * will terminate the program if it is unable to obtain memory from 01323 * the operating system. 01324 * @note 4. By default, glib atomic functions are used to provide 01325 * thread-safe manipulation of the reference count. However, a 01326 * library user can define the symbol CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01327 * before shared_handle.h is parsed so as to use mutexes instead, 01328 * which might be useful for some debugging purposes. Were she to do 01329 * so, Cgu::SharedHandleError might be thrown by this method if 01330 * initialization of the mutex fails (even if the 01331 * --with-glib-memory-slices-no-compat configuration option is 01332 * chosen), but it is usually not worth checking for such mutex 01333 * initialization failure. 01334 */ 01335 void reset(T ptr, Cgu::SharedHandleAllocFail::Leave tag) { 01336 SharedLockHandle tmp(ptr, tag); 01337 std::swap(ref_items, tmp.ref_items); 01338 } 01339 01340 /** 01341 * The copy constructor does not throw. 01342 * @param sh_hand The handle to be copied. 01343 */ 01344 SharedLockHandle(const SharedLockHandle& sh_hand) { 01345 ref_items = sh_hand.ref_items; 01346 reference(); 01347 } 01348 01349 /** 01350 * The move constructor does not throw. It has move semantics. 01351 * @param sh_hand The handle to be moved. 01352 */ 01353 SharedLockHandle(SharedLockHandle&& sh_hand) { 01354 ref_items = sh_hand.ref_items; 01355 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01356 sh_hand.ref_items.mutex_p = 0; // make sure the value is valid as we may assign it 01357 #endif 01358 sh_hand.ref_items.ref_count_p = 0; 01359 sh_hand.ref_items.obj = 0; 01360 } 01361 01362 /** 01363 * This method (and so copy or move assignment) does not throw unless 01364 * the destructor of a managed object throws. 01365 * @param sh_hand the assignor. 01366 * @return The SharedLockHandle object after assignment. 01367 */ 01368 // having a value type as the argument, rather than reference to const 01369 // and then initialising a tmp object, gives the compiler more scope 01370 // for optimisation 01371 SharedLockHandle& operator=(SharedLockHandle sh_hand) { 01372 std::swap(ref_items, sh_hand.ref_items); 01373 return *this; 01374 } 01375 01376 /** 01377 * This method does not throw. 01378 * @return A pointer to the handled object (or NULL if none is 01379 * handled). 01380 */ 01381 T get() const {return ref_items.obj;} 01382 01383 /** 01384 * This method does not throw. 01385 * @return A pointer to the handled object (or NULL if none is 01386 * handled). 01387 */ 01388 operator T() const {return ref_items.obj;} 01389 01390 /** 01391 * This method does not throw. 01392 * @return The number of SharedLockHandle objects referencing the 01393 * managed object (or 0 if none is managed by this SharedLockHandle). 01394 * @note The return value may not be valid if another thread has 01395 * changed the reference count before the value returned by this 01396 * method is acted on. It is provided as a utility, but may not be 01397 * meaningful, depending on the intended usage. 01398 */ 01399 unsigned int get_refcount() const { 01400 if (!ref_items.ref_count_p) return 0; 01401 #ifdef CGU_SHARED_LOCK_HANDLE_USE_MUTEX 01402 Thread::Mutex::Lock lock(*ref_items.mutex_p); 01403 return *ref_items.ref_count_p; 01404 #else 01405 return g_atomic_int_get(ref_items.ref_count_p); 01406 #endif 01407 } 01408 01409 /** 01410 * The destructor does not throw unless the destructor of a handled 01411 * object throws - that should never happen. 01412 */ 01413 ~SharedLockHandle() {unreference();} 01414 }; 01415 01416 #if defined(CGU_USE_SMART_PTR_COMPARISON) || defined(DOXYGEN_PARSING) 01417 01418 // we can use built-in operator == when comparing pointers referencing 01419 // different objects of the same type 01420 /** 01421 * @ingroup handles 01422 * 01423 * This comparison operator does not throw. It compares the addresses 01424 * of the managed objects. 01425 * 01426 * Since 2.0.0-rc2 01427 */ 01428 template <class T, class Dealloc> 01429 bool operator==(const SharedHandle<T, Dealloc>& s1, const SharedHandle<T, Dealloc>& s2) { 01430 return (s1.get() == s2.get()); 01431 } 01432 01433 /** 01434 * @ingroup handles 01435 * 01436 * This comparison operator does not throw. It compares the addresses 01437 * of the managed objects. 01438 * 01439 * Since 2.0.0-rc2 01440 */ 01441 template <class T, class Dealloc> 01442 bool operator!=(const SharedHandle<T, Dealloc>& s1, const SharedHandle<T, Dealloc>& s2) { 01443 return !(s1 == s2); 01444 } 01445 01446 // we must use std::less rather than the < built-in operator for 01447 // pointers to objects not within the same array or object: "For 01448 // templates greater, less, greater_equal, and less_equal, the 01449 // specializations for any pointer type yield a total order, even if 01450 // the built-in operators <, >, <=, >= do not." (para 20.3.3/8). 01451 /** 01452 * @ingroup handles 01453 * 01454 * This comparison operator does not throw. It compares the addresses 01455 * of the managed objects. 01456 * 01457 * Since 2.0.0-rc2 01458 */ 01459 template <class T, class Dealloc> 01460 bool operator<(const SharedHandle<T, Dealloc>& s1, const SharedHandle<T, Dealloc>& s2) { 01461 return std::less<T>()(s1.get(), s2.get()); 01462 } 01463 01464 /** 01465 * @ingroup handles 01466 * 01467 * This comparison operator does not throw. It compares the addresses 01468 * of the managed objects. 01469 * 01470 * Since 2.0.0-rc2 01471 */ 01472 template <class T, class Dealloc> 01473 bool operator==(const SharedLockHandle<T, Dealloc>& s1, const SharedLockHandle<T, Dealloc>& s2) { 01474 return (s1.get() == s2.get()); 01475 } 01476 01477 /** 01478 * @ingroup handles 01479 * 01480 * This comparison operator does not throw. It compares the addresses 01481 * of the managed objects. 01482 * 01483 * Since 2.0.0-rc2 01484 */ 01485 template <class T, class Dealloc> 01486 bool operator!=(const SharedLockHandle<T, Dealloc>& s1, const SharedLockHandle<T, Dealloc>& s2) { 01487 return !(s1 == s2); 01488 } 01489 01490 /** 01491 * @ingroup handles 01492 * 01493 * This comparison operator does not throw. It compares the addresses 01494 * of the managed objects. 01495 * 01496 * Since 2.0.0-rc2 01497 */ 01498 template <class T, class Dealloc> 01499 bool operator<(const SharedLockHandle<T, Dealloc>& s1, const SharedLockHandle<T, Dealloc>& s2) { 01500 return std::less<T>()(s1.get(), s2.get()); 01501 } 01502 01503 #endif // CGU_USE_SMART_PTR_COMPARISON 01504 01505 } // namespace Cgu 01506 01507 // doxygen produces long filenames that tar can't handle: 01508 // we have generic documentation for std::hash specialisations 01509 // in doxygen.main.in 01510 #if defined(CGU_USE_SMART_PTR_COMPARISON) && !defined(DOXYGEN_PARSING) 01511 /* These structs allow SharedHandle and SharedLockHandle objects to be 01512 keys in unordered associative containers */ 01513 namespace std { 01514 template <class T, class Dealloc> 01515 struct hash<Cgu::SharedHandle<T, Dealloc>> { 01516 typedef std::size_t result_type; 01517 typedef Cgu::SharedHandle<T, Dealloc> argument_type; 01518 result_type operator()(const argument_type& s) const { 01519 // this is fine: std::hash structs do not normally contain data and 01520 // std::hash<T*> certainly won't, so we don't have overhead constructing 01521 // std::hash<T*> on the fly 01522 return std::hash<T>()(s.get()); 01523 } 01524 }; 01525 template <class T, class Dealloc> 01526 struct hash<Cgu::SharedLockHandle<T, Dealloc>> { 01527 typedef std::size_t result_type; 01528 typedef Cgu::SharedLockHandle<T, Dealloc> argument_type; 01529 result_type operator()(const argument_type& s) const { 01530 // this is fine: std::hash structs do not normally contain data and 01531 // std::hash<T*> certainly won't, so we don't have overhead constructing 01532 // std::hash<T*> on the fly 01533 return std::hash<T>()(s.get()); 01534 } 01535 }; 01536 } // namespace std 01537 #endif // CGU_USE_SMART_PTR_COMPARISON 01538 01539 #endif