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 #endif
00219   bool M_initialized;
00220   unsigned int M_count[bucket_sizes];           // Number of blocks (in the full+notfull list).
00221   unsigned short M_keep[bucket_sizes];          // Number of blocks that shouldn't be freed.
00222   BlockList M_list_notfull[bucket_sizes];
00223   BlockList M_list_full[bucket_sizes];
00224 
00225 #if LIBCWD_THREAD_SAFE
00226   void initialize(TSD_st& __libcwd_tsd);
00227 #else
00228   void initialize(void);
00229 #endif
00230   void uninitialize(void);
00231   ~FreeList() { uninitialize(); }
00232   char* allocate(int power, size_t size);
00233   void deallocate(char* p, int power, size_t size);
00234 #if CWDEBUG_DEBUG
00235   void consistency_check(void);
00236 #endif
00237 };
00238 
00239 template<bool needs_lock, int pool_instance>
00240   class CharPoolAlloc {
00241   private:
00242     static FreeList S_freelist;
00243 
00244   public:
00245     // Type definitions.
00246     typedef char        value_type;
00247     typedef size_t      size_type;
00248     typedef ptrdiff_t   difference_type;
00249     typedef char*       pointer;
00250     typedef char const* const_pointer;
00251     typedef char&       reference;
00252     typedef char const& const_reference;
00253 
00254     // Allocate but don't initialize num elements of type T.
00255 #if LIBCWD_THREAD_SAFE
00256     pointer allocate(size_type num, TSD_st&);
00257 #else
00258     pointer allocate(size_type num);
00259 #endif
00260 
00261     // Deallocate storage p of deleted elements.
00262 #if LIBCWD_THREAD_SAFE
00263     void deallocate(pointer p, size_type num, TSD_st&);
00264 #else
00265     void deallocate(pointer p, size_type num);
00266 #endif
00267 
00268     template <bool needs_lock1, int pool_instance1,
00269               bool needs_lock2, int pool_instance2>
00270       friend inline
00271       bool operator==(CharPoolAlloc<needs_lock1, pool_instance1> const&,
00272                       CharPoolAlloc<needs_lock2, pool_instance2> const&);
00273     template <bool needs_lock1, int pool_instance1,
00274               bool needs_lock2, int pool_instance2>
00275       friend inline
00276       bool operator!=(CharPoolAlloc<needs_lock1, pool_instance1> const&,
00277                       CharPoolAlloc<needs_lock2, pool_instance2> const&);
00278 
00279     size_type max_size(void) const { return std::numeric_limits<size_type>::max(); }
00280   };
00281 #endif // gcc 4.0 and higher.
00282 
00283 // Convenience macros.
00284 #if CWDEBUG_DEBUG
00285 #define LIBCWD_COMMA_INT_INSTANCE , int instance
00286 #define LIBCWD_COMMA_INSTANCE , instance
00287 #define LIBCWD_DEBUGDEBUG_COMMA(x) , x
00288 #else
00289 #define LIBCWD_COMMA_INT_INSTANCE
00290 #define LIBCWD_COMMA_INSTANCE
00291 #define LIBCWD_DEBUGDEBUG_COMMA(x)
00292 #endif
00293 
00294 enum pool_nt {
00295   userspace_pool,
00296   internal_pool,
00297   auto_internal_pool
00298 };
00299 
00300 // This wrapper adds sanity checks to the allocator use (like testing if
00301 // 'internal' allocators are indeed only used while in internal mode, and
00302 // critical area allocators are only used when the related lock is indeed
00303 // locked etc.
00304 template<typename T, class CharAlloc, pool_nt internal LIBCWD_COMMA_INT_INSTANCE>
00305     class allocator_adaptor {
00306     private:
00307       // The underlying allocator.
00308       CharAlloc M_char_allocator;
00309 
00310     public:
00311       // Type definitions.
00312       typedef T         value_type;
00313       typedef size_t    size_type;
00314       typedef ptrdiff_t difference_type;
00315       typedef T*                pointer;
00316       typedef T const*  const_pointer;
00317       typedef T&                reference;
00318       typedef T const&  const_reference;
00319 
00320       // Rebind allocator to type U.
00321       template <class U>
00322         struct rebind {
00323           typedef allocator_adaptor<U, CharAlloc, internal LIBCWD_COMMA_INSTANCE> other;
00324         };
00325 
00326       // Return address of values.
00327       pointer address(reference value) const { return &value; }
00328       const_pointer address(const_reference value) const { return &value; }
00329 
00330       // Constructors and destructor.
00331       allocator_adaptor(void) throw() { }
00332       allocator_adaptor(allocator_adaptor const& a) : M_char_allocator(a.M_char_allocator) { }
00333       template<class U>
00334         allocator_adaptor(allocator_adaptor<U, CharAlloc, internal LIBCWD_COMMA_INSTANCE> const& a) :
00335             M_char_allocator(a.M_char_allocator) { }
00336       template<class T2, class CharAlloc2, pool_nt internal2 LIBCWD_DEBUGDEBUG_COMMA(int instance2)>
00337         friend class allocator_adaptor;
00338       ~allocator_adaptor() throw() { }
00339 
00340       // Return maximum number of elements that can be allocated.
00341       size_type max_size(void) const { return M_char_allocator.max_size() / sizeof(T); }
00342 
00343       // Allocate but don't initialize num elements of type T.
00344       pointer allocate(size_type num);
00345       pointer allocate(size_type num, void const* hint);
00346 
00347       // Deallocate storage p of deleted elements.
00348       void deallocate(pointer p, size_type num);
00349 
00350       // Initialize elements of allocated storage p with value value.
00351       void construct(pointer p, T const& value) { new ((void*)p) T(value); }
00352 
00353       // Destroy elements of initialized storage p.
00354       void destroy(pointer p) { p->~T(); }
00355 
00356 #if CWDEBUG_DEBUG || CWDEBUG_DEBUGM
00357     private:
00358       static void sanity_check(void);
00359 #endif
00360 
00361       template <class T1, class CharAlloc1, pool_nt internal1 LIBCWD_DEBUGDEBUG_COMMA(int inst1),
00362                 class T2, class CharAlloc2, pool_nt internal2 LIBCWD_DEBUGDEBUG_COMMA(int inst2)>
00363         friend inline
00364         bool operator==(allocator_adaptor<T1, CharAlloc1, internal1 LIBCWD_DEBUGDEBUG_COMMA(inst1)> const& a1,
00365                         allocator_adaptor<T2, CharAlloc2, internal2 LIBCWD_DEBUGDEBUG_COMMA(inst2)> const& a2);
00366       template <class T1, class CharAlloc1, pool_nt internal1 LIBCWD_DEBUGDEBUG_COMMA(int inst1),
00367                 class T2, class CharAlloc2, pool_nt internal2 LIBCWD_DEBUGDEBUG_COMMA(int inst2)>
00368         friend inline
00369         bool operator!=(allocator_adaptor<T1, CharAlloc1, internal1 LIBCWD_DEBUGDEBUG_COMMA(inst1)> const& a1,
00370                         allocator_adaptor<T2, CharAlloc2, internal2 LIBCWD_DEBUGDEBUG_COMMA(inst2)> const& a2);
00371     };
00372 
00373 #if LIBCWD_THREAD_SAFE
00374 // We normally would be able to use the default allocator, but... libcwd functions can
00375 // at all times be called from malloc which might be called from std::allocator with its
00376 // lock set.  Therefore we also use a separate allocator pool for the userspace, in the
00377 // threaded case.
00378 #define LIBCWD_CHARALLOCATOR_USERSPACE(instance) ::libcwd::_private_::                          \
00379         allocator_adaptor<char,                                                                 \
00380                           CharPoolAlloc<true, userspace_instance>,                              \
00381                           userspace_pool                                                        \
00382                           LIBCWD_DEBUGDEBUG_COMMA(::libcwd::_private_::instance)>
00383 #endif
00384 
00385 // Both, multi_threaded_internal_instance and memblk_map_instance use also locks for
00386 // the allocator pool itself because they (the memory pools) are being shared between
00387 // threads from within critical areas with different mutexes.
00388 // Other instances (> 0) are supposed to only use the allocator instance from within
00389 // the critical area of the corresponding mutex_tct<instance>, and thus only by one
00390 // thread at a time.
00391 #if LIBCWD_THREAD_SAFE
00392 #define LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance)                                              \
00393                                 ::libcwd::_private_::instance ==                                \
00394                                 ::libcwd::_private_::multi_threaded_internal_instance ||        \
00395                                 ::libcwd::_private_::instance ==                                \
00396                                 ::libcwd::_private_::memblk_map_instance
00397 #else // !LIBCWD_THREAD_SAFE
00398 #define LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance) false
00399 #endif // !LIBCWD_THREAD_SAFE
00400 
00401 #define LIBCWD_CHARALLOCATOR_INTERNAL(instance) ::libcwd::_private_::                   \
00402         allocator_adaptor<char,                                                                 \
00403                           CharPoolAlloc<LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance),             \
00404                                         ::libcwd::_private_::instance >,                        \
00405                           internal_pool                                                         \
00406                           LIBCWD_DEBUGDEBUG_COMMA(::libcwd::_private_::instance)>
00407 
00408 #define LIBCWD_CHARALLOCATOR_AUTO_INTERNAL(instance) ::libcwd::_private_::              \
00409         allocator_adaptor<char,                                                                 \
00410                           CharPoolAlloc<LIBCWD_ALLOCATOR_POOL_NEEDS_LOCK(instance),             \
00411                                         ::libcwd::_private_::instance >,                        \
00412                           auto_internal_pool                                                    \
00413                           LIBCWD_DEBUGDEBUG_COMMA(::libcwd::_private_::instance)>
00414 
00415 #if LIBCWD_THREAD_SAFE
00416 // Our allocator adaptor for the Non-Shared internal cases: Single Threaded
00417 // (inst = single_threaded_internal_instance) or inside the critical area of the corresponding
00418 // libcwd mutex instance.
00419 #define LIBCWD_NS_INTERNAL_ALLOCATOR(instance)  LIBCWD_CHARALLOCATOR_INTERNAL(instance)
00420 #else // !LIBCWD_THREAD_SAFE
00421 // In a single threaded application, the Non-Shared case is equivalent to the Single Threaded case.
00422 #define LIBCWD_NS_INTERNAL_ALLOCATOR(instance)  LIBCWD_CHARALLOCATOR_INTERNAL(single_threaded_internal_instance)
00423 #endif // !LIBCWD_THREAD_SAFE
00424 
00425 #if LIBCWD_THREAD_SAFE
00426 // LIBCWD_MT_*_ALLOCATOR uses a different allocator than the normal default allocator of libstdc++
00427 // in the case of multi-threading because it can be that the allocator mutex is locked, which would
00428 // result in a deadlock if we try to use it again here.
00429 #define LIBCWD_MT_USERSPACE_ALLOCATOR           LIBCWD_CHARALLOCATOR_USERSPACE(userspace_instance)
00430 #define LIBCWD_MT_INTERNAL_ALLOCATOR            LIBCWD_CHARALLOCATOR_INTERNAL(multi_threaded_internal_instance)
00431 #define LIBCWD_MT_AUTO_INTERNAL_ALLOCATOR       LIBCWD_CHARALLOCATOR_AUTO_INTERNAL(multi_threaded_internal_instance)
00432 #else // !LIBCWD_THREAD_SAFE
00433 // LIBCWD_MT_*_ALLOCATOR uses the normal default allocator of libstdc++-v3 (alloc) using locking
00434 // itself.  The userspace allocator shares it memory pool with everything else (that uses this
00435 // allocator, which is most of the (userspace) STL).
00436 #define LIBCWD_MT_USERSPACE_ALLOCATOR           std::allocator<char>
00437 #define LIBCWD_MT_INTERNAL_ALLOCATOR            LIBCWD_CHARALLOCATOR_INTERNAL(single_threaded_internal_instance)
00438 #define LIBCWD_MT_AUTO_INTERNAL_ALLOCATOR       LIBCWD_CHARALLOCATOR_AUTO_INTERNAL(single_threaded_internal_instance)
00439 #endif // !LIBCWD_THREAD_SAFE
00440 
00441 //---------------------------------------------------------------------------------------------------
00442 // Internal allocator types.
00443 
00444 // This allocator is used in critical areas that are already locked by memblk_map_instance.
00445 typedef LIBCWD_NS_INTERNAL_ALLOCATOR(memblk_map_instance) memblk_map_allocator;
00446 
00447 // This allocator is used in critical areas that are already locked by object_files_instance.
00448 typedef LIBCWD_NS_INTERNAL_ALLOCATOR(object_files_instance) object_files_allocator;
00449 
00450 // This general allocator can be used outside libcwd-specific critical areas,
00451 // but inside a set_alloc_checking_off() .. set_alloc_checking_on() pair.
00452 typedef LIBCWD_MT_INTERNAL_ALLOCATOR internal_allocator;
00453 
00454 // This general allocator can be used outside libcwd-specific critical areas,
00455 // in "user space" but that will cause internal memory to be allocated.
00456 typedef LIBCWD_MT_AUTO_INTERNAL_ALLOCATOR auto_internal_allocator;
00457 
00458 //---------------------------------------------------------------------------------------------------
00459 // User space allocator type.
00460 
00461 // This general allocator can be used outside libcwd-specific critical areas.
00462 typedef LIBCWD_MT_USERSPACE_ALLOCATOR userspace_allocator;
00463 
00464   } // namespace _private_
00465 } // namespace libcwd
00466  
00467 #endif // CWDEBUG_ALLOC
00468 #endif // LIBCWD_PRIVATE_ALLOCATOR_H
00469 
Copyright © 2001 - 2004 Carlo Wood.  All rights reserved.