Allocator inSGI STL

Allocator

//

new operator can be divided into 3 steps.

1.         Allocate the memory(use malloc).

2.         Cast the pointer.

3.         Construct the object(use placement new, new (p) T(…)).

Delete operator includes:

1.         Destruct the object

2.         Free the memory(free).

 

Usually “malloc” will allocate a memory which is larger than you need, because it need to store some maintenance information which is needed when you free the memory block you just allocate.

 

//The standard interface of a allocator

//typedefs

allocator::value_type

allocator::pointer

allocator::const_pointer

allocator::reference

allocator::const_reference

allocator::size_type

allocator_difference_type

//a nested template class, class rebind<U> has only one member “other” which is a typedef, it means allocator<U>.

allocator::rebind

//default constructor

allocator::allocator()

//copy constructor

allocator::allocator(const allocator&)

//generic template copy constructor

template<typename U>

allocator::allocator(const allocator<U>&)

//desctructor

allocator::~allocator()

//return the address of a object, a.address(x) == &x.

pointer allocator::address(reference x) const

//return the address of a const object, a.address(x) == &x.

const pointer allocator::address(const_reference x) const;

//allocate the memory

pointer allocator::allocate(size_type n, const void* = 0)

//deallocate the memory

void allocator::deallocate(pointer p, size_type n)

//max size

size_type allocator::max_size() const

//construct

void allocator::construct(pointer p, const T& x)

//destroy

void allocator::destroy(pointer p)

 

//First, let’s which container the STL containers use.

#   ifdef __STL_USE_STD_ALLOCATORS

#     define __STL_DEFAULT_ALLOCATOR(T) allocator< T >

#   else

#     define __STL_DEFAULT_ALLOCATOR(T) alloc

#   endif

//vector

template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >

class vector : protected _Vector_base<_Tp, _Alloc>

//deque

template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >

class deque : protected _Deque_base<_Tp, _Alloc>

//list

template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >

class list : protected _List_base<_Tp, _Alloc>

//

# ifdef __USE_MALLOC

typedef malloc_alloc alloc;

typedef malloc_alloc single_client_alloc;

# else

typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;

typedef __default_alloc_template<false, 0> single_client_alloc;

# endif

 

typedef __malloc_alloc_template<0> malloc_alloc;

 

//the standard version of the allocator in SGI STL.

template <class _Tp>

class allocator {

 typedef alloc _Alloc;          // The underlying allocator.

public:

 typedef size_t     size_type;

 typedef ptrdiff_t difference_type;

 typedef _Tp*       pointer;

 typedef const _Tp* const_pointer;

 typedef _Tp&       reference;

 typedef const _Tp& const_reference;

 typedef _Tp        value_type;

 

 template <class _Tp1> struct rebind {

    typedef allocator<_Tp1> other;

 };

 

 allocator()throw() {}

 allocator(const allocator&) throw() {}

 template <class _Tp1> allocator(const allocator<_Tp1>&)throw() {}

 ~allocator()throw() {}

 

 pointer address(reference __x) const { return &__x; }

 const_pointer address(const_reference __x) const { return &__x; }

 

 // __n is permitted to be 0. The C++ standard says nothing about what

 // the return value is when __n == 0.

 _Tp* allocate(size_type __n, const void* = 0) {

    return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp)))

                    : 0;

 }

 

 // __p is not permitted to be a null pointer.

 void deallocate(pointer __p, size_type __n)

    { _Alloc::deallocate(__p, __n * sizeof(_Tp)); }

 

 size_type max_size() const throw()

    { return size_t(-1) / sizeof(_Tp); }

 

 void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }

 void destroy(pointer __p) { __p->~_Tp(); }

};

 

//

typedef __malloc_alloc_template<0> malloc_alloc;

//

template <int __inst> //__inst seems useless, it has never be used in SGI STL.

class __malloc_alloc_template {

 

private:

 

 static void* _S_oom_malloc(size_t); // set_new_hander mechanism.

 static void* _S_oom_realloc(void*, size_t);

 

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG

 static void (* __malloc_alloc_oom_handler)();

#endif

 

public:

 

 static void* allocate(size_t __n)

 {

    void* __result = malloc(__n);

    if (0 == __result) __result = _S_oom_malloc(__n);

    return __result;

 }

 

 static void deallocate(void* __p, size_t /* __n */)

 {

    free(__p);

 }

 

 static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz)

 {

    void* __result = realloc(__p, __new_sz);

    if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);

    return __result;

 }

 

 static void (* __set_malloc_handler(void (*__f)()))()

 {

    void (* __old)() = __malloc_alloc_oom_handler;

    __malloc_alloc_oom_handler = __f;

    return(__old);

 }

 

};

 

// malloc_alloc out-of-memory handling

 

#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG

template <int __inst>

void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;

#endif

 

template <int __inst>

void*

__malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)

{

    void (* __my_malloc_handler)();

    void* __result;

 

    for (;;) {

        __my_malloc_handler = __malloc_alloc_oom_handler;

        if (0 == __my_malloc_handler) { throw std::bad_alloc(); }

        (*__my_malloc_handler)();

        __result = malloc(__n);

        if (__result) return(__result);

    }

}

 

template <int __inst>

void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p, size_t __n)

{

    void (* __my_malloc_handler)();

    void* __result;

 

    for (;;) {

        __my_malloc_handler = __malloc_alloc_oom_handler;

        if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }

        (*__my_malloc_handler)();

        __result = realloc(__p, __n);

        if (__result) return(__result);

    }

}

 

 

//The sub-allocation allocator in SGI STL, which is actually used in all the containers in SGI STL.Its policy is when the size the user request is larger than 128Byte, itll be considered as a large block request, the level-1 allocator(malloc_alloc) will be used. Otherwise, the level-2 allocator will be used.

static void* allocate(size_t __n)

{

    void* __ret = 0;

 

    if (__n > (size_t) _MAX_BYTES) {

      __ret = malloc_alloc::allocate(__n);

    }

else {

}

}

//free_list[16]

//memory pool

//embeded pointer

union _Obj {

        union _Obj* _M_free_list_link;

        char _M_client_data[1];    /* The client sees this.        */

 };

//src

template <bool threads, int inst> //inst is useless, its never used in the SGI STL.

class __default_alloc_template {

 

private:

    enum {_ALIGN = 8};

    enum {_MAX_BYTES = 128};

    enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN

 static size_t

 _S_round_up(size_t __bytes)

    { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }

 

private:

 union _Obj {

        union _Obj* _M_free_list_link;

        char _M_client_data[1];    /* The client sees this.        */

 };

private:

 static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];

 static size_t _S_freelist_index(size_t __bytes) {

        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);

 }

 

 // Returns an object of size __n, and optionally adds to size __n free list.

 static void* _S_refill(size_t __n);

 // Allocates a chunk for nobjs of size size. nobjs may be reduced

 // if it is inconvenient to allocate the requested number.

 static char* _S_chunk_alloc(size_t __size, int& __nobjs);

 

 // Chunk allocation state.

 static char* _S_start_free;

 static char* _S_end_free;

 static size_t _S_heap_size;

public:

 

 /* __n must be > 0      */

 static void* allocate(size_t __n)

 {

    void* __ret = 0;

    if (__n > (size_t) _MAX_BYTES) { //level-1 allocator

      __ret = malloc_alloc::allocate(__n);

    }

    else {

      _Obj* __STL_VOLATILE* __my_free_list

          = _S_free_list + _S_freelist_index(__n);

      _Obj* __RESTRICT __result = *__my_free_list;

      if (__result == 0)

        __ret = _S_refill(_S_round_up(__n));

      else {

        *__my_free_list = __result -> _M_free_list_link;

        __ret = __result;

      }

    }

 

    return __ret;

 };

 

 /* __p may not be 0 */

 static void deallocate(void* __p, size_t __n)

 {

    if (__n > (size_t) _MAX_BYTES)

      malloc_alloc::deallocate(__p, __n);

    else {

      _Obj* __STL_VOLATILE* __my_free_list

          = _S_free_list + _S_freelist_index(__n);

      _Obj* __q = (_Obj*)__p;

 

      __q -> _M_free_list_link = *__my_free_list;

      *__my_free_list = __q;

    }

 }

 

 static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);

 

} ;

