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