Subversion Repositories Kolibri OS

Rev

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  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 
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  struct rebind {
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: