Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 1996
  3.  * Silicon Graphics Computer Systems, Inc.
  4.  *
  5.  * Permission to use, copy, modify, distribute and sell this software
  6.  * and its documentation for any purpose is hereby granted without fee,
  7.  * provided that the above copyright notice appear in all copies and
  8.  * that both that copyright notice and this permission notice appear
  9.  * in supporting documentation.  Silicon Graphics makes no
  10.  * representations about the suitability of this software for any
  11.  * purpose.  It is provided "as is" without express or implied warranty.
  12.  */
  13.  
  14. #ifndef _CPP_BITS_PTHREAD_ALLOCIMPL_H
  15. #define _CPP_BITS_PTHREAD_ALLOCIMPL_H 1
  16.  
  17. // Pthread-specific node allocator.
  18. // This is similar to the default allocator, except that free-list
  19. // information is kept separately for each thread, avoiding locking.
  20. // This should be reasonably fast even in the presence of threads.
  21. // The down side is that storage may not be well-utilized.
  22. // It is not an error to allocate memory in thread A and deallocate
  23. // it in thread B.  But this effectively transfers ownership of the memory,
  24. // so that it can only be reallocated by thread B.  Thus this can effectively
  25. // result in a storage leak if it's done on a regular basis.
  26. // It can also result in frequent sharing of
  27. // cache lines among processors, with potentially serious performance
  28. // consequences.
  29.  
  30. #include <bits/c++config.h>
  31. #include <bits/std_cerrno.h>
  32. #include <bits/stl_alloc.h>
  33. #ifndef __RESTRICT
  34. #  define __RESTRICT
  35. #endif
  36.  
  37. #include <new>
  38.  
  39. namespace std
  40. {
  41.  
  42. #define __STL_DATA_ALIGNMENT 8
  43.  
  44. union _Pthread_alloc_obj {
  45.     union _Pthread_alloc_obj * __free_list_link;
  46.     char __client_data[__STL_DATA_ALIGNMENT];    /* The client sees this.    */
  47. };
  48.  
  49. // Pthread allocators don't appear to the client to have meaningful
  50. // instances.  We do in fact need to associate some state with each
  51. // thread.  That state is represented by
  52. // _Pthread_alloc_per_thread_state<_Max_size>.
  53.  
  54. template<size_t _Max_size>
  55. struct _Pthread_alloc_per_thread_state {
  56.   typedef _Pthread_alloc_obj __obj;
  57.   enum { _S_NFREELISTS = _Max_size/__STL_DATA_ALIGNMENT };
  58.   _Pthread_alloc_obj* volatile __free_list[_S_NFREELISTS];
  59.   _Pthread_alloc_per_thread_state<_Max_size> * __next;
  60.         // Free list link for list of available per thread structures.
  61.         // When one of these becomes available for reuse due to thread
  62.         // termination, any objects in its free list remain associated
  63.         // with it.  The whole structure may then be used by a newly
  64.         // created thread.
  65.   _Pthread_alloc_per_thread_state() : __next(0)
  66.   {
  67.     memset((void *)__free_list, 0, (size_t) _S_NFREELISTS * sizeof(__obj *));
  68.   }
  69.   // Returns an object of size __n, and possibly adds to size n free list.
  70.   void *_M_refill(size_t __n);
  71. };
  72.  
  73. // Pthread-specific allocator.
  74. // The argument specifies the largest object size allocated from per-thread
  75. // free lists.  Larger objects are allocated using malloc_alloc.
  76. // Max_size must be a power of 2.
  77. template <size_t _Max_size = 128>
  78. class _Pthread_alloc_template {
  79.  
  80. public: // but only for internal use:
  81.  
  82.   typedef _Pthread_alloc_obj __obj;
  83.  
  84.   // Allocates a chunk for nobjs of size size.  nobjs may be reduced
  85.   // if it is inconvenient to allocate the requested number.
  86.   static char *_S_chunk_alloc(size_t __size, int &__nobjs);
  87.  
  88.   enum {_S_ALIGN = __STL_DATA_ALIGNMENT};
  89.  
  90.   static size_t _S_round_up(size_t __bytes) {
  91.     return (((__bytes) + (int) _S_ALIGN-1) & ~((int) _S_ALIGN - 1));
  92.   }
  93.   static size_t _S_freelist_index(size_t __bytes) {
  94.     return (((__bytes) + (int) _S_ALIGN-1)/(int)_S_ALIGN - 1);
  95.   }
  96.  
  97. private:
  98.   // Chunk allocation state. And other shared state.
  99.   // Protected by _S_chunk_allocator_lock.
  100.   static pthread_mutex_t _S_chunk_allocator_lock;
  101.   static char *_S_start_free;
  102.   static char *_S_end_free;
  103.   static size_t _S_heap_size;
  104.   static _Pthread_alloc_per_thread_state<_Max_size>* _S_free_per_thread_states;
  105.   static pthread_key_t _S_key;
  106.   static bool _S_key_initialized;
  107.         // Pthread key under which per thread state is stored.
  108.         // Allocator instances that are currently unclaimed by any thread.
  109.   static void _S_destructor(void *instance);
  110.         // Function to be called on thread exit to reclaim per thread
  111.         // state.
  112.   static _Pthread_alloc_per_thread_state<_Max_size> *_S_new_per_thread_state();
  113.         // Return a recycled or new per thread state.
  114.   static _Pthread_alloc_per_thread_state<_Max_size> *_S_get_per_thread_state();
  115.         // ensure that the current thread has an associated
  116.         // per thread state.
  117.   class _M_lock;
  118.   friend class _M_lock;
  119.   class _M_lock {
  120.       public:
  121.         _M_lock () { pthread_mutex_lock(&_S_chunk_allocator_lock); }
  122.         ~_M_lock () { pthread_mutex_unlock(&_S_chunk_allocator_lock); }
  123.   };
  124.  
  125. public:
  126.  
  127.   /* n must be > 0      */
  128.   static void * allocate(size_t __n)
  129.   {
  130.     __obj * volatile * __my_free_list;
  131.     __obj * __RESTRICT __result;
  132.     _Pthread_alloc_per_thread_state<_Max_size>* __a;
  133.  
  134.     if (__n > _Max_size) {
  135.         return(malloc_alloc::allocate(__n));
  136.     }
  137.     if (!_S_key_initialized ||
  138.         !(__a = (_Pthread_alloc_per_thread_state<_Max_size>*)
  139.                                  pthread_getspecific(_S_key))) {
  140.         __a = _S_get_per_thread_state();
  141.     }
  142.     __my_free_list = __a -> __free_list + _S_freelist_index(__n);
  143.     __result = *__my_free_list;
  144.     if (__result == 0) {
  145.         void *__r = __a -> _M_refill(_S_round_up(__n));
  146.         return __r;
  147.     }
  148.     *__my_free_list = __result -> __free_list_link;
  149.     return (__result);
  150.   };
  151.  
  152.   /* p may not be 0 */
  153.   static void deallocate(void *__p, size_t __n)
  154.   {
  155.     __obj *__q = (__obj *)__p;
  156.     __obj * volatile * __my_free_list;
  157.     _Pthread_alloc_per_thread_state<_Max_size>* __a;
  158.  
  159.     if (__n > _Max_size) {
  160.         malloc_alloc::deallocate(__p, __n);
  161.         return;
  162.     }
  163.     if (!_S_key_initialized ||
  164.         !(__a = (_Pthread_alloc_per_thread_state<_Max_size> *)
  165.                 pthread_getspecific(_S_key))) {
  166.         __a = _S_get_per_thread_state();
  167.     }
  168.     __my_free_list = __a->__free_list + _S_freelist_index(__n);
  169.     __q -> __free_list_link = *__my_free_list;
  170.     *__my_free_list = __q;
  171.   }
  172.  
  173.   static void * reallocate(void *__p, size_t __old_sz, size_t __new_sz);
  174.  
  175. } ;
  176.  
  177. typedef _Pthread_alloc_template<> pthread_alloc;
  178.  
  179.  
  180. template <size_t _Max_size>
  181. void _Pthread_alloc_template<_Max_size>::_S_destructor(void * __instance)
  182. {
  183.     _M_lock __lock_instance;    // Need to acquire lock here.
  184.     _Pthread_alloc_per_thread_state<_Max_size>* __s =
  185.         (_Pthread_alloc_per_thread_state<_Max_size> *)__instance;
  186.     __s -> __next = _S_free_per_thread_states;
  187.     _S_free_per_thread_states = __s;
  188. }
  189.  
  190. template <size_t _Max_size>
  191. _Pthread_alloc_per_thread_state<_Max_size> *
  192. _Pthread_alloc_template<_Max_size>::_S_new_per_thread_state()
  193. {    
  194.     /* lock already held here.  */
  195.     if (0 != _S_free_per_thread_states) {
  196.         _Pthread_alloc_per_thread_state<_Max_size> *__result =
  197.                                         _S_free_per_thread_states;
  198.         _S_free_per_thread_states = _S_free_per_thread_states -> __next;
  199.         return __result;
  200.     } else {
  201.         return new _Pthread_alloc_per_thread_state<_Max_size>;
  202.     }
  203. }
  204.  
  205. template <size_t _Max_size>
  206. _Pthread_alloc_per_thread_state<_Max_size> *
  207. _Pthread_alloc_template<_Max_size>::_S_get_per_thread_state()
  208. {
  209.     /*REFERENCED*/
  210.     _M_lock __lock_instance;    // Need to acquire lock here.
  211.     int __ret_code;
  212.     _Pthread_alloc_per_thread_state<_Max_size> * __result;
  213.     if (!_S_key_initialized) {
  214.         if (pthread_key_create(&_S_key, _S_destructor)) {
  215.             std::__throw_bad_alloc();  // defined in funcexcept.h
  216.         }
  217.         _S_key_initialized = true;
  218.     }
  219.     __result = _S_new_per_thread_state();
  220.     __ret_code = pthread_setspecific(_S_key, __result);
  221.     if (__ret_code) {
  222.       if (__ret_code == ENOMEM) {
  223.         std::__throw_bad_alloc();
  224.       } else {
  225.         // EINVAL
  226.         abort();
  227.       }
  228.     }
  229.     return __result;
  230. }
  231.  
  232. /* We allocate memory in large chunks in order to avoid fragmenting     */
  233. /* the malloc heap too much.                                            */
  234. /* We assume that size is properly aligned.                             */
  235. template <size_t _Max_size>
  236. char *_Pthread_alloc_template<_Max_size>
  237. ::_S_chunk_alloc(size_t __size, int &__nobjs)
  238. {
  239.   {
  240.     char * __result;
  241.     size_t __total_bytes;
  242.     size_t __bytes_left;
  243.     /*REFERENCED*/
  244.     _M_lock __lock_instance;         // Acquire lock for this routine
  245.  
  246.     __total_bytes = __size * __nobjs;
  247.     __bytes_left = _S_end_free - _S_start_free;
  248.     if (__bytes_left >= __total_bytes) {
  249.         __result = _S_start_free;
  250.         _S_start_free += __total_bytes;
  251.         return(__result);
  252.     } else if (__bytes_left >= __size) {
  253.         __nobjs = __bytes_left/__size;
  254.         __total_bytes = __size * __nobjs;
  255.         __result = _S_start_free;
  256.         _S_start_free += __total_bytes;
  257.         return(__result);
  258.     } else {
  259.         size_t __bytes_to_get =
  260.                 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
  261.         // Try to make use of the left-over piece.
  262.         if (__bytes_left > 0) {
  263.             _Pthread_alloc_per_thread_state<_Max_size>* __a =
  264.                 (_Pthread_alloc_per_thread_state<_Max_size>*)
  265.                         pthread_getspecific(_S_key);
  266.             __obj * volatile * __my_free_list =
  267.                         __a->__free_list + _S_freelist_index(__bytes_left);
  268.  
  269.             ((__obj *)_S_start_free) -> __free_list_link = *__my_free_list;
  270.             *__my_free_list = (__obj *)_S_start_free;
  271.         }
  272. #       ifdef _SGI_SOURCE
  273.           // Try to get memory that's aligned on something like a
  274.           // cache line boundary, so as to avoid parceling out
  275.           // parts of the same line to different threads and thus
  276.           // possibly different processors.
  277.           {
  278.             const int __cache_line_size = 128;  // probable upper bound
  279.             __bytes_to_get &= ~(__cache_line_size-1);
  280.             _S_start_free = (char *)memalign(__cache_line_size, __bytes_to_get);
  281.             if (0 == _S_start_free) {
  282.               _S_start_free = (char *)malloc_alloc::allocate(__bytes_to_get);
  283.             }
  284.           }
  285. #       else  /* !SGI_SOURCE */
  286.           _S_start_free = (char *)malloc_alloc::allocate(__bytes_to_get);
  287. #       endif
  288.         _S_heap_size += __bytes_to_get;
  289.         _S_end_free = _S_start_free + __bytes_to_get;
  290.     }
  291.   }
  292.   // lock is released here
  293.   return(_S_chunk_alloc(__size, __nobjs));
  294. }
  295.  
  296.  
  297. /* Returns an object of size n, and optionally adds to size n free list.*/
  298. /* We assume that n is properly aligned.                                */
  299. /* We hold the allocation lock.                                         */
  300. template <size_t _Max_size>
  301. void *_Pthread_alloc_per_thread_state<_Max_size>
  302. ::_M_refill(size_t __n)
  303. {
  304.     int __nobjs = 128;
  305.     char * __chunk =
  306.         _Pthread_alloc_template<_Max_size>::_S_chunk_alloc(__n, __nobjs);
  307.     __obj * volatile * __my_free_list;
  308.     __obj * __result;
  309.     __obj * __current_obj, * __next_obj;
  310.     int __i;
  311.  
  312.     if (1 == __nobjs)  {
  313.         return(__chunk);
  314.     }
  315.     __my_free_list = __free_list
  316.                  + _Pthread_alloc_template<_Max_size>::_S_freelist_index(__n);
  317.  
  318.     /* Build free list in chunk */
  319.       __result = (__obj *)__chunk;
  320.       *__my_free_list = __next_obj = (__obj *)(__chunk + __n);
  321.       for (__i = 1; ; __i++) {
  322.         __current_obj = __next_obj;
  323.         __next_obj = (__obj *)((char *)__next_obj + __n);
  324.         if (__nobjs - 1 == __i) {
  325.             __current_obj -> __free_list_link = 0;
  326.             break;
  327.         } else {
  328.             __current_obj -> __free_list_link = __next_obj;
  329.         }
  330.       }
  331.     return(__result);
  332. }
  333.  
  334. template <size_t _Max_size>
  335. void *_Pthread_alloc_template<_Max_size>
  336. ::reallocate(void *__p, size_t __old_sz, size_t __new_sz)
  337. {
  338.     void * __result;
  339.     size_t __copy_sz;
  340.  
  341.     if (__old_sz > _Max_size
  342.         && __new_sz > _Max_size) {
  343.         return(realloc(__p, __new_sz));
  344.     }
  345.     if (_S_round_up(__old_sz) == _S_round_up(__new_sz)) return(__p);
  346.     __result = allocate(__new_sz);
  347.     __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz;
  348.     memcpy(__result, __p, __copy_sz);
  349.     deallocate(__p, __old_sz);
  350.     return(__result);
  351. }
  352.  
  353. template <size_t _Max_size>
  354. _Pthread_alloc_per_thread_state<_Max_size> *
  355. _Pthread_alloc_template<_Max_size>::_S_free_per_thread_states = 0;
  356.  
  357. template <size_t _Max_size>
  358. pthread_key_t _Pthread_alloc_template<_Max_size>::_S_key;
  359.  
  360. template <size_t _Max_size>
  361. bool _Pthread_alloc_template<_Max_size>::_S_key_initialized = false;
  362.  
  363. template <size_t _Max_size>
  364. pthread_mutex_t _Pthread_alloc_template<_Max_size>::_S_chunk_allocator_lock
  365. = PTHREAD_MUTEX_INITIALIZER;
  366.  
  367. template <size_t _Max_size>
  368. char *_Pthread_alloc_template<_Max_size>
  369. ::_S_start_free = 0;
  370.  
  371. template <size_t _Max_size>
  372. char *_Pthread_alloc_template<_Max_size>
  373. ::_S_end_free = 0;
  374.  
  375. template <size_t _Max_size>
  376. size_t _Pthread_alloc_template<_Max_size>
  377. ::_S_heap_size = 0;
  378.  
  379.  
  380. template <class _Tp>
  381. class pthread_allocator {
  382.   typedef pthread_alloc _S_Alloc;          // The underlying allocator.
  383. public:
  384.   typedef size_t     size_type;
  385.   typedef ptrdiff_t  difference_type;
  386.   typedef _Tp*       pointer;
  387.   typedef const _Tp* const_pointer;
  388.   typedef _Tp&       reference;
  389.   typedef const _Tp& const_reference;
  390.   typedef _Tp        value_type;
  391.  
  392.   template <class _NewType> struct rebind {
  393.     typedef pthread_allocator<_NewType> other;
  394.   };
  395.  
  396.   pthread_allocator() __STL_NOTHROW {}
  397.   pthread_allocator(const pthread_allocator& a) __STL_NOTHROW {}
  398.   template <class _OtherType>
  399.         pthread_allocator(const pthread_allocator<_OtherType>&)
  400.                 __STL_NOTHROW {}
  401.   ~pthread_allocator() __STL_NOTHROW {}
  402.  
  403.   pointer address(reference __x) const { return &__x; }
  404.   const_pointer address(const_reference __x) const { return &__x; }
  405.  
  406.   // __n is permitted to be 0.  The C++ standard says nothing about what
  407.   // the return value is when __n == 0.
  408.   _Tp* allocate(size_type __n, const void* = 0) {
  409.     return __n != 0 ? static_cast<_Tp*>(_S_Alloc::allocate(__n * sizeof(_Tp)))
  410.                     : 0;
  411.   }
  412.  
  413.   // p is not permitted to be a null pointer.
  414.   void deallocate(pointer __p, size_type __n)
  415.     { _S_Alloc::deallocate(__p, __n * sizeof(_Tp)); }
  416.  
  417.   size_type max_size() const __STL_NOTHROW
  418.     { return size_t(-1) / sizeof(_Tp); }
  419.  
  420.   void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
  421.   void destroy(pointer _p) { _p->~_Tp(); }
  422. };
  423.  
  424. template<>
  425. class pthread_allocator<void> {
  426. public:
  427.   typedef size_t      size_type;
  428.   typedef ptrdiff_t   difference_type;
  429.   typedef void*       pointer;
  430.   typedef const void* const_pointer;
  431.   typedef void        value_type;
  432.  
  433.   template <class _NewType> struct rebind {
  434.     typedef pthread_allocator<_NewType> other;
  435.   };
  436. };
  437.  
  438. template <size_t _Max_size>
  439. inline bool operator==(const _Pthread_alloc_template<_Max_size>&,
  440.                        const _Pthread_alloc_template<_Max_size>&)
  441. {
  442.   return true;
  443. }
  444.  
  445. template <class _T1, class _T2>
  446. inline bool operator==(const pthread_allocator<_T1>&,
  447.                        const pthread_allocator<_T2>& a2)
  448. {
  449.   return true;
  450. }
  451.  
  452. template <class _T1, class _T2>
  453. inline bool operator!=(const pthread_allocator<_T1>&,
  454.                        const pthread_allocator<_T2>&)
  455. {
  456.   return false;
  457. }
  458.  
  459. template <class _Tp, size_t _Max_size>
  460. struct _Alloc_traits<_Tp, _Pthread_alloc_template<_Max_size> >
  461. {
  462.   static const bool _S_instanceless = true;
  463.   typedef simple_alloc<_Tp, _Pthread_alloc_template<_Max_size> > _Alloc_type;
  464.   typedef __allocator<_Tp, _Pthread_alloc_template<_Max_size> >
  465.           allocator_type;
  466. };
  467.  
  468. template <class _Tp, class _Atype, size_t _Max>
  469. struct _Alloc_traits<_Tp, __allocator<_Atype, _Pthread_alloc_template<_Max> > >
  470. {
  471.   static const bool _S_instanceless = true;
  472.   typedef simple_alloc<_Tp, _Pthread_alloc_template<_Max> > _Alloc_type;
  473.   typedef __allocator<_Tp, _Pthread_alloc_template<_Max> > allocator_type;
  474. };
  475.  
  476. template <class _Tp, class _Atype>
  477. struct _Alloc_traits<_Tp, pthread_allocator<_Atype> >
  478. {
  479.   static const bool _S_instanceless = true;
  480.   typedef simple_alloc<_Tp, _Pthread_alloc_template<> > _Alloc_type;
  481.   typedef pthread_allocator<_Tp> allocator_type;
  482. };
  483.  
  484.  
  485. } // namespace std
  486.  
  487. #endif /* _CPP_BITS_PTHREAD_ALLOCIMPL_H */
  488.  
  489. // Local Variables:
  490. // mode:C++
  491. // End:
  492.