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 repsective 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 
Copyright © 2001 - 2004 Carlo Wood.  All rights reserved.