//

/* Returns an object of size __n, and optionally adds to size __n free list.*/

/* We assume that __n is properly aligned.                                */

/* We hold the allocation lock.                                         */

template <bool __threads, int __inst>

void*

__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)

{

    int __nobjs = 20;

    char* __chunk = _S_chunk_alloc(__n, __nobjs); //alloc the chunk

    _Obj* __STL_VOLATILE* __my_free_list;

    _Obj* __result;

    _Obj* __current_obj;

    _Obj* __next_obj;

    int __i;

 

    if (1 == __nobjs) return(__chunk);

    __my_free_list = _S_free_list + _S_freelist_index(__n);

 

    /* Build free list in chunk */   //build the free_list

      __result = (_Obj*)__chunk;

      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);

      for (__i = 1; ; __i++) {

        __current_obj = __next_obj;

        __next_obj = (_Obj*)((char*)__next_obj + __n);

        if (__nobjs - 1 == __i) {

            __current_obj -> _M_free_list_link = 0;

            break;

        } else {

            __current_obj -> _M_free_list_link = __next_obj;

        }

      }

    return(__result);

}

 

/* We allocate memory in large chunks in order to avoid fragmenting     */

/* the malloc heap too much.                                            */

/* We assume that size is properly aligned.                             */

/* We hold the allocation lock.                                         */

template <bool __threads, int __inst>

char*

__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,

                                                            int& __nobjs)

{

    char* __result;

    size_t __total_bytes = __size * __nobjs;

    size_t __bytes_left = _S_end_free - _S_start_free;

 

    if (__bytes_left >= __total_bytes) {//the memory pool has engough memory

        __result = _S_start_free;

        _S_start_free += __total_bytes;

        return(__result);

    } else if (__bytes_left >= __size) {//has at least one __size memory

        __nobjs = (int)(__bytes_left/__size);

        __total_bytes = __size * __nobjs;

        __result = _S_start_free;

        _S_start_free += __total_bytes;

        return(__result);

    } else {                              //not enough for even one __size memory

        size_t __bytes_to_get =

      2 * __total_bytes + _S_round_up(_S_heap_size >> 4);

        // Try to make use of the left-over piece.

        if (__bytes_left > 0) {           //hook the left unused memory to the free_list

            _Obj* __STL_VOLATILE* __my_free_list =

                        _S_free_list + _S_freelist_index(__bytes_left);

 

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;

            *__my_free_list = (_Obj*)_S_start_free;

        }

        _S_start_free = (char*)malloc(__bytes_to_get);

        if (0 == _S_start_free) {    //Faile to get

            size_t __i;

            _Obj* __STL_VOLATILE* __my_free_list;

         _Obj* __p;

            // Try to make do with what we have. That can't

            // hurt. We do not try smaller requests, since that tends

            // to result in disaster on multi-process machines.

            for (__i = __size;

                 __i <= (size_t) _MAX_BYTES;

                 __i += (size_t) _ALIGN) {         //Check the current free_list.

                __my_free_list = _S_free_list + _S_freelist_index(__i);

                __p = *__my_free_list;

                if (0 != __p) {

                    *__my_free_list = __p -> _M_free_list_link;

                    _S_start_free = (char*)__p;

                    _S_end_free = _S_start_free + __i;

                    return(_S_chunk_alloc(__size, __nobjs));

                    // Any leftover piece will eventually make it to the

                    // right free list.

                }

            }

         _S_end_free = 0;   // In case of exception.

            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);

            // This should either throw an

            // exception or remedy the situation. Thus we assume it

            // succeeded.

        }

        _S_heap_size += __bytes_to_get;

        _S_end_free = _S_start_free + __bytes_to_get;

        return(_S_chunk_alloc(__size, __nobjs));

    }

}

 参考文献

  •  侯杰  《STL源码分析》
  • SGI STL 源码。
posted @ 2008-11-18 19:43  kasim  阅读(522)  评论(0编辑  收藏  举报