Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4680 | right-hear | 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 |
||
31 | #include |
||
32 | #include |
||
33 | #ifndef __RESTRICT |
||
34 | # define __RESTRICT |
||
35 | #endif |
||
36 | |||
37 | #include |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
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 |
||
354 | _Pthread_alloc_per_thread_state<_Max_size> * |
||
355 | _Pthread_alloc_template<_Max_size>::_S_free_per_thread_states = 0; |
||
356 | |||
357 | template |
||
358 | pthread_key_t _Pthread_alloc_template<_Max_size>::_S_key; |
||
359 | |||
360 | template |
||
361 | bool _Pthread_alloc_template<_Max_size>::_S_key_initialized = false; |
||
362 | |||
363 | template |
||
364 | pthread_mutex_t _Pthread_alloc_template<_Max_size>::_S_chunk_allocator_lock |
||
365 | = PTHREAD_MUTEX_INITIALIZER; |
||
366 | |||
367 | template |
||
368 | char *_Pthread_alloc_template<_Max_size> |
||
369 | ::_S_start_free = 0; |
||
370 | |||
371 | template |
||
372 | char *_Pthread_alloc_template<_Max_size> |
||
373 | ::_S_end_free = 0; |
||
374 | |||
375 | template |
||
376 | size_t _Pthread_alloc_template<_Max_size> |
||
377 | ::_S_heap_size = 0; |
||
378 | |||
379 | |||
380 | template |
||
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 |
||
393 | typedef pthread_allocator<_NewType> other; |
||
394 | }; |
||
395 | |||
396 | pthread_allocator() __STL_NOTHROW {} |
||
397 | pthread_allocator(const pthread_allocator& a) __STL_NOTHROW {} |
||
398 | template |
||
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 |
||
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 |
||
434 | typedef pthread_allocator<_NewType> other; |
||
435 | }; |
||
436 | }; |
||
437 | |||
438 | template |
||
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 |
||
446 | inline bool operator==(const pthread_allocator<_T1>&, |
||
447 | const pthread_allocator<_T2>& a2) |
||
448 | { |
||
449 | return true; |
||
450 | } |
||
451 | |||
452 | template |
||
453 | inline bool operator!=(const pthread_allocator<_T1>&, |
||
454 | const pthread_allocator<_T2>&) |
||
455 | { |
||
456 | return false; |
||
457 | } |
||
458 | |||
459 | template |
||
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 |
||
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 |
||
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:>>> |