Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

pthread_allocimpl.h

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 1996
00003  * Silicon Graphics Computer Systems, Inc.
00004  *
00005  * Permission to use, copy, modify, distribute and sell this software
00006  * and its documentation for any purpose is hereby granted without fee,
00007  * provided that the above copyright notice appear in all copies and
00008  * that both that copyright notice and this permission notice appear
00009  * in supporting documentation.  Silicon Graphics makes no
00010  * representations about the suitability of this software for any
00011  * purpose.  It is provided "as is" without express or implied warranty.
00012  */
00013 
00014 #ifndef _CPP_BITS_PTHREAD_ALLOCIMPL_H
00015 #define _CPP_BITS_PTHREAD_ALLOCIMPL_H 1
00016 
00017 // Pthread-specific node allocator.
00018 // This is similar to the default allocator, except that free-list
00019 // information is kept separately for each thread, avoiding locking.
00020 // This should be reasonably fast even in the presence of threads.
00021 // The down side is that storage may not be well-utilized.
00022 // It is not an error to allocate memory in thread A and deallocate
00023 // it in thread B.  But this effectively transfers ownership of the memory,
00024 // so that it can only be reallocated by thread B.  Thus this can effectively
00025 // result in a storage leak if it's done on a regular basis.
00026 // It can also result in frequent sharing of
00027 // cache lines among processors, with potentially serious performance
00028 // consequences.
00029 
00030 #include <bits/c++config.h>
00031 #include <bits/std_cerrno.h>
00032 #include <bits/stl_alloc.h>
00033 #ifndef __RESTRICT
00034 #  define __RESTRICT
00035 #endif
00036 
00037 #include <new>
00038 
00039 namespace std
00040 {
00041 
00042 #define __STL_DATA_ALIGNMENT 8
00043 
00044 union _Pthread_alloc_obj {
00045     union _Pthread_alloc_obj * __free_list_link;
00046     char __client_data[__STL_DATA_ALIGNMENT];    /* The client sees this.    */
00047 };
00048 
00049 // Pthread allocators don't appear to the client to have meaningful
00050 // instances.  We do in fact need to associate some state with each
00051 // thread.  That state is represented by
00052 // _Pthread_alloc_per_thread_state<_Max_size>.
00053 
00054 template<size_t _Max_size>
00055 struct _Pthread_alloc_per_thread_state {
00056   typedef _Pthread_alloc_obj __obj;
00057   enum { _S_NFREELISTS = _Max_size/__STL_DATA_ALIGNMENT };
00058   _Pthread_alloc_obj* volatile __free_list[_S_NFREELISTS]; 
00059   _Pthread_alloc_per_thread_state<_Max_size> * __next; 
00060     // Free list link for list of available per thread structures.
00061     // When one of these becomes available for reuse due to thread
00062     // termination, any objects in its free list remain associated
00063     // with it.  The whole structure may then be used by a newly
00064     // created thread.
00065   _Pthread_alloc_per_thread_state() : __next(0)
00066   {
00067     memset((void *)__free_list, 0, (size_t) _S_NFREELISTS * sizeof(__obj *));
00068   }
00069   // Returns an object of size __n, and possibly adds to size n free list.
00070   void *_M_refill(size_t __n);
00071 };
00072 
00073 // Pthread-specific allocator.
00074 // The argument specifies the largest object size allocated from per-thread
00075 // free lists.  Larger objects are allocated using malloc_alloc.
00076 // Max_size must be a power of 2.
00077 template <size_t _Max_size = 128>
00078 class _Pthread_alloc_template {
00079 
00080 public: // but only for internal use:
00081 
00082   typedef _Pthread_alloc_obj __obj;
00083 
00084   // Allocates a chunk for nobjs of size size.  nobjs may be reduced
00085   // if it is inconvenient to allocate the requested number.
00086   static char *_S_chunk_alloc(size_t __size, int &__nobjs);
00087 
00088   enum {_S_ALIGN = __STL_DATA_ALIGNMENT};
00089 
00090   static size_t _S_round_up(size_t __bytes) {
00091     return (((__bytes) + (int) _S_ALIGN-1) & ~((int) _S_ALIGN - 1));
00092   }
00093   static size_t _S_freelist_index(size_t __bytes) {
00094     return (((__bytes) + (int) _S_ALIGN-1)/(int)_S_ALIGN - 1);
00095   }
00096 
00097 private:
00098   // Chunk allocation state. And other shared state.
00099   // Protected by _S_chunk_allocator_lock.
00100   static pthread_mutex_t _S_chunk_allocator_lock;
00101   static char *_S_start_free;
00102   static char *_S_end_free;
00103   static size_t _S_heap_size;
00104   static _Pthread_alloc_per_thread_state<_Max_size>* _S_free_per_thread_states;
00105   static pthread_key_t _S_key;
00106   static bool _S_key_initialized;
00107         // Pthread key under which per thread state is stored. 
00108         // Allocator instances that are currently unclaimed by any thread.
00109   static void _S_destructor(void *instance);
00110         // Function to be called on thread exit to reclaim per thread
00111         // state.
00112   static _Pthread_alloc_per_thread_state<_Max_size> *_S_new_per_thread_state();
00113         // Return a recycled or new per thread state.
00114   static _Pthread_alloc_per_thread_state<_Max_size> *_S_get_per_thread_state();
00115         // ensure that the current thread has an associated
00116         // per thread state.
00117   class _M_lock;
00118   friend class _M_lock;
00119   class _M_lock {
00120       public:
00121         _M_lock () { pthread_mutex_lock(&_S_chunk_allocator_lock); }
00122         ~_M_lock () { pthread_mutex_unlock(&_S_chunk_allocator_lock); }
00123   };
00124 
00125 public:
00126 
00127   /* n must be > 0      */
00128   static void * allocate(size_t __n)
00129   {
00130     __obj * volatile * __my_free_list;
00131     __obj * __RESTRICT __result;
00132     _Pthread_alloc_per_thread_state<_Max_size>* __a;
00133 
00134     if (__n > _Max_size) {
00135         return(malloc_alloc::allocate(__n));
00136     }
00137     if (!_S_key_initialized ||
00138         !(__a = (_Pthread_alloc_per_thread_state<_Max_size>*)
00139                                  pthread_getspecific(_S_key))) {
00140         __a = _S_get_per_thread_state();
00141     }
00142     __my_free_list = __a -> __free_list + _S_freelist_index(__n);
00143     __result = *__my_free_list;
00144     if (__result == 0) {
00145         void *__r = __a -> _M_refill(_S_round_up(__n));
00146         return __r;
00147     }
00148     *__my_free_list = __result -> __free_list_link;
00149     return (__result);
00150   };
00151 
00152   /* p may not be 0 */
00153   static void deallocate(void *__p, size_t __n)
00154   {
00155     __obj *__q = (__obj *)__p;
00156     __obj * volatile * __my_free_list;
00157     _Pthread_alloc_per_thread_state<_Max_size>* __a;
00158 
00159     if (__n > _Max_size) {
00160         malloc_alloc::deallocate(__p, __n);
00161         return;
00162     }
00163     if (!_S_key_initialized ||
00164         !(__a = (_Pthread_alloc_per_thread_state<_Max_size> *)
00165                 pthread_getspecific(_S_key))) {
00166         __a = _S_get_per_thread_state();
00167     }
00168     __my_free_list = __a->__free_list + _S_freelist_index(__n);
00169     __q -> __free_list_link = *__my_free_list;
00170     *__my_free_list = __q;
00171   }
00172 
00173   static void * reallocate(void *__p, size_t __old_sz, size_t __new_sz);
00174 
00175 } ;
00176 
00177 typedef _Pthread_alloc_template<> pthread_alloc;
00178 
00179 
00180 template <size_t _Max_size>
00181 void _Pthread_alloc_template<_Max_size>::_S_destructor(void * __instance)
00182 {
00183     _M_lock __lock_instance;    // Need to acquire lock here.
00184     _Pthread_alloc_per_thread_state<_Max_size>* __s =
00185         (_Pthread_alloc_per_thread_state<_Max_size> *)__instance;
00186     __s -> __next = _S_free_per_thread_states;
00187     _S_free_per_thread_states = __s;
00188 }
00189 
00190 template <size_t _Max_size>
00191 _Pthread_alloc_per_thread_state<_Max_size> *
00192 _Pthread_alloc_template<_Max_size>::_S_new_per_thread_state()
00193 {    
00194     /* lock already held here.  */
00195     if (0 != _S_free_per_thread_states) {
00196         _Pthread_alloc_per_thread_state<_Max_size> *__result =
00197                     _S_free_per_thread_states;
00198         _S_free_per_thread_states = _S_free_per_thread_states -> __next;
00199         return __result;
00200     } else {
00201         return new _Pthread_alloc_per_thread_state<_Max_size>;
00202     }
00203 }
00204 
00205 template <size_t _Max_size>
00206 _Pthread_alloc_per_thread_state<_Max_size> *
00207 _Pthread_alloc_template<_Max_size>::_S_get_per_thread_state()
00208 {
00209     /*REFERENCED*/
00210     _M_lock __lock_instance;    // Need to acquire lock here.
00211     int __ret_code;
00212     _Pthread_alloc_per_thread_state<_Max_size> * __result;
00213     if (!_S_key_initialized) {
00214         if (pthread_key_create(&_S_key, _S_destructor)) {
00215         std::__throw_bad_alloc();  // defined in funcexcept.h
00216         }
00217         _S_key_initialized = true;
00218     }
00219     __result = _S_new_per_thread_state();
00220     __ret_code = pthread_setspecific(_S_key, __result);
00221     if (__ret_code) {
00222       if (__ret_code == ENOMEM) {
00223     std::__throw_bad_alloc();
00224       } else {
00225     // EINVAL
00226     abort();
00227       }
00228     }
00229     return __result;
00230 }
00231 
00232 /* We allocate memory in large chunks in order to avoid fragmenting     */
00233 /* the malloc heap too much.                                            */
00234 /* We assume that size is properly aligned.                             */
00235 template <size_t _Max_size>
00236 char *_Pthread_alloc_template<_Max_size>
00237 ::_S_chunk_alloc(size_t __size, int &__nobjs)
00238 {
00239   {
00240     char * __result;
00241     size_t __total_bytes;
00242     size_t __bytes_left;
00243     /*REFERENCED*/
00244     _M_lock __lock_instance;         // Acquire lock for this routine
00245 
00246     __total_bytes = __size * __nobjs;
00247     __bytes_left = _S_end_free - _S_start_free;
00248     if (__bytes_left >= __total_bytes) {
00249         __result = _S_start_free;
00250         _S_start_free += __total_bytes;
00251         return(__result);
00252     } else if (__bytes_left >= __size) {
00253         __nobjs = __bytes_left/__size;
00254         __total_bytes = __size * __nobjs;
00255         __result = _S_start_free;
00256         _S_start_free += __total_bytes;
00257         return(__result);
00258     } else {
00259         size_t __bytes_to_get =
00260         2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
00261         // Try to make use of the left-over piece.
00262         if (__bytes_left > 0) {
00263             _Pthread_alloc_per_thread_state<_Max_size>* __a = 
00264                 (_Pthread_alloc_per_thread_state<_Max_size>*)
00265             pthread_getspecific(_S_key);
00266             __obj * volatile * __my_free_list =
00267                         __a->__free_list + _S_freelist_index(__bytes_left);
00268 
00269             ((__obj *)_S_start_free) -> __free_list_link = *__my_free_list;
00270             *__my_free_list = (__obj *)_S_start_free;
00271         }
00272 #       ifdef _SGI_SOURCE
00273           // Try to get memory that's aligned on something like a
00274           // cache line boundary, so as to avoid parceling out
00275           // parts of the same line to different threads and thus
00276           // possibly different processors.
00277           {
00278             const int __cache_line_size = 128;  // probable upper bound
00279             __bytes_to_get &= ~(__cache_line_size-1);
00280             _S_start_free = (char *)memalign(__cache_line_size, __bytes_to_get); 
00281             if (0 == _S_start_free) {
00282               _S_start_free = (char *)malloc_alloc::allocate(__bytes_to_get);
00283             }
00284           }
00285 #       else  /* !SGI_SOURCE */
00286           _S_start_free = (char *)malloc_alloc::allocate(__bytes_to_get);
00287 #       endif
00288         _S_heap_size += __bytes_to_get;
00289         _S_end_free = _S_start_free + __bytes_to_get;
00290     }
00291   }
00292   // lock is released here
00293   return(_S_chunk_alloc(__size, __nobjs));
00294 }
00295 
00296 
00297 /* Returns an object of size n, and optionally adds to size n free list.*/
00298 /* We assume that n is properly aligned.                                */
00299 /* We hold the allocation lock.                                         */
00300 template <size_t _Max_size>
00301 void *_Pthread_alloc_per_thread_state<_Max_size>
00302 ::_M_refill(size_t __n)
00303 {
00304     int __nobjs = 128;
00305     char * __chunk =
00306     _Pthread_alloc_template<_Max_size>::_S_chunk_alloc(__n, __nobjs);
00307     __obj * volatile * __my_free_list;
00308     __obj * __result;
00309     __obj * __current_obj, * __next_obj;
00310     int __i;
00311 
00312     if (1 == __nobjs)  {
00313         return(__chunk);
00314     }
00315     __my_free_list = __free_list
00316          + _Pthread_alloc_template<_Max_size>::_S_freelist_index(__n);
00317 
00318     /* Build free list in chunk */
00319       __result = (__obj *)__chunk;
00320       *__my_free_list = __next_obj = (__obj *)(__chunk + __n);
00321       for (__i = 1; ; __i++) {
00322         __current_obj = __next_obj;
00323         __next_obj = (__obj *)((char *)__next_obj + __n);
00324         if (__nobjs - 1 == __i) {
00325             __current_obj -> __free_list_link = 0;
00326             break;
00327         } else {
00328             __current_obj -> __free_list_link = __next_obj;
00329         }
00330       }
00331     return(__result);
00332 }
00333 
00334 template <size_t _Max_size>
00335 void *_Pthread_alloc_template<_Max_size>
00336 ::reallocate(void *__p, size_t __old_sz, size_t __new_sz)
00337 {
00338     void * __result;
00339     size_t __copy_sz;
00340 
00341     if (__old_sz > _Max_size
00342     && __new_sz > _Max_size) {
00343         return(realloc(__p, __new_sz));
00344     }
00345     if (_S_round_up(__old_sz) == _S_round_up(__new_sz)) return(__p);
00346     __result = allocate(__new_sz);
00347     __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz;
00348     memcpy(__result, __p, __copy_sz);
00349     deallocate(__p, __old_sz);
00350     return(__result);
00351 }
00352 
00353 template <size_t _Max_size>
00354 _Pthread_alloc_per_thread_state<_Max_size> *
00355 _Pthread_alloc_template<_Max_size>::_S_free_per_thread_states = 0;
00356 
00357 template <size_t _Max_size>
00358 pthread_key_t _Pthread_alloc_template<_Max_size>::_S_key;
00359 
00360 template <size_t _Max_size>
00361 bool _Pthread_alloc_template<_Max_size>::_S_key_initialized = false;
00362 
00363 template <size_t _Max_size>
00364 pthread_mutex_t _Pthread_alloc_template<_Max_size>::_S_chunk_allocator_lock
00365 = PTHREAD_MUTEX_INITIALIZER;
00366 
00367 template <size_t _Max_size>
00368 char *_Pthread_alloc_template<_Max_size>
00369 ::_S_start_free = 0;
00370 
00371 template <size_t _Max_size>
00372 char *_Pthread_alloc_template<_Max_size>
00373 ::_S_end_free = 0;
00374 
00375 template <size_t _Max_size>
00376 size_t _Pthread_alloc_template<_Max_size>
00377 ::_S_heap_size = 0;
00378 
00379 
00380 template <class _Tp>
00381 class pthread_allocator {
00382   typedef pthread_alloc _S_Alloc;          // The underlying allocator.
00383 public:
00384   typedef size_t     size_type;
00385   typedef ptrdiff_t  difference_type;
00386   typedef _Tp*       pointer;
00387   typedef const _Tp* const_pointer;
00388   typedef _Tp&       reference;
00389   typedef const _Tp& const_reference;
00390   typedef _Tp        value_type;
00391 
00392   template <class _NewType> struct rebind {
00393     typedef pthread_allocator<_NewType> other;
00394   };
00395 
00396   pthread_allocator() __STL_NOTHROW {}
00397   pthread_allocator(const pthread_allocator& a) __STL_NOTHROW {}
00398   template <class _OtherType>
00399     pthread_allocator(const pthread_allocator<_OtherType>&)
00400         __STL_NOTHROW {}
00401   ~pthread_allocator() __STL_NOTHROW {}
00402 
00403   pointer address(reference __x) const { return &__x; }
00404   const_pointer address(const_reference __x) const { return &__x; }
00405 
00406   // __n is permitted to be 0.  The C++ standard says nothing about what
00407   // the return value is when __n == 0.
00408   _Tp* allocate(size_type __n, const void* = 0) {
00409     return __n != 0 ? static_cast<_Tp*>(_S_Alloc::allocate(__n * sizeof(_Tp)))
00410                     : 0;
00411   }
00412 
00413   // p is not permitted to be a null pointer.
00414   void deallocate(pointer __p, size_type __n)
00415     { _S_Alloc::deallocate(__p, __n * sizeof(_Tp)); }
00416 
00417   size_type max_size() const __STL_NOTHROW 
00418     { return size_t(-1) / sizeof(_Tp); }
00419 
00420   void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
00421   void destroy(pointer _p) { _p->~_Tp(); }
00422 };
00423 
00424 template<>
00425 class pthread_allocator<void> {
00426 public:
00427   typedef size_t      size_type;
00428   typedef ptrdiff_t   difference_type;
00429   typedef void*       pointer;
00430   typedef const void* const_pointer;
00431   typedef void        value_type;
00432 
00433   template <class _NewType> struct rebind {
00434     typedef pthread_allocator<_NewType> other;
00435   };
00436 };
00437 
00438 template <size_t _Max_size>
00439 inline bool operator==(const _Pthread_alloc_template<_Max_size>&,
00440                        const _Pthread_alloc_template<_Max_size>&)
00441 {
00442   return true;
00443 }
00444 
00445 template <class _T1, class _T2>
00446 inline bool operator==(const pthread_allocator<_T1>&,
00447                        const pthread_allocator<_T2>& a2) 
00448 {
00449   return true;
00450 }
00451 
00452 template <class _T1, class _T2>
00453 inline bool operator!=(const pthread_allocator<_T1>&,
00454                        const pthread_allocator<_T2>&)
00455 {
00456   return false;
00457 }
00458 
00459 template <class _Tp, size_t _Max_size>
00460 struct _Alloc_traits<_Tp, _Pthread_alloc_template<_Max_size> >
00461 {
00462   static const bool _S_instanceless = true;
00463   typedef simple_alloc<_Tp, _Pthread_alloc_template<_Max_size> > _Alloc_type;
00464   typedef __allocator<_Tp, _Pthread_alloc_template<_Max_size> > 
00465           allocator_type;
00466 };
00467 
00468 template <class _Tp, class _Atype, size_t _Max>
00469 struct _Alloc_traits<_Tp, __allocator<_Atype, _Pthread_alloc_template<_Max> > >
00470 {
00471   static const bool _S_instanceless = true;
00472   typedef simple_alloc<_Tp, _Pthread_alloc_template<_Max> > _Alloc_type;
00473   typedef __allocator<_Tp, _Pthread_alloc_template<_Max> > allocator_type;
00474 };
00475 
00476 template <class _Tp, class _Atype>
00477 struct _Alloc_traits<_Tp, pthread_allocator<_Atype> >
00478 {
00479   static const bool _S_instanceless = true;
00480   typedef simple_alloc<_Tp, _Pthread_alloc_template<> > _Alloc_type;
00481   typedef pthread_allocator<_Tp> allocator_type;
00482 };
00483 
00484 
00485 } // namespace std
00486 
00487 #endif /* _CPP_BITS_PTHREAD_ALLOCIMPL_H */
00488 
00489 // Local Variables:
00490 // mode:C++
00491 // End:

Generated at Tue May 1 16:28:38 2001 for libstdc++-v3 by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001