4 * Hewlett-Packard Company
6 * Copyright (c) 1996,1997
7 * Silicon Graphics Computer Systems, Inc.
10 * Moscow Center for SPARC Technology
15 * This material is provided "as is", with absolutely no warranty expressed
16 * or implied. Any use is at your own risk.
18 * Permission to use or copy this software for any purpose is hereby granted
19 * without fee, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
26 #ifndef _STLP_PTHREAD_ALLOC_H
27 #define _STLP_PTHREAD_ALLOC_H
29 // Pthread-specific node allocator.
30 // This is similar to the default allocator, except that free-list
31 // information is kept separately for each thread, avoiding locking.
32 // This should be reasonably fast even in the presence of threads.
33 // The down side is that storage may not be well-utilized.
34 // It is not an error to allocate memory in thread A and deallocate
35 // it in thread B. But this effectively transfers ownership of the memory,
36 // so that it can only be reallocated by thread B. Thus this can effectively
37 // result in a storage leak if it's done on a regular basis.
38 // It can also result in frequent sharing of
39 // cache lines among processors, with potentially serious performance
44 #ifndef _STLP_INTERNAL_ALLOC_H
45 #include <stl/_alloc.h>
54 #define _STLP_DATA_ALIGNMENT 8
56 union _Pthread_alloc_obj {
57 union _Pthread_alloc_obj * __free_list_link;
58 char __client_data[_STLP_DATA_ALIGNMENT]; /* The client sees this. */
61 // Pthread allocators don't appear to the client to have meaningful
62 // instances. We do in fact need to associate some state with each
63 // thread. That state is represented by
64 // _Pthread_alloc_per_thread_state<_Max_size>.
66 template<size_t _Max_size>
67 struct _Pthread_alloc_per_thread_state {
68 typedef _Pthread_alloc_obj __obj;
69 enum { _S_NFREELISTS = _Max_size/_STLP_DATA_ALIGNMENT };
71 // Free list link for list of available per thread structures.
72 // When one of these becomes available for reuse due to thread
73 // termination, any objects in its free list remain associated
74 // with it. The whole structure may then be used by a newly
76 _Pthread_alloc_per_thread_state() : __next(0)
78 memset((void *)__free_list, 0, (size_t)_S_NFREELISTS * sizeof(__obj *));
80 // Returns an object of size __n, and possibly adds to size n free list.
81 void *_M_refill(size_t __n);
83 _Pthread_alloc_obj* volatile __free_list[_S_NFREELISTS];
84 _Pthread_alloc_per_thread_state<_Max_size> * __next;
85 // this data member is only to be used by per_thread_allocator, which returns memory to the originating thread.
90 // Pthread-specific allocator.
91 // The argument specifies the largest object size allocated from per-thread
92 // free lists. Larger objects are allocated using malloc_alloc.
93 // Max_size must be a power of 2.
94 template < __DFL_NON_TYPE_PARAM(size_t, _Max_size, _MAX_BYTES) >
95 class _Pthread_alloc {
97 public: // but only for internal use:
99 typedef _Pthread_alloc_obj __obj;
100 typedef _Pthread_alloc_per_thread_state<_Max_size> __state_type;
101 typedef char value_type;
103 // Allocates a chunk for nobjs of size size. nobjs may be reduced
104 // if it is inconvenient to allocate the requested number.
105 static char *_S_chunk_alloc(size_t __size, size_t &__nobjs);
107 enum {_S_ALIGN = _STLP_DATA_ALIGNMENT};
109 static size_t _S_round_up(size_t __bytes) {
110 return (((__bytes) + (int)_S_ALIGN-1) & ~((int)_S_ALIGN - 1));
112 static size_t _S_freelist_index(size_t __bytes) {
113 return (((__bytes) + (int)_S_ALIGN-1)/(int)_S_ALIGN - 1);
117 // Chunk allocation state. And other shared state.
118 // Protected by _S_chunk_allocator_lock.
119 static _STLP_mutex_base _S_chunk_allocator_lock;
120 static char *_S_start_free;
121 static char *_S_end_free;
122 static size_t _S_heap_size;
123 static _Pthread_alloc_per_thread_state<_Max_size>* _S_free_per_thread_states;
124 static pthread_key_t _S_key;
125 static bool _S_key_initialized;
126 // Pthread key under which per thread state is stored.
127 // Allocator instances that are currently unclaimed by any thread.
128 static void _S_destructor(void *instance);
129 // Function to be called on thread exit to reclaim per thread
131 static _Pthread_alloc_per_thread_state<_Max_size> *_S_new_per_thread_state();
133 // Return a recycled or new per thread state.
134 static _Pthread_alloc_per_thread_state<_Max_size> *_S_get_per_thread_state();
136 // ensure that the current thread has an associated
139 friend class _M_lock;
142 _M_lock () { _S_chunk_allocator_lock._M_acquire_lock(); }
143 ~_M_lock () { _S_chunk_allocator_lock._M_release_lock(); }
149 static void * allocate(size_t __n)
151 __obj * volatile * __my_free_list;
152 __obj * __RESTRICT __result;
155 if (__n > _Max_size) {
156 return(__malloc_alloc<0>::allocate(__n));
159 __a = _S_get_per_thread_state();
161 __my_free_list = __a -> __free_list + _S_freelist_index(__n);
162 __result = *__my_free_list;
164 void *__r = __a -> _M_refill(_S_round_up(__n));
167 *__my_free_list = __result -> __free_list_link;
172 static void deallocate(void *__p, size_t __n)
174 __obj *__q = (__obj *)__p;
175 __obj * volatile * __my_free_list;
178 if (__n > _Max_size) {
179 __malloc_alloc<0>::deallocate(__p, __n);
183 __a = _S_get_per_thread_state();
185 __my_free_list = __a->__free_list + _S_freelist_index(__n);
186 __q -> __free_list_link = *__my_free_list;
187 *__my_free_list = __q;
190 // boris : versions for per_thread_allocator
192 static void * allocate(size_t __n, __state_type* __a)
194 __obj * volatile * __my_free_list;
195 __obj * __RESTRICT __result;
197 if (__n > _Max_size) {
198 return(__malloc_alloc<0>::allocate(__n));
201 // boris : here, we have to lock per thread state, as we may be getting memory from
202 // different thread pool.
203 _STLP_mutex_lock __lock(__a->_M_lock);
205 __my_free_list = __a -> __free_list + _S_freelist_index(__n);
206 __result = *__my_free_list;
208 void *__r = __a -> _M_refill(_S_round_up(__n));
211 *__my_free_list = __result -> __free_list_link;
216 static void deallocate(void *__p, size_t __n, __state_type* __a)
218 __obj *__q = (__obj *)__p;
219 __obj * volatile * __my_free_list;
221 if (__n > _Max_size) {
222 __malloc_alloc<0>::deallocate(__p, __n);
226 // boris : here, we have to lock per thread state, as we may be returning memory from
228 _STLP_mutex_lock __lock(__a->_M_lock);
230 __my_free_list = __a->__free_list + _S_freelist_index(__n);
231 __q -> __free_list_link = *__my_free_list;
232 *__my_free_list = __q;
235 static void * reallocate(void *__p, size_t __old_sz, size_t __new_sz);
239 # if defined (_STLP_USE_TEMPLATE_EXPORT)
240 _STLP_EXPORT_TEMPLATE_CLASS _Pthread_alloc<_MAX_BYTES>;
243 typedef _Pthread_alloc<_MAX_BYTES> __pthread_alloc;
244 typedef __pthread_alloc pthread_alloc;
247 class pthread_allocator {
248 typedef pthread_alloc _S_Alloc; // The underlying allocator.
250 typedef size_t size_type;
251 typedef ptrdiff_t difference_type;
252 typedef _Tp* pointer;
253 typedef const _Tp* const_pointer;
254 typedef _Tp& reference;
255 typedef const _Tp& const_reference;
256 typedef _Tp value_type;
258 #ifdef _STLP_MEMBER_TEMPLATE_CLASSES
259 template <class _NewType> struct rebind {
260 typedef pthread_allocator<_NewType> other;
264 pthread_allocator() _STLP_NOTHROW {}
265 pthread_allocator(const pthread_allocator<_Tp>& a) _STLP_NOTHROW {}
267 #if defined (_STLP_MEMBER_TEMPLATES) /* && defined (_STLP_FUNCTION_PARTIAL_ORDER) */
268 template <class _OtherType> pthread_allocator(const pthread_allocator<_OtherType>&)
272 ~pthread_allocator() _STLP_NOTHROW {}
274 pointer address(reference __x) const { return &__x; }
275 const_pointer address(const_reference __x) const { return &__x; }
277 // __n is permitted to be 0. The C++ standard says nothing about what
278 // the return value is when __n == 0.
279 _Tp* allocate(size_type __n, const void* = 0) {
280 return __n != 0 ? __STATIC_CAST(_Tp*,_S_Alloc::allocate(__n * sizeof(_Tp)))
284 // p is not permitted to be a null pointer.
285 void deallocate(pointer __p, size_type __n)
286 { _S_Alloc::deallocate(__p, __n * sizeof(_Tp)); }
288 size_type max_size() const _STLP_NOTHROW
289 { return size_t(-1) / sizeof(_Tp); }
291 void construct(pointer __p, const _Tp& __val) { _STLP_PLACEMENT_NEW (__p) _Tp(__val); }
292 void destroy(pointer _p) { _p->~_Tp(); }
296 class _STLP_CLASS_DECLSPEC pthread_allocator<void> {
298 typedef size_t size_type;
299 typedef ptrdiff_t difference_type;
300 typedef void* pointer;
301 typedef const void* const_pointer;
302 typedef void value_type;
303 #ifdef _STLP_MEMBER_TEMPLATE_CLASSES
304 template <class _NewType> struct rebind {
305 typedef pthread_allocator<_NewType> other;
310 template <class _T1, class _T2>
311 inline bool operator==(const pthread_allocator<_T1>&,
312 const pthread_allocator<_T2>& a2)
317 #ifdef _STLP_FUNCTION_TMPL_PARTIAL_ORDER
318 template <class _T1, class _T2>
319 inline bool operator!=(const pthread_allocator<_T1>&,
320 const pthread_allocator<_T2>&)
327 #ifdef _STLP_CLASS_PARTIAL_SPECIALIZATION
329 # ifdef _STLP_USE_RAW_SGI_ALLOCATORS
330 template <class _Tp, size_t _Max_size>
331 struct _Alloc_traits<_Tp, _Pthread_alloc<_Max_size> >
333 typedef __allocator<_Tp, _Pthread_alloc<_Max_size> >
338 template <class _Tp, class _Atype>
339 struct _Alloc_traits<_Tp, pthread_allocator<_Atype> >
341 typedef pthread_allocator<_Tp> allocator_type;
346 #if !defined (_STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM)
348 template <class _Tp1, class _Tp2>
349 inline pthread_allocator<_Tp2>&
350 __stl_alloc_rebind(pthread_allocator<_Tp1>& __x, const _Tp2*) {
351 return (pthread_allocator<_Tp2>&)__x;
354 template <class _Tp1, class _Tp2>
355 inline pthread_allocator<_Tp2>
356 __stl_alloc_create(pthread_allocator<_Tp1>&, const _Tp2*) {
357 return pthread_allocator<_Tp2>();
360 #endif /* _STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM */
363 // per_thread_allocator<> : this allocator always return memory to the same thread
364 // it was allocated from.
368 class per_thread_allocator {
369 typedef pthread_alloc _S_Alloc; // The underlying allocator.
370 typedef pthread_alloc::__state_type __state_type;
372 typedef size_t size_type;
373 typedef ptrdiff_t difference_type;
374 typedef _Tp* pointer;
375 typedef const _Tp* const_pointer;
376 typedef _Tp& reference;
377 typedef const _Tp& const_reference;
378 typedef _Tp value_type;
380 #ifdef _STLP_MEMBER_TEMPLATE_CLASSES
381 template <class _NewType> struct rebind {
382 typedef per_thread_allocator<_NewType> other;
386 per_thread_allocator() _STLP_NOTHROW {
387 _M_state = _S_Alloc::_S_get_per_thread_state();
389 per_thread_allocator(const per_thread_allocator<_Tp>& __a) _STLP_NOTHROW : _M_state(__a._M_state){}
391 #if defined (_STLP_MEMBER_TEMPLATES) /* && defined (_STLP_FUNCTION_PARTIAL_ORDER) */
392 template <class _OtherType> per_thread_allocator(const per_thread_allocator<_OtherType>& __a)
393 _STLP_NOTHROW : _M_state(__a._M_state) {}
396 ~per_thread_allocator() _STLP_NOTHROW {}
398 pointer address(reference __x) const { return &__x; }
399 const_pointer address(const_reference __x) const { return &__x; }
401 // __n is permitted to be 0. The C++ standard says nothing about what
402 // the return value is when __n == 0.
403 _Tp* allocate(size_type __n, const void* = 0) {
404 return __n != 0 ? __STATIC_CAST(_Tp*,_S_Alloc::allocate(__n * sizeof(_Tp), _M_state)): 0;
407 // p is not permitted to be a null pointer.
408 void deallocate(pointer __p, size_type __n)
409 { _S_Alloc::deallocate(__p, __n * sizeof(_Tp), _M_state); }
411 size_type max_size() const _STLP_NOTHROW
412 { return size_t(-1) / sizeof(_Tp); }
414 void construct(pointer __p, const _Tp& __val) { _STLP_PLACEMENT_NEW (__p) _Tp(__val); }
415 void destroy(pointer _p) { _p->~_Tp(); }
417 // state is being kept here
418 __state_type* _M_state;
422 class _STLP_CLASS_DECLSPEC per_thread_allocator<void> {
424 typedef size_t size_type;
425 typedef ptrdiff_t difference_type;
426 typedef void* pointer;
427 typedef const void* const_pointer;
428 typedef void value_type;
429 #ifdef _STLP_MEMBER_TEMPLATE_CLASSES
430 template <class _NewType> struct rebind {
431 typedef per_thread_allocator<_NewType> other;
436 template <class _T1, class _T2>
437 inline bool operator==(const per_thread_allocator<_T1>& __a1,
438 const per_thread_allocator<_T2>& __a2)
440 return __a1._M_state == __a2._M_state;
443 #ifdef _STLP_FUNCTION_TMPL_PARTIAL_ORDER
444 template <class _T1, class _T2>
445 inline bool operator!=(const per_thread_allocator<_T1>& __a1,
446 const per_thread_allocator<_T2>& __a2)
448 return __a1._M_state != __a2._M_state;
453 #ifdef _STLP_CLASS_PARTIAL_SPECIALIZATION
455 template <class _Tp, class _Atype>
456 struct _Alloc_traits<_Tp, per_thread_allocator<_Atype> >
458 typedef per_thread_allocator<_Tp> allocator_type;
463 #if !defined (_STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM)
465 template <class _Tp1, class _Tp2>
466 inline per_thread_allocator<_Tp2>&
467 __stl_alloc_rebind(per_thread_allocator<_Tp1>& __x, const _Tp2*) {
468 return (per_thread_allocator<_Tp2>&)__x;
471 template <class _Tp1, class _Tp2>
472 inline per_thread_allocator<_Tp2>
473 __stl_alloc_create(per_thread_allocator<_Tp1>&, const _Tp2*) {
474 return per_thread_allocator<_Tp2>();
477 #endif /* _STLP_USE_NESTED_TCLASS_THROUGHT_TPARAM */
481 # if defined (_STLP_EXPOSE_GLOBALS_IMPLEMENTATION) && !defined (_STLP_LINK_TIME_INSTANTIATION)
482 # include <stl/_pthread_alloc.c>
485 #endif /* _STLP_PTHREAD_ALLOC */