libcwd: The C++ Debugging Support Library - Reference Manual
Author: Carlo Wood
Description: C++ debugging support library with ostream debug output
Keywords: libcwd, debug output, debug facility, debug channel, object orientation, OO, C++, nested debug output, ostream, memory allocation, debugging, source file and line number

Main Page Reference Manual Namespace List Compound List Namespace Members Compound Members File Members

# libcwd/private_allocator.h

Go to the documentation of this file.

    00001 // $Header$
    00002 //
    00003 // Copyright (C) 2001 - 2004, by
    00004 //
    00005 // Carlo Wood, Run on IRC <carlo@alinoe.com>
    00006 // RSA-1024 0x624ACAD5 1997-01-26                    Sign & Encrypt
    00007 // Fingerprint16 = 32 EC A7 B6 AC DB 65 A6  F6 F6 55 DD 1C DC FF 61
    00008 //
    00009 // This file may be distributed under the terms of the Q Public License
    00010 // version 1.0 as appearing in the file LICENSE.QPL included in the
    00011 // packaging of this file.
    00012 //
    00013
    00018 #ifndef LIBCWD_PRIVATE_ALLOCATOR_H
    00019 #define LIBCWD_PRIVATE_ALLOCATOR_H
    00020
    00021 #ifndef LIBCWD_CONFIG_H
    00022 #include <libcwd/config.h>
    00023 #endif
    00024
    00025 #if CWDEBUG_ALLOC               // This file is not used when --disable-alloc was used.
    00026
    00027 #ifndef LIBCWD_PRIVATE_MUTEX_INSTANCES_H
    00028 #include <libcwd/private_mutex_instances.h>
    00029 #endif
    00030 #ifndef LIBCWD_CORE_DUMP_H
    00031 #include <libcwd/core_dump.h>
    00032 #endif
    00033 #ifndef LIBCW_CSTDDEF
    00034 #define LIBCW_CSTDDEF
    00035 #include <cstddef>                      // Needed for size_t
    00036 #endif
    00037 #if __GNUC__ > 3 && LIBCWD_THREAD_SAFE
    00038 #include <libcwd/private_mutex.h>       // mutex_ct
    00039 #endif
    00040 #include <memory>
    00041 #include <limits>
    00042
    00043 //===================================================================================================
    00044 // Allocators
    00045 //
    00046 //
    00047
    00048 /* The allocators used by libcwd have the following characteristics:
    00049
    00050    1) The type T that is being allocated and deallocated.
    00051    2) Whether or not the allocation is internal, auto-internal or in userspace.
    00052    3) The pool instance from which the allocation should be drawn.
    00053    4) Whether or not a lock is needed for this pool.
    00054    5) Whether or not this allocation belongs to a libcwd
    00055       critical area and if so, which one.
    00056
    00057    Note that each critical area (if any) uses its own lock and
    00058    therefore no (additional) lock will be needed for the allocator.
    00059    Otherwise a lock is always needed (in the multi-threaded case).
    00060    As of gcc 4.0, the used pool allocator doesn't use locks anymore
    00061    but separates the pools per thread (except for one common pool),
    00062    this need is equivalent for us to needing a lock or not: if we
    00063    don't need a lock then there is also no need to separate per thread.
    00064
    00065    There are five different allocators in use by libcwd:
    00066
    00067 Multi-threaded case:
    00068
    00069    Allocator name               | internal | Pool instance                      | Needs lock
    00070    ----------------------------------------------------------------------------------------------------
    00071    memblk_map_allocator         | yes      | memblk_map_instance                | no (memblk_map_instance critical area)
    00072    object_files_allocator       | yes      | object_files_instance              | no (object_files_instance critical area)
    00073    internal_allocator           | yes      | multi_threaded_internal_instance   | yes
    00074    auto_internal_allocator      | auto     | multi_threaded_internal_instance   | yes
    00075    userspace_allocator          | no       | userspace_instance                 | yes
    00076
    00077 Single-threaded case:
    00078
    00079    Allocator name               | internal | Pool instance                      | Needs lock
    00080    ----------------------------------------------------------------------------------------------------
    00081    memblk_map_allocator         | yes      | single_threaded_internal_instance  | no
    00082    object_files_allocator       | yes      | single_threaded_internal_instance  | no
    00083    internal_allocator           | yes      | single_threaded_internal_instance  | no
    00084    auto_internal_allocator      | auto     | single_threaded_internal_instance  | no
    00085    userspace_allocator          | no       | std::alloc                         | -
    00086
    00087 */
    00088
    00089 #if __GNUC__ == 3 && __GNUC_MINOR__ == 4
    00090 #include <ext/pool_allocator.h>         // __gnu_cxx::__pool_alloc
    00091 #endif
    00092
    00093 namespace libcwd {
    00094   namespace _private_ {
    00095
    00096 // This is a random number in the hope nobody else uses it.
    00097 int const random_salt = 327665;
    00098
    00099 // Dummy mutex instance numbers, these must be negative.
    00100 int const multi_threaded_internal_instance = -1;
    00101 int const single_threaded_internal_instance = -2;
    00102 int const userspace_instance = -3;
    00103
    00104 // Definition of CharPoolAlloc.
    00105 #if __GNUC__ == 3 && __GNUC_MINOR__ < 4
    00106 template<bool needs_lock, int pool_instance>
    00107   struct CharPoolAlloc : public std::__default_alloc_template<needs_lock, random_salt + pool_instance> {
    00108     typedef char* pointer;
    00109   };
    00110 #elif __GNUC__ == 3 && __GNUC_MINOR__ == 4 && __GNUC_PATCHLEVEL__ == 0
    00111 template<bool needs_lock, int pool_instance>
    00112   struct CharPoolAlloc : public __gnu_cxx::__pool_alloc<needs_lock, random_salt + pool_instance> {
    00113     typedef char* pointer;
    00114   };
    00115 #elif __GNUC__ == 3
    00116 // gcc 3.4.1 and higher.
    00117 template<int pool_instance>
    00118   struct char_wrapper {
    00119     char c;
    00120   };
    00121 // gcc 3.4.1 and 3.4.2 always use a lock, in the threaded case.
    00122 template<bool needs_lock, int pool_instance>
    00123   class CharPoolAlloc : public __gnu_cxx::__pool_alloc<char_wrapper<pool_instance> > { };
    00124 #else // gcc 4.0 and higher.
    00125 // Sometimes reusing code isn't possibly anymore (die gcc developers die).
    00126
    00127 static size_t const maximum_size_exp = 10;                      // The log2 of the maximum size that is
    00128                                                                 // allocated in a pool. Larger sizes are
    00129                                                                 // allocated directly with operator new.
    00130 static size_t const maximum_size = (1U << maximum_size_exp);    // 1024 bytes.
    00131
    00132 struct Node {
    00133   Node* M_next;
    00134   Node* M_prev;
    00135
    00136   Node* next(void) const { return M_next; }
    00137   Node* prev(void) const { return M_prev; }
    00138
    00139   void unlink(void)
    00140   {
    00141     M_prev->M_next = M_next;
    00142     M_next->M_prev = M_prev;
    00143   }
    00144 };
    00145
    00146 // The log2 of minimum_size. 2^(minimum_size_exp - 1) < sizeof(Node) <= 2^minimum_size_exp.
    00147 template <unsigned int N> struct log2 { enum { result = 1 + log2<N/2>::result }; };
    00148 template<> struct log2<0> { enum { result = -1 }; };
    00149 static size_t const minimum_size_exp = log2<sizeof(Node) - 1>::result + 1;      // Calculate rounded up log2 value.
    00150
    00151 static size_t const minimum_size = (1U << minimum_size_exp);    // The minimum chunk size, must be a power of 2.
    00152 // The number of different buckets (with respective chunk sizes: 8, 16, 32, 64, 128, 256, 512 and 1024).
    00153 static int const bucket_sizes = maximum_size_exp - minimum_size_exp + 1;
    00154
    00155 struct List : public Node {
    00156   bool empty(void) const { return M_next == this; }
    00157   void insert(Node* node)
    00158   {
    00159     node->M_prev = this;
    00160     node->M_next = M_next;
    00161     M_next->M_prev = node;
    00162     M_next = node;
    00163   }
    00164   void insert_back(Node* node)
    00165   {
    00166     node->M_prev = M_prev;
    00167     node->M_next = this;
    00168     M_prev->M_next = node;
    00169     M_prev = node;
    00170   }
    00171 private:
    00172   using Node::next;
    00173   using Node::prev;
    00174 };
    00175
    00176 struct ChunkNode : public Node {
    00177   // This is commented out because it's 'virtual' (it can be zero size too).
    00178   // char M_padding[size_of(ChunkNode) - sizeof(Node)];
    00179
    00180   ChunkNode* next(void) const { return static_cast<ChunkNode*>(M_next); }
    00181   ChunkNode* prev(void) const { return static_cast<ChunkNode*>(M_prev); }
    00182 };
    00183
    00184 struct ChunkList : public List {
    00185   unsigned int M_used_count;    // Number of _used_ chunks (thus, that are allocated and not in the list anymore).
    00186   ChunkNode* begin(void) const { return static_cast<ChunkNode*>(M_next); }
    00187   Node const* end(void) const { return this; }
    00188 };
    00189
    00190 struct BlockNode : public Node {
    00191   ChunkList M_chunks;
    00192   ChunkNode M_data[1];          // One or more Chunks.
    00193
    00194   BlockNode* next(void) const { return static_cast<BlockNode*>(M_next); }
    00195   BlockNode* prev(void) const { return static_cast<BlockNode*>(M_prev); }
    00196 };
    00197
    00198 struct BlockList : public List {
    00199   unsigned int* M_count_ptr;    // Pointer to number of blocks (thus, that are in the (full+notfull) list).
    00200   unsigned short M_internal;    // Whether or not this block list contains internal blocks or not.
    00201
    00202   BlockNode* begin(void) const { return static_cast<BlockNode*>(M_next); }
    00203   Node const* end(void) const { return this; }
    00204
    00205   void initialize(unsigned int* count_ptr, unsigned short internal);
    00206   void uninitialize(void);
    00207   ~BlockList() { uninitialize(); }
    00208 #if CWDEBUG_DEBUG
    00209   void consistency_check(void);
    00210 #endif
    00211 };
    00212
    00213 struct TSD_st;
    00214
    00215 struct FreeList {
    00216 #if LIBCWD_THREAD_SAFE
    00217   pthread_mutex_t M_mutex;
    00218   static pthread_mutex_t S_mutex;
    00219 #endif
    00220   bool M_initialized;
    00221   unsigned int M_count[bucket_sizes];           // Number of blocks (in the full+notfull list).
    00222   unsigned short M_keep[bucket_sizes];          // Number of blocks that shouldn't be freed.
    00223   BlockList M_list_notfull[bucket_sizes];
    00224   BlockList M_list_full[bucket_sizes];
    00225
    00226 #if LIBCWD_THREAD_SAFE
    00227   void initialize(TSD_st& __libcwd_tsd);
    00228 #else
    00229   void initialize(void);
    00230 #endif
    00231   void uninitialize(void);
    00232   ~FreeList() { uninitialize(); }
    00233   char* allocate(int power, size_t size);
    00234   void deallocate(char* p, int power, size_t size);
    00235 #if CWDEBUG_DEBUG
    00236   void consistency_check(void);
    00237 #endif
    00238 };
    00239
    00240 template<bool needs_lock, int pool_instance>
    00241   class CharPoolAlloc {
    00242   private:
    00243     static FreeList S_freelist;
    00244
    00245   public:
    00246     // Type definitions.
    00247     typedef char        value_type;
    00248     typedef size_t      size_type;
    00249     typedef ptrdiff_t   difference_type;
    00250     typedef char*       pointer;
    00251     typedef char const* const_pointer;
    00252     typedef char&       reference;
    00253     typedef char const& const_reference;
    00254
    00255     // Allocate but don't initialize num elements of type T.
    00256 #if LIBCWD_THREAD_SAFE
    00257     pointer allocate(size_type num, TSD_st&);
    00258 #else
    00259     pointer allocate(size_type num);
    00260 #endif
    00261
    00262     // Deallocate storage p of deleted elements.
    00263 #if LIBCWD_THREAD_SAFE
    00264     void deallocate(pointer p, size_type num, TSD_st&);
    00265 #else
    00266     void deallocate(pointer p, size_type num);
    00267 #endif
    00268
    00269     template <bool needs_lock1, int pool_instance1,
    00270               bool needs_lock2, int pool_instance2>
    00271       friend inline
    00272       bool operator==(CharPoolAlloc<needs_lock1, pool_instance1> const&,
    00273                       CharPoolAlloc<needs_lock2, pool_instance2> const&);
    00274     template <bool needs_lock1, int pool_instance1,
    00275               bool needs_lock2, int pool_instance2>
    00276       friend inline
    00277       bool operator!=(CharPoolAlloc<needs_lock1, pool_instance1> const&,
    00278                       CharPoolAlloc<needs_lock2, pool_instance2> const&);
    00279
    00280     size_type max_size(void) const { return std::numeric_limits<size_type>::max(); }
    00281   };
    00282 #endif // gcc 4.0 and higher.
    00283
    00284 // Convenience macros.
    00285 #if CWDEBUG_DEBUG
    00286 #define LIBCWD_COMMA_INT_INSTANCE , int instance
    00287 #define LIBCWD_COMMA_INSTANCE , instance
    00288 #define LIBCWD_DEBUGDEBUG_COMMA(x) , x
    00289 #else
    00290 #define LIBCWD_COMMA_INT_INSTANCE
    00291 #define LIBCWD_COMMA_INSTANCE
    00292 #define LIBCWD_DEBUGDEBUG_COMMA(x)
    00293 #endif
    00294
    00295 enum pool_nt {
    00296   userspace_pool,
    00297   internal_pool,
    00298   auto_internal_pool
    00299 };
    00300
    00301 // This wrapper adds sanity checks to the allocator use (like testing if
    00302 // 'internal' allocators are indeed only used while in internal mode, and
    00303 // critical area allocators are only used when the related lock is indeed
    00304 // locked etc.
    00305 template<typename T, class CharAlloc, pool_nt internal LIBCWD_COMMA_INT_INSTANCE>
    00306     class allocator_adaptor {
    00307     private:
    00308       // The underlying allocator.
    00309       CharAlloc M_char_allocator;
    00310
    00311     public:
    00312       // Type definitions.
    00313       typedef T         value_type;
    00314       typedef size_t    size_type;
    00315       typedef ptrdiff_t difference_type;
    00316       typedef T*                pointer;
    00317       typedef T const*  const_pointer;
    00318       typedef T&                reference;
    00319       typedef T const&  const_reference;
    00320
    00321       // Rebind allocator to type U.
    00322       template <class U>
    00323         struct rebind {
    00324           typedef allocator_adaptor<U, CharAlloc, internal LIBCWD_COMMA_INSTANCE> other;
    00325         };
    00326
    00327       // Return address of values.
    00328       pointer address(reference value) const { return &value; }
    00329       const_pointer address(const_reference value) const { return &value; }
    00330
    00331       // Constructors and destructor.
    00332       allocator_adaptor(void) throw() { }
    00333       allocator_adaptor(allocator_adaptor const& a) : M_char_allocator(a.M_char_allocator) { }
    00334       template<class U>
    00335         allocator_adaptor(allocator_adaptor<U, CharAlloc, internal LIBCWD_COMMA_INSTANCE> const& a) :
    00336             M_char_allocator(a.M_char_allocator) { }
    00337       template<class T2, class CharAlloc2, pool_nt internal2 LIBCWD_DEBUGDEBUG_COMMA(int instance2)>
    00338         friend class allocator_adaptor;
    00339       ~allocator_adaptor() throw() { }
    00340
    00341       // Return maximum number of elements that can be allocated.
    00342       size_type max_size(void) const { return M_char_allocator.max_size() / sizeof(T); }
    00343
    00344       // Allocate but don't initialize num elements of type T.
    00345       pointer allocate(size_type num);
    00346       pointer allocate(size_type num, void const* hint);
    00347
    00348       // Deallocate storage p of deleted elements.
    00349       void deallocate(pointer p, size_type num);
    00350
    00351       // Initialize elements of allocated storage p with value value.
    00352       void construct(pointer p, T const& value) { new ((void*)p) T(value); }
    00353
    00354       // Destroy elements of initialized storage p.
    00355       void destroy(pointer p) { p->~T(); }
    00356
    00357 #if CWDEBUG_DEBUG || CWDEBUG_DEBUGM
    00358     private:
    00359       static void sanity_check(void);
    00360 #endif
    00361
    00362       template <class T1, class CharAlloc1, pool_nt internal1 LIBCWD_DEBUGDEBUG_COMMA(int inst1),
    00363                 class T2, class CharAlloc2, pool_nt internal2 LIBCWD_DEBUGDEBUG_COMMA(int inst2)>
    00364         friend inline
    00365         bool operator==(allocator_adaptor<T1, CharAlloc1, internal1 LIBCWD_DEBUGDEBUG_COMMA(inst1)> const& a1,
    00366                         allocator_adaptor<T2, CharAlloc2, internal2 LIBCWD_DEBUGDEBUG_COMMA(inst2)> const& a2);
    00367       template <class T1, class CharAlloc1, pool_nt internal1 LIBCWD_DEBUGDEBUG_COMMA(int inst1),
    00368                 class T2, class CharAlloc2, pool_nt internal2 LIBCWD_DEBUGDEBUG_COMMA(int inst2)>
    00369         friend inline
    00370         bool operator!=(allocator_adaptor<T1, CharAlloc1, internal1 LIBCWD_DEBUGDEBUG_COMMA(inst1)> const& a1,
    00371                         allocator_adaptor<T2, CharAlloc2, internal2 LIBCWD_DEBUGDEBUG_COMMA(inst2)> const& a2);
    00372     };
    00373
    00374 #if LIBCWD_THREAD_SAFE
    00375 // We normally would be able to use the default allocator, but... libcwd functions can
    00376 // at all times be called from malloc which might be called from std::allocator with its
    00377 // lock set.  Therefore we also use a separate allocator pool for the userspace, in the
    00378 // threaded case.
    00379 #define LIBCWD_CHARALLOCATOR_USERSPACE(instance) ::libcwd::_private_::                          \
    00380         allocator_adaptor<char,                                                                 \
    00381                           CharPoolAlloc<true, userspace_instance>,                              \
    00382                           userspace_pool                                                        \
    00383                           LIBCWD_DEBUGDEBUG_COMMA(::libcwd::_private_::instance)>
    00384 #endif
    00385
    00386 // Both, multi_threaded_internal_instance and memblk_map_instance use also locks for
    00387 // the allocator pool itself because they (the memory pools) are being shared between
    00388 // threads from within critical areas with different mutexes.
    00389 // Other instances (> 0) are supposed to only use the allocator instance from within
    00390 // the critical area of the corresponding mutex_tct<instance>, and thus only by one
    00391 // thread at a time.
    00392 #if LIBCWD_THREAD_SAFE
    00393 #define LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance)                                              \
    00394                                 ::libcwd::_private_::instance ==                                \
    00395                                 ::libcwd::_private_::multi_threaded_internal_instance ||        \
    00396                                 ::libcwd::_private_::instance ==                                \
    00397                                 ::libcwd::_private_::memblk_map_instance
    00398 #else // !LIBCWD_THREAD_SAFE
    00399 #define LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance) false
    00400 #endif // !LIBCWD_THREAD_SAFE
    00401
    00402 #define LIBCWD_CHARALLOCATOR_INTERNAL(instance) ::libcwd::_private_::                   \
    00403         allocator_adaptor<char,                                                                 \
    00404                           CharPoolAlloc<LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance),             \
    00405                                         ::libcwd::_private_::instance >,                        \
    00406                           internal_pool                                                         \
    00407                           LIBCWD_DEBUGDEBUG_COMMA(::libcwd::_private_::instance)>
    00408
    00409 #define LIBCWD_CHARALLOCATOR_AUTO_INTERNAL(instance) ::libcwd::_private_::              \
    00410         allocator_adaptor<char,                                                                 \
    00411                           CharPoolAlloc<LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance),             \
    00412                                         ::libcwd::_private_::instance >,                        \
    00413                           auto_internal_pool                                                    \
    00414                           LIBCWD_DEBUGDEBUG_COMMA(::libcwd::_private_::instance)>
    00415
    00416 #if LIBCWD_THREAD_SAFE
    00417 // Our allocator adaptor for the Non-Shared internal cases: Single Threaded
    00418 // (inst = single_threaded_internal_instance) or inside the critical area of the corresponding
    00419 // libcwd mutex instance.
    00420 #define LIBCWD_NS_INTERNAL_ALLOCATOR(instance)  LIBCWD_CHARALLOCATOR_INTERNAL(instance)
    00421 #else // !LIBCWD_THREAD_SAFE
    00422 // In a single threaded application, the Non-Shared case is equivalent to the Single Threaded case.
    00423 #define LIBCWD_NS_INTERNAL_ALLOCATOR(instance)  LIBCWD_CHARALLOCATOR_INTERNAL(single_threaded_internal_instance)
    00424 #endif // !LIBCWD_THREAD_SAFE
    00425
    00426 #if LIBCWD_THREAD_SAFE
    00427 // LIBCWD_MT_*_ALLOCATOR uses a different allocator than the normal default allocator of libstdc++
    00428 // in the case of multi-threading because it can be that the allocator mutex is locked, which would
    00429 // result in a deadlock if we try to use it again here.
    00430 #define LIBCWD_MT_USERSPACE_ALLOCATOR           LIBCWD_CHARALLOCATOR_USERSPACE(userspace_instance)
    00431 #define LIBCWD_MT_INTERNAL_ALLOCATOR            LIBCWD_CHARALLOCATOR_INTERNAL(multi_threaded_internal_instance)
    00432 #define LIBCWD_MT_AUTO_INTERNAL_ALLOCATOR       LIBCWD_CHARALLOCATOR_AUTO_INTERNAL(multi_threaded_internal_instance)
    00433 #else // !LIBCWD_THREAD_SAFE
    00434 // LIBCWD_MT_*_ALLOCATOR uses the normal default allocator of libstdc++-v3 (alloc) using locking
    00435 // itself.  The userspace allocator shares it memory pool with everything else (that uses this
    00436 // allocator, which is most of the (userspace) STL).
    00437 #define LIBCWD_MT_USERSPACE_ALLOCATOR           std::allocator<char>
    00438 #define LIBCWD_MT_INTERNAL_ALLOCATOR            LIBCWD_CHARALLOCATOR_INTERNAL(single_threaded_internal_instance)
    00439 #define LIBCWD_MT_AUTO_INTERNAL_ALLOCATOR       LIBCWD_CHARALLOCATOR_AUTO_INTERNAL(single_threaded_internal_instance)
    00440 #endif // !LIBCWD_THREAD_SAFE
    00441
    00442 //---------------------------------------------------------------------------------------------------
    00443 // Internal allocator types.
    00444
    00445 // This allocator is used in critical areas that are already locked by memblk_map_instance.
    00446 typedef LIBCWD_NS_INTERNAL_ALLOCATOR(memblk_map_instance) memblk_map_allocator;
    00447
    00448 // This allocator is used in critical areas that are already locked by object_files_instance.
    00449 typedef LIBCWD_NS_INTERNAL_ALLOCATOR(object_files_instance) object_files_allocator;
    00450
    00451 // This general allocator can be used outside libcwd-specific critical areas,
    00452 // but inside a set_alloc_checking_off() .. set_alloc_checking_on() pair.
    00453 typedef LIBCWD_MT_INTERNAL_ALLOCATOR internal_allocator;
    00454
    00455 // This general allocator can be used outside libcwd-specific critical areas,
    00456 // in "user space" but that will cause internal memory to be allocated.
    00457 typedef LIBCWD_MT_AUTO_INTERNAL_ALLOCATOR auto_internal_allocator;
    00458
    00459 //---------------------------------------------------------------------------------------------------
    00460 // User space allocator type.
    00461
    00462 // This general allocator can be used outside libcwd-specific critical areas.
    00463 typedef LIBCWD_MT_USERSPACE_ALLOCATOR userspace_allocator;
    00464
    00465   } // namespace _private_
    00466 } // namespace libcwd
    00467
    00468 #endif // CWDEBUG_ALLOC
    00469 #endif // LIBCWD_PRIVATE_ALLOCATOR_H
    00470