williamr@4: /* williamr@4: * williamr@4: * Copyright (c) 1994 williamr@4: * Hewlett-Packard Company williamr@4: * williamr@4: * Copyright (c) 1996,1997 williamr@4: * Silicon Graphics Computer Systems, Inc. williamr@4: * williamr@4: * Copyright (c) 1997 williamr@4: * Moscow Center for SPARC Technology williamr@4: * williamr@4: * Copyright (c) 1999 williamr@4: * Boris Fomitchev williamr@4: * williamr@4: * This material is provided "as is", with absolutely no warranty expressed williamr@4: * or implied. Any use is at your own risk. williamr@4: * williamr@4: * Permission to use or copy this software for any purpose is hereby granted williamr@4: * without fee, provided the above notices are retained on all copies. williamr@4: * Permission to modify the code and to distribute modified code is granted, williamr@4: * provided the above notices are retained, and a notice that the code was williamr@4: * modified is included with the above copyright notice. williamr@4: * williamr@4: */ williamr@4: williamr@4: #ifndef _STLP_PTHREAD_ALLOC_H williamr@4: #define _STLP_PTHREAD_ALLOC_H williamr@4: williamr@4: // Pthread-specific node allocator. williamr@4: // This is similar to the default allocator, except that free-list williamr@4: // information is kept separately for each thread, avoiding locking. williamr@4: // This should be reasonably fast even in the presence of threads. williamr@4: // The down side is that storage may not be well-utilized. williamr@4: // It is not an error to allocate memory in thread A and deallocate williamr@4: // it in thread B. But this effectively transfers ownership of the memory, williamr@4: // so that it can only be reallocated by thread B. Thus this can effectively williamr@4: // result in a storage leak if it's done on a regular basis. williamr@4: // It can also result in frequent sharing of williamr@4: // cache lines among processors, with potentially serious performance williamr@4: // consequences. williamr@4: williamr@4: #include williamr@4: williamr@4: #ifndef _STLP_INTERNAL_ALLOC_H williamr@4: #include williamr@4: #endif williamr@4: williamr@4: #ifndef __RESTRICT williamr@4: # define __RESTRICT williamr@4: #endif williamr@4: williamr@4: _STLP_BEGIN_NAMESPACE williamr@4: williamr@4: #define _STLP_DATA_ALIGNMENT 8 williamr@4: williamr@4: union _Pthread_alloc_obj { williamr@4: union _Pthread_alloc_obj * __free_list_link; williamr@4: char __client_data[_STLP_DATA_ALIGNMENT]; /* The client sees this. */ williamr@4: }; williamr@4: williamr@4: // Pthread allocators don't appear to the client to have meaningful williamr@4: // instances. We do in fact need to associate some state with each williamr@4: // thread. That state is represented by williamr@4: // _Pthread_alloc_per_thread_state<_Max_size>. williamr@4: williamr@4: template williamr@4: struct _Pthread_alloc_per_thread_state { williamr@4: typedef _Pthread_alloc_obj __obj; williamr@4: enum { _S_NFREELISTS = _Max_size/_STLP_DATA_ALIGNMENT }; williamr@4: williamr@4: // Free list link for list of available per thread structures. williamr@4: // When one of these becomes available for reuse due to thread williamr@4: // termination, any objects in its free list remain associated williamr@4: // with it. The whole structure may then be used by a newly williamr@4: // created thread. williamr@4: _Pthread_alloc_per_thread_state() : __next(0) williamr@4: { williamr@4: memset((void *)__free_list, 0, (size_t)_S_NFREELISTS * sizeof(__obj *)); williamr@4: } williamr@4: // Returns an object of size __n, and possibly adds to size n free list. williamr@4: void *_M_refill(size_t __n); williamr@4: williamr@4: _Pthread_alloc_obj* volatile __free_list[_S_NFREELISTS]; williamr@4: _Pthread_alloc_per_thread_state<_Max_size> * __next; williamr@4: // this data member is only to be used by per_thread_allocator, which returns memory to the originating thread. williamr@4: _STLP_mutex _M_lock; williamr@4: williamr@4: }; williamr@4: williamr@4: // Pthread-specific allocator. williamr@4: // The argument specifies the largest object size allocated from per-thread williamr@4: // free lists. Larger objects are allocated using malloc_alloc. williamr@4: // Max_size must be a power of 2. williamr@4: template < __DFL_NON_TYPE_PARAM(size_t, _Max_size, _MAX_BYTES) > williamr@4: class _Pthread_alloc { williamr@4: williamr@4: public: // but only for internal use: williamr@4: williamr@4: typedef _Pthread_alloc_obj __obj; williamr@4: typedef _Pthread_alloc_per_thread_state<_Max_size> __state_type; williamr@4: typedef char value_type; williamr@4: williamr@4: // Allocates a chunk for nobjs of size size. nobjs may be reduced williamr@4: // if it is inconvenient to allocate the requested number. williamr@4: static char *_S_chunk_alloc(size_t __size, size_t &__nobjs); williamr@4: williamr@4: enum {_S_ALIGN = _STLP_DATA_ALIGNMENT}; williamr@4: williamr@4: static size_t _S_round_up(size_t __bytes) { williamr@4: return (((__bytes) + (int)_S_ALIGN-1) & ~((int)_S_ALIGN - 1)); williamr@4: } williamr@4: static size_t _S_freelist_index(size_t __bytes) { williamr@4: return (((__bytes) + (int)_S_ALIGN-1)/(int)_S_ALIGN - 1); williamr@4: } williamr@4: williamr@4: private: williamr@4: // Chunk allocation state. And other shared state. williamr@4: // Protected by _S_chunk_allocator_lock. williamr@4: static _STLP_mutex_base _S_chunk_allocator_lock; williamr@4: static char *_S_start_free; williamr@4: static char *_S_end_free; williamr@4: static size_t _S_heap_size; williamr@4: static _Pthread_alloc_per_thread_state<_Max_size>* _S_free_per_thread_states; williamr@4: static pthread_key_t _S_key; williamr@4: static bool _S_key_initialized; williamr@4: // Pthread key under which per thread state is stored. williamr@4: // Allocator instances that are currently unclaimed by any thread. williamr@4: static void _S_destructor(void *instance); williamr@4: // Function to be called on thread exit to reclaim per thread williamr@4: // state. williamr@4: static _Pthread_alloc_per_thread_state<_Max_size> *_S_new_per_thread_state(); williamr@4: public: williamr@4: // Return a recycled or new per thread state. williamr@4: static _Pthread_alloc_per_thread_state<_Max_size> *_S_get_per_thread_state(); williamr@4: private: williamr@4: // ensure that the current thread has an associated williamr@4: // per thread state. williamr@4: class _M_lock; williamr@4: friend class _M_lock; williamr@4: class _M_lock { williamr@4: public: williamr@4: _M_lock () { _S_chunk_allocator_lock._M_acquire_lock(); } williamr@4: ~_M_lock () { _S_chunk_allocator_lock._M_release_lock(); } williamr@4: }; williamr@4: williamr@4: public: williamr@4: williamr@4: /* n must be > 0 */ williamr@4: static void * allocate(size_t __n) williamr@4: { williamr@4: __obj * volatile * __my_free_list; williamr@4: __obj * __RESTRICT __result; williamr@4: __state_type* __a; williamr@4: williamr@4: if (__n > _Max_size) { williamr@4: return(__malloc_alloc<0>::allocate(__n)); williamr@4: } williamr@4: williamr@4: __a = _S_get_per_thread_state(); williamr@4: williamr@4: __my_free_list = __a -> __free_list + _S_freelist_index(__n); williamr@4: __result = *__my_free_list; williamr@4: if (__result == 0) { williamr@4: void *__r = __a -> _M_refill(_S_round_up(__n)); williamr@4: return __r; williamr@4: } williamr@4: *__my_free_list = __result -> __free_list_link; williamr@4: return (__result); williamr@4: }; williamr@4: williamr@4: /* p may not be 0 */ williamr@4: static void deallocate(void *__p, size_t __n) williamr@4: { williamr@4: __obj *__q = (__obj *)__p; williamr@4: __obj * volatile * __my_free_list; williamr@4: __state_type* __a; williamr@4: williamr@4: if (__n > _Max_size) { williamr@4: __malloc_alloc<0>::deallocate(__p, __n); williamr@4: return; williamr@4: } williamr@4: williamr@4: __a = _S_get_per_thread_state(); williamr@4: williamr@4: __my_free_list = __a->__free_list + _S_freelist_index(__n); williamr@4: __q -> __free_list_link = *__my_free_list; williamr@4: *__my_free_list = __q; williamr@4: } williamr@4: williamr@4: // boris : versions for per_thread_allocator williamr@4: /* n must be > 0 */ williamr@4: static void * allocate(size_t __n, __state_type* __a) williamr@4: { williamr@4: __obj * volatile * __my_free_list; williamr@4: __obj * __RESTRICT __result; williamr@4: williamr@4: if (__n > _Max_size) { williamr@4: return(__malloc_alloc<0>::allocate(__n)); williamr@4: } williamr@4: williamr@4: // boris : here, we have to lock per thread state, as we may be getting memory from williamr@4: // different thread pool. williamr@4: _STLP_mutex_lock __lock(__a->_M_lock); williamr@4: williamr@4: __my_free_list = __a -> __free_list + _S_freelist_index(__n); williamr@4: __result = *__my_free_list; williamr@4: if (__result == 0) { williamr@4: void *__r = __a -> _M_refill(_S_round_up(__n)); williamr@4: return __r; williamr@4: } williamr@4: *__my_free_list = __result -> __free_list_link; williamr@4: return (__result); williamr@4: }; williamr@4: williamr@4: /* p may not be 0 */ williamr@4: static void deallocate(void *__p, size_t __n, __state_type* __a) williamr@4: { williamr@4: __obj *__q = (__obj *)__p; williamr@4: __obj * volatile * __my_free_list; williamr@4: williamr@4: if (__n > _Max_size) { williamr@4: __malloc_alloc<0>::deallocate(__p, __n); williamr@4: return; williamr@4: } williamr@4: williamr@4: // boris : here, we have to lock per thread state, as we may be returning memory from williamr@4: // different thread. williamr@4: _STLP_mutex_lock __lock(__a->_M_lock); williamr@4: williamr@4: __my_free_list = __a->__free_list + _S_freelist_index(__n); williamr@4: __q -> __free_list_link = *__my_free_list; williamr@4: *__my_free_list = __q; williamr@4: } williamr@4: williamr@4: static void * reallocate(void *__p, size_t __old_sz, size_t __new_sz); williamr@4: williamr@4: } ; williamr@4: williamr@4: # if defined (_STLP_USE_TEMPLATE_EXPORT) williamr@4: _STLP_EXPORT_TEMPLATE_CLASS _Pthread_alloc<_MAX_BYTES>; williamr@4: # endif williamr@4: williamr@4: typedef _Pthread_alloc<_MAX_BYTES> __pthread_alloc; williamr@4: typedef __pthread_alloc pthread_alloc; williamr@4: williamr@4: template williamr@4: class pthread_allocator { williamr@4: typedef pthread_alloc _S_Alloc; // The underlying allocator. williamr@4: public: williamr@4: typedef size_t size_type; williamr@4: typedef ptrdiff_t difference_type; williamr@4: typedef _Tp* pointer; williamr@4: typedef const _Tp* const_pointer; williamr@4: typedef _Tp& reference; williamr@4: typedef const _Tp& const_reference; williamr@4: typedef _Tp value_type; williamr@4: williamr@4: #ifdef _STLP_MEMBER_TEMPLATE_CLASSES williamr@4: template struct rebind { williamr@4: typedef pthread_allocator<_NewType> other; williamr@4: }; williamr@4: #endif williamr@4: williamr@4: pthread_allocator() _STLP_NOTHROW {} williamr@4: pthread_allocator(const pthread_allocator<_Tp>& a) _STLP_NOTHROW {} williamr@4: williamr@4: #if defined (_STLP_MEMBER_TEMPLATES) /* && defined (_STLP_FUNCTION_PARTIAL_ORDER) */ williamr@4: template pthread_allocator(const pthread_allocator<_OtherType>&) williamr@4: _STLP_NOTHROW {} williamr@4: #endif williamr@4: williamr@4: ~pthread_allocator() _STLP_NOTHROW {} williamr@4: williamr@4: pointer address(reference __x) const { return &__x; } williamr@4: const_pointer address(const_reference __x) const { return &__x; } williamr@4: williamr@4: // __n is permitted to be 0. The C++ standard says nothing about what williamr@4: // the return value is when __n == 0. williamr@4: _Tp* allocate(size_type __n, const void* = 0) { williamr@4: return __n != 0 ? __STATIC_CAST(_Tp*,_S_Alloc::allocate(__n * sizeof(_Tp))) williamr@4: : 0; williamr@4: } williamr@4: williamr@4: // p is not permitted to be a null pointer. williamr@4: void deallocate(pointer __p, size_type __n) williamr@4: { _S_Alloc::deallocate(__p, __n * sizeof(_Tp)); } williamr@4: williamr@4: size_type max_size() const _STLP_NOTHROW williamr@4: { return size_t(-1) / sizeof(_Tp); } williamr@4: williamr@4: void construct(pointer __p, const _Tp& __val) { _STLP_PLACEMENT_NEW (__p) _Tp(__val); } williamr@4: void destroy(pointer _p) { _p->~_Tp(); } williamr@4: }; williamr@4: williamr@4: _STLP_TEMPLATE_NULL williamr@4: class _STLP_CLASS_DECLSPEC pthread_allocator { williamr@4: public: williamr@4: typedef size_t size_type; williamr@4: typedef ptrdiff_t difference_type; williamr@4: typedef void* pointer; williamr@4: typedef const void* const_pointer; williamr@4: typedef void value_type; williamr@4: #ifdef _STLP_MEMBER_TEMPLATE_CLASSES williamr@4: template struct rebind { williamr@4: typedef pthread_allocator<_NewType> other; williamr@4: }; williamr@4: #endif williamr@4: }; williamr@4: williamr@4: template williamr@4: inline bool operator==(const pthread_allocator<_T1>&, williamr@4: const pthread_allocator<_T2>& a2) williamr@4: { williamr@4: return true; williamr@4: } williamr@4: williamr@4: #ifdef _STLP_FUNCTION_TMPL_PARTIAL_ORDER williamr@4: template williamr@4: inline bool operator!=(const pthread_allocator<_T1>&, williamr@4: const pthread_allocator<_T2>&) williamr@4: { williamr@4: return false; williamr@4: } williamr@4: #endif williamr@4: williamr@4: williamr@4: #ifdef _STLP_CLASS_PARTIAL_SPECIALIZATION williamr@4: williamr@4: # ifdef _STLP_USE_RAW_SGI_ALLOCATORS williamr@4: template williamr@4: struct _Alloc_traits<_Tp, _Pthread_alloc<_Max_size> > williamr@4: { williamr@4: typedef __allocator<_Tp, _Pthread_alloc<_Max_size> > williamr@4: allocator_type; williamr@4: }; williamr@4: # endif williamr@4: williamr@4: template williamr@4: struct _Alloc_traits<_Tp, pthread_allocator<_Atype> > williamr@4: { williamr@4: typedef pthread_allocator<_Tp> allocator_type; williamr@4: }; williamr@4: williamr@4: #endif williamr@4: williamr@4: #if !defined (_STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM) williamr@4: williamr@4: template williamr@4: inline pthread_allocator<_Tp2>& williamr@4: __stl_alloc_rebind(pthread_allocator<_Tp1>& __x, const _Tp2*) { williamr@4: return (pthread_allocator<_Tp2>&)__x; williamr@4: } williamr@4: williamr@4: template williamr@4: inline pthread_allocator<_Tp2> williamr@4: __stl_alloc_create(pthread_allocator<_Tp1>&, const _Tp2*) { williamr@4: return pthread_allocator<_Tp2>(); williamr@4: } williamr@4: williamr@4: #endif /* _STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM */ williamr@4: williamr@4: // williamr@4: // per_thread_allocator<> : this allocator always return memory to the same thread williamr@4: // it was allocated from. williamr@4: // williamr@4: williamr@4: template williamr@4: class per_thread_allocator { williamr@4: typedef pthread_alloc _S_Alloc; // The underlying allocator. williamr@4: typedef pthread_alloc::__state_type __state_type; williamr@4: public: williamr@4: typedef size_t size_type; williamr@4: typedef ptrdiff_t difference_type; williamr@4: typedef _Tp* pointer; williamr@4: typedef const _Tp* const_pointer; williamr@4: typedef _Tp& reference; williamr@4: typedef const _Tp& const_reference; williamr@4: typedef _Tp value_type; williamr@4: williamr@4: #ifdef _STLP_MEMBER_TEMPLATE_CLASSES williamr@4: template struct rebind { williamr@4: typedef per_thread_allocator<_NewType> other; williamr@4: }; williamr@4: #endif williamr@4: williamr@4: per_thread_allocator() _STLP_NOTHROW { williamr@4: _M_state = _S_Alloc::_S_get_per_thread_state(); williamr@4: } williamr@4: per_thread_allocator(const per_thread_allocator<_Tp>& __a) _STLP_NOTHROW : _M_state(__a._M_state){} williamr@4: williamr@4: #if defined (_STLP_MEMBER_TEMPLATES) /* && defined (_STLP_FUNCTION_PARTIAL_ORDER) */ williamr@4: template per_thread_allocator(const per_thread_allocator<_OtherType>& __a) williamr@4: _STLP_NOTHROW : _M_state(__a._M_state) {} williamr@4: #endif williamr@4: williamr@4: ~per_thread_allocator() _STLP_NOTHROW {} williamr@4: williamr@4: pointer address(reference __x) const { return &__x; } williamr@4: const_pointer address(const_reference __x) const { return &__x; } williamr@4: williamr@4: // __n is permitted to be 0. The C++ standard says nothing about what williamr@4: // the return value is when __n == 0. williamr@4: _Tp* allocate(size_type __n, const void* = 0) { williamr@4: return __n != 0 ? __STATIC_CAST(_Tp*,_S_Alloc::allocate(__n * sizeof(_Tp), _M_state)): 0; williamr@4: } williamr@4: williamr@4: // p is not permitted to be a null pointer. williamr@4: void deallocate(pointer __p, size_type __n) williamr@4: { _S_Alloc::deallocate(__p, __n * sizeof(_Tp), _M_state); } williamr@4: williamr@4: size_type max_size() const _STLP_NOTHROW williamr@4: { return size_t(-1) / sizeof(_Tp); } williamr@4: williamr@4: void construct(pointer __p, const _Tp& __val) { _STLP_PLACEMENT_NEW (__p) _Tp(__val); } williamr@4: void destroy(pointer _p) { _p->~_Tp(); } williamr@4: williamr@4: // state is being kept here williamr@4: __state_type* _M_state; williamr@4: }; williamr@4: williamr@4: _STLP_TEMPLATE_NULL williamr@4: class _STLP_CLASS_DECLSPEC per_thread_allocator { williamr@4: public: williamr@4: typedef size_t size_type; williamr@4: typedef ptrdiff_t difference_type; williamr@4: typedef void* pointer; williamr@4: typedef const void* const_pointer; williamr@4: typedef void value_type; williamr@4: #ifdef _STLP_MEMBER_TEMPLATE_CLASSES williamr@4: template struct rebind { williamr@4: typedef per_thread_allocator<_NewType> other; williamr@4: }; williamr@4: #endif williamr@4: }; williamr@4: williamr@4: template williamr@4: inline bool operator==(const per_thread_allocator<_T1>& __a1, williamr@4: const per_thread_allocator<_T2>& __a2) williamr@4: { williamr@4: return __a1._M_state == __a2._M_state; williamr@4: } williamr@4: williamr@4: #ifdef _STLP_FUNCTION_TMPL_PARTIAL_ORDER williamr@4: template williamr@4: inline bool operator!=(const per_thread_allocator<_T1>& __a1, williamr@4: const per_thread_allocator<_T2>& __a2) williamr@4: { williamr@4: return __a1._M_state != __a2._M_state; williamr@4: } williamr@4: #endif williamr@4: williamr@4: williamr@4: #ifdef _STLP_CLASS_PARTIAL_SPECIALIZATION williamr@4: williamr@4: template williamr@4: struct _Alloc_traits<_Tp, per_thread_allocator<_Atype> > williamr@4: { williamr@4: typedef per_thread_allocator<_Tp> allocator_type; williamr@4: }; williamr@4: williamr@4: #endif williamr@4: williamr@4: #if !defined (_STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM) williamr@4: williamr@4: template williamr@4: inline per_thread_allocator<_Tp2>& williamr@4: __stl_alloc_rebind(per_thread_allocator<_Tp1>& __x, const _Tp2*) { williamr@4: return (per_thread_allocator<_Tp2>&)__x; williamr@4: } williamr@4: williamr@4: template williamr@4: inline per_thread_allocator<_Tp2> williamr@4: __stl_alloc_create(per_thread_allocator<_Tp1>&, const _Tp2*) { williamr@4: return per_thread_allocator<_Tp2>(); williamr@4: } williamr@4: williamr@4: #endif /* _STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM */ williamr@4: williamr@4: _STLP_END_NAMESPACE williamr@4: williamr@4: # if defined (_STLP_EXPOSE_GLOBALS_IMPLEMENTATION) && !defined (_STLP_LINK_TIME_INSTANTIATION) williamr@4: # include williamr@4: # endif williamr@4: williamr@4: #endif /* _STLP_PTHREAD_ALLOC */ williamr@4: williamr@4: // Local Variables: williamr@4: // mode:C++ williamr@4: // End: