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

private_allocator.h

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