OSDN Git Service

2004-03-06 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / include / ext / mt_allocator.h
1 // MT-optimized allocator -*- C++ -*-
2
3 // Copyright (C) 2003, 2004 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15
16 // You should have received a copy of the GNU General Public License along
17 // with this library; see the file COPYING.  If not, write to the Free
18 // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 // USA.
20
21 // As a special exception, you may use this file as part of a free software
22 // library without restriction.  Specifically, if other files instantiate
23 // templates or use macros or inline functions from this file, or you compile
24 // this file and link it with other files to produce an executable, this
25 // file does not by itself cause the resulting executable to be covered by
26 // the GNU General Public License.  This exception does not however
27 // invalidate any other reasons why the executable file might be covered by
28 // the GNU General Public License.
29
30 /** @file ext/mt_allocator.h
31  *  This file is a GNU extension to the Standard C++ Library.
32  *  You should only include this header if you are using GCC 3 or later.
33  */
34
35 #ifndef _MT_ALLOCATOR_H
36 #define _MT_ALLOCATOR_H 1
37
38 #include <new>
39 #include <cstdlib>
40 #include <bits/functexcept.h>
41 #include <bits/gthr.h>
42 #include <bits/atomicity.h>
43
44 namespace __gnu_cxx
45 {
46   /**
47    *  This is a fixed size (power of 2) allocator which - when
48    *  compiled with thread support - will maintain one freelist per
49    *  size per thread plus a "global" one. Steps are taken to limit
50    *  the per thread freelist sizes (by returning excess back to
51    *  "global").
52    *
53    *  Further details:
54    *  http://gcc.gnu.org/onlinedocs/libstdc++/ext/mt_allocator.html
55    */
56   template<typename _Tp>
57     class __mt_alloc
58     {
59     public:
60       typedef size_t     size_type;
61       typedef ptrdiff_t  difference_type;
62       typedef _Tp*       pointer;
63       typedef const _Tp* const_pointer;
64       typedef _Tp&       reference;
65       typedef const _Tp& const_reference;
66       typedef _Tp        value_type;
67
68       template<typename _Tp1>
69         struct rebind
70         { typedef __mt_alloc<_Tp1> other; };
71
72       __mt_alloc() throw() 
73       {
74         // XXX
75       }
76
77       __mt_alloc(const __mt_alloc&) throw() 
78       {
79         // XXX
80       }
81
82       template<typename _Tp1>
83         __mt_alloc(const __mt_alloc<_Tp1>& obj) throw()  
84         {
85           // XXX
86         }
87
88       ~__mt_alloc() throw() { }
89
90       pointer
91       address(reference __x) const { return &__x; }
92
93       const_pointer
94       address(const_reference __x) const { return &__x; }
95
96       size_type
97       max_size() const throw() 
98       { return size_t(-1) / sizeof(_Tp); }
99
100       // _GLIBCXX_RESOLVE_LIB_DEFECTS
101       // 402. wrong new expression in [some_] allocator::construct
102       void 
103       construct(pointer __p, const _Tp& __val) 
104       { ::new(__p) _Tp(__val); }
105
106       void 
107       destroy(pointer __p) { __p->~_Tp(); }
108
109       pointer
110       allocate(size_t __n, const void* = 0);
111
112       void
113       deallocate(pointer __p, size_type __n);
114
115       // Variables used to configure the behavior of the allocator,
116       // assigned and explained in detail below.
117       struct tune
118       {
119         // Allocation requests (after round-up to power of 2) below
120         // this value will be handled by the allocator. A raw new/
121         // call will be used for requests larger than this value.
122         size_t  _M_max_bytes; 
123
124         // In order to avoid fragmenting and minimize the number of
125         // new() calls we always request new memory using this
126         // value. Based on previous discussions on the libstdc++
127         // mailing list we have choosen the value below.
128         // See http://gcc.gnu.org/ml/libstdc++/2001-07/msg00077.html
129         size_t  _M_chunk_size;
130
131         // The maximum number of supported threads. Our Linux 2.4.18
132         // reports 4070 in /proc/sys/kernel/threads-max
133         size_t  _M_max_threads;
134
135         // Each time a deallocation occurs in a threaded application
136         // we make sure that there are no more than
137         // _M_freelist_headroom % of used memory on the freelist. If
138         // the number of additional records is more than
139         // _M_freelist_headroom % of the freelist, we move these
140         // records back to the global pool.
141         size_t  _M_freelist_headroom;
142
143         // Set to true forces all allocations to use new().
144         bool    _M_force_new; 
145      
146         explicit tune() 
147         : _M_max_bytes(128), _M_chunk_size(4096 - 4 * sizeof(void*)), 
148 #ifdef __GTHREADS
149           _M_max_threads(4096), 
150 #else
151           _M_max_threads(0), 
152 #endif
153           _M_freelist_headroom(10), 
154           _M_force_new(getenv("GLIBCXX_FORCE_NEW") ? true : false) 
155         { }      
156
157         explicit tune(size_t __maxb, size_t __chunk, size_t __maxthreads, 
158                          size_t __headroom, bool __force) 
159         : _M_max_bytes(__maxb), _M_chunk_size(__chunk), 
160           _M_max_threads(__maxthreads), _M_freelist_headroom(__headroom), 
161           _M_force_new(__force)
162         { }      
163       };
164
165     private:
166       // We need to create the initial lists and set up some variables
167       // before we can answer to the first request for memory.
168 #ifdef __GTHREADS
169       static __gthread_once_t           _S_once;
170 #endif
171       static bool                       _S_init;
172
173       static void 
174       _S_initialize();
175
176       // Configuration options.
177       static tune                       _S_options;
178
179       static const tune
180       _S_get_options() { return _S_options; }
181
182       static void
183       _S_set_options(tune __t)
184       { 
185         if (!_S_init)
186           _S_options = __t;
187       }
188
189       // Using short int as type for the binmap implies we are never
190       // caching blocks larger than 65535 with this allocator
191       typedef unsigned short int binmap_type;
192       static binmap_type*               _S_binmap;
193
194       // Each requesting thread is assigned an id ranging from 1 to
195       // _S_max_threads. Thread id 0 is used as a global memory pool.
196       // In order to get constant performance on the thread assignment
197       // routine, we keep a list of free ids. When a thread first
198       // requests memory we remove the first record in this list and
199       // stores the address in a __gthread_key. When initializing the
200       // __gthread_key we specify a destructor. When this destructor
201       // (i.e. the thread dies) is called, we return the thread id to
202       // the front of this list.
203 #ifdef __GTHREADS
204       struct thread_record
205       {
206         // Points to next free thread id record. NULL if last record in list.
207         thread_record* volatile next;
208
209         // Thread id ranging from 1 to _S_max_threads.
210         size_t id;
211       };
212
213       static thread_record* volatile    _S_thread_freelist_first;
214       static __gthread_mutex_t          _S_thread_freelist_mutex;
215       static __gthread_key_t            _S_thread_key;
216
217       static void 
218       _S_destroy_thread_key(void* freelist_pos);
219
220       static size_t 
221       _S_get_thread_id();
222 #endif
223
224       struct block_record
225       {
226         // Points to the next block_record for its thread_id.
227         block_record* volatile next;
228
229         // The thread id of the thread which has requested this block.
230 #ifdef __GTHREADS
231         size_t thread_id;
232 #endif
233       };
234
235       struct bin_record
236       {
237         // An "array" of pointers to the first free block for each
238         // thread id. Memory to this "array" is allocated in _S_initialize()
239         // for _S_max_threads + global pool 0.
240         block_record** volatile first;
241
242         // An "array" of counters used to keep track of the amount of
243         // blocks that are on the freelist/used for each thread id.
244         // Memory to these "arrays" is allocated in _S_initialize() for
245         // _S_max_threads + global pool 0.
246         size_t* volatile free;
247         size_t* volatile used;
248
249         // Each bin has its own mutex which is used to ensure data
250         // integrity while changing "ownership" on a block.  The mutex
251         // is initialized in _S_initialize().
252 #ifdef __GTHREADS
253         __gthread_mutex_t* mutex;
254 #endif
255       };
256
257       // An "array" of bin_records each of which represents a specific
258       // power of 2 size. Memory to this "array" is allocated in
259       // _S_initialize().
260       static bin_record* volatile       _S_bin;
261
262       // Actual value calculated in _S_initialize().
263       static size_t                     _S_bin_size; 
264     };
265
266   template<typename _Tp>
267     typename __mt_alloc<_Tp>::pointer
268     __mt_alloc<_Tp>::
269     allocate(size_t __n, const void*)
270     {
271       // Although the test in __gthread_once() would suffice, we wrap
272       // test of the once condition in our own unlocked check. This
273       // saves one function call to pthread_once() (which itself only
274       // tests for the once value unlocked anyway and immediately
275       // returns if set)
276       if (!_S_init)
277         {
278 #ifdef __GTHREADS
279           if (__gthread_active_p())
280             __gthread_once(&_S_once, _S_initialize);
281 #endif
282           if (!_S_init)
283             _S_initialize();
284         }
285       
286       // Requests larger than _M_max_bytes are handled by new/delete
287       // directly.
288       const size_t __bytes = __n * sizeof(_Tp);
289       if (__bytes > _S_options._M_max_bytes || _S_options._M_force_new)
290         {
291           void* __ret = ::operator new(__bytes);
292           return static_cast<_Tp*>(__ret);
293         }
294       
295       // Round up to power of 2 and figure out which bin to use.
296       size_t bin = _S_binmap[__bytes];
297       
298 #ifdef __GTHREADS
299       size_t thread_id = _S_get_thread_id();
300 #else
301       size_t thread_id = 0;
302 #endif
303       
304       // Find out if we have blocks on our freelist.  If so, go ahead
305       // and use them directly without having to lock anything.
306       block_record* block = NULL;
307       if (_S_bin[bin].first[thread_id] == NULL)
308         {
309           // Are we using threads?
310           // - Yes, check if there are free blocks on the global
311           //   list. If so, grab up to block_count blocks in one
312           //   lock and change ownership. If the global list is 
313           //   empty, we allocate a new chunk and add those blocks 
314           //   directly to our own freelist (with us as owner).
315           // - No, all operations are made directly to global pool 0
316           //   no need to lock or change ownership but check for free
317           //   blocks on global list (and if not add new ones) and
318           //   get the first one.
319 #ifdef __GTHREADS
320           if (__gthread_active_p())
321             {
322               size_t bin_t = 1 << bin;
323               size_t block_count =
324                 _S_options._M_chunk_size /(bin_t + sizeof(block_record));
325               
326               __gthread_mutex_lock(_S_bin[bin].mutex);
327               
328               if (_S_bin[bin].first[0] == NULL)
329                 {
330                   // No need to hold the lock when we are adding a
331                   // whole chunk to our own list.
332                   __gthread_mutex_unlock(_S_bin[bin].mutex);
333                   
334                   _S_bin[bin].first[thread_id] =
335                     static_cast<block_record*>(::operator new(_S_options._M_chunk_size));
336                   
337                   if (!_S_bin[bin].first[thread_id])
338                     std::__throw_bad_alloc();
339                   
340                   _S_bin[bin].free[thread_id] = block_count;
341                   
342                   block_count--;
343                   block = _S_bin[bin].first[thread_id];
344                   
345                   while (block_count > 0)
346                     {
347                       block->next = (block_record*)((char*)block +
348                                                     (bin_t + sizeof(block_record)));
349                       block->thread_id = thread_id;
350                       block = block->next;
351                       block_count--;
352                     }
353                   
354                   block->next = NULL;
355                   block->thread_id = thread_id;
356                 }
357               else
358                 {
359                   size_t global_count = 0;                
360                   block_record* tmp;              
361                   while (_S_bin[bin].first[0] != NULL 
362                          && global_count < block_count)
363                     {
364                       tmp = _S_bin[bin].first[0]->next;
365                       block = _S_bin[bin].first[0];
366
367                       if (_S_bin[bin].first[thread_id] == NULL)
368                         {
369                           _S_bin[bin].first[thread_id] = block;
370                           block->next = NULL;
371                         }
372                       else
373                         {
374                           block->next = _S_bin[bin].first[thread_id];
375                           _S_bin[bin].first[thread_id] = block;
376                         }
377                       
378                       block->thread_id = thread_id;
379                       _S_bin[bin].free[thread_id]++;
380                       _S_bin[bin].first[0] = tmp;
381                       global_count++;
382                     }
383                   __gthread_mutex_unlock(_S_bin[bin].mutex);
384                 }
385               
386               // Return the first newly added block in our list and
387               // update the counters
388               block = _S_bin[bin].first[thread_id];
389               _S_bin[bin].first[thread_id] = 
390                 _S_bin[bin].first[thread_id]->next;           
391               _S_bin[bin].free[thread_id]--;
392               _S_bin[bin].used[thread_id]++;
393             }
394           else
395 #endif
396             {
397               _S_bin[bin].first[0] = 
398                 static_cast<block_record*>(::operator new(_S_options._M_chunk_size));
399               
400               size_t bin_t = 1 << bin;
401               size_t block_count = 
402                 _S_options._M_chunk_size / (bin_t + sizeof(block_record));
403               
404               block_count--;
405               block = _S_bin[bin].first[0];           
406               while (block_count > 0)
407                 {
408                   block->next = (block_record*)((char*)block +
409                                                 (bin_t + sizeof(block_record)));
410                   block = block->next;
411                   block_count--;
412                 }
413               block->next = NULL;
414               block = _S_bin[bin].first[0];
415               
416               // Remove from list.
417               _S_bin[bin].first[0] = _S_bin[bin].first[0]->next;
418             }
419         }
420       else
421         {
422           // "Default" operation - we have blocks on our own
423           // freelist grab the first record and update the counters.
424           block = _S_bin[bin].first[thread_id];
425           
426           _S_bin[bin].first[thread_id] = _S_bin[bin].first[thread_id]->next;
427           
428 #ifdef __GTHREADS
429           if (__gthread_active_p())
430             {
431               _S_bin[bin].free[thread_id]--;
432               _S_bin[bin].used[thread_id]++;
433             }
434 #endif
435         }
436       return static_cast<_Tp*>(static_cast<void*>((char*)block + 
437                                                   sizeof(block_record)));
438     }
439   
440   template<typename _Tp>
441     void
442     __mt_alloc<_Tp>::
443     deallocate(pointer __p, size_type __n)
444     {
445       // Requests larger than _M_max_bytes are handled by operators
446       // new/delete directly.
447       if (__n * sizeof(_Tp) > _S_options._M_max_bytes 
448           || _S_options._M_force_new)
449         {
450           ::operator delete(__p);
451           return;
452         }
453       
454       // Round up to power of 2 and figure out which bin to use.
455       size_t bin = _S_binmap[__n * sizeof(_Tp)];
456       
457 #ifdef __GTHREADS
458       size_t thread_id = _S_get_thread_id();
459 #else
460       size_t thread_id = 0;
461 #endif
462       
463       block_record* block = (block_record*)((char*)__p
464                                             - sizeof(block_record));
465       
466 #ifdef __GTHREADS
467       if (__gthread_active_p())
468         {
469           // Calculate the number of records to remove from our freelist.
470           int remove = _S_bin[bin].free[thread_id] -
471             (_S_bin[bin].used[thread_id] / _S_options._M_freelist_headroom);
472
473           // The calculation above will almost always tell us to
474           // remove one or two records at a time, but this creates too
475           // much contention when locking and therefore we wait until
476           // the number of records is "high enough".
477           if (remove > (int)(100 * (_S_bin_size - bin)) &&
478               remove > (int)(_S_bin[bin].free[thread_id] /
479                                _S_options._M_freelist_headroom))
480             {
481               __gthread_mutex_lock(_S_bin[bin].mutex);
482               block_record* tmp;
483               while (remove > 0)
484                 {
485                   tmp = _S_bin[bin].first[thread_id]->next;
486                   if (_S_bin[bin].first[0] == NULL)
487                     {
488                       _S_bin[bin].first[0] = _S_bin[bin].first[thread_id];
489                       _S_bin[bin].first[0]->next = NULL;
490                     }
491                   else
492                     {
493                       _S_bin[bin].first[thread_id]->next = _S_bin[bin].first[0];
494                       _S_bin[bin].first[0] = _S_bin[bin].first[thread_id];
495                     }
496                   
497                   _S_bin[bin].first[thread_id] = tmp;
498                   _S_bin[bin].free[thread_id]--;
499                   remove--;
500                 }
501               __gthread_mutex_unlock(_S_bin[bin].mutex);
502             }
503           
504           // Return this block to our list and update counters and
505           // owner id as needed.
506           if (_S_bin[bin].first[thread_id] == NULL)
507             {
508               _S_bin[bin].first[thread_id] = block;
509               block->next = NULL;
510             }
511           else
512             {
513               block->next = _S_bin[bin].first[thread_id];
514               _S_bin[bin].first[thread_id] = block;
515             }
516           
517           _S_bin[bin].free[thread_id]++;
518           
519           if (thread_id == block->thread_id)
520             _S_bin[bin].used[thread_id]--;
521           else
522             {
523               _S_bin[bin].used[block->thread_id]--;
524               block->thread_id = thread_id;
525             }
526         }
527       else
528 #endif
529         {
530           // Single threaded application - return to global pool.
531           if (_S_bin[bin].first[0] == NULL)
532             {
533               _S_bin[bin].first[0] = block;
534               block->next = NULL;
535             }
536           else
537             {
538               block->next = _S_bin[bin].first[0];
539               _S_bin[bin].first[0] = block;
540             }
541         }
542     }
543   
544   template<typename _Tp>
545     void
546     __mt_alloc<_Tp>::
547     _S_initialize()
548     {
549       if (_S_options._M_force_new)
550         return;
551         
552       // Calculate the number of bins required based on _M_max_bytes.
553       // _S_bin_size is statically-initialized to one.
554       size_t bin_size = 1;
555       while (_S_options._M_max_bytes > bin_size)
556         {
557           bin_size = bin_size << 1;
558           _S_bin_size++;
559         }
560
561       // Setup the bin map for quick lookup of the relevant bin.
562       const size_t n1 = (_S_options._M_max_bytes + 1) * sizeof(binmap_type);
563       _S_binmap = static_cast<binmap_type*>(::operator new(n1));
564
565       binmap_type* bp_t = _S_binmap;
566       binmap_type bin_max_t = 1;
567       binmap_type bin_t = 0;
568       for (binmap_type ct = 0; ct <= _S_options._M_max_bytes; ct++)
569         {
570           if (ct > bin_max_t)
571             {
572               bin_max_t <<= 1;
573               bin_t++;
574             }
575           *bp_t++ = bin_t;
576         }
577
578       // If __gthread_active_p() create and initialize the list of
579       // free thread ids. Single threaded applications use thread id 0
580       // directly and have no need for this.
581 #ifdef __GTHREADS
582       if (__gthread_active_p())
583         {
584           const size_t n2 = sizeof(thread_record) * _S_options._M_max_threads;
585           _S_thread_freelist_first = static_cast<thread_record*>(::operator new(n2));
586
587           // NOTE! The first assignable thread id is 1 since the
588           // global pool uses id 0
589           size_t i;
590           for (i = 1; i < _S_options._M_max_threads; i++)
591             {
592               thread_record& tr = _S_thread_freelist_first[i - 1];
593               tr.next = &_S_thread_freelist_first[i];
594               tr.id = i;
595             }
596
597           // Set last record.
598           _S_thread_freelist_first[i - 1].next = NULL;
599           _S_thread_freelist_first[i - 1].id = i;
600
601           // Initialize per thread key to hold pointer to
602           // _S_thread_freelist.
603           __gthread_key_create(&_S_thread_key, _S_destroy_thread_key);
604         }
605 #endif
606
607       // Initialize _S_bin and its members.
608       _S_bin = static_cast<bin_record*>(::operator 
609                                         new(sizeof(bin_record) * _S_bin_size));
610
611       // Maximum number of threads. 
612       size_t __n = 1;
613 #ifdef __GTHREADS
614       if (__gthread_active_p())
615         __n = _S_options._M_max_threads + 1;
616 #endif
617
618       for (size_t bin = 0; bin < _S_bin_size; bin++)
619         {
620           bin_record& br = _S_bin[bin];
621           br.first = static_cast<block_record**>(::operator new(sizeof(block_record*) * __n));
622
623 #ifdef __GTHREADS
624           if (__gthread_active_p())
625             {
626               br.free = static_cast<size_t*>(::operator new(sizeof(size_t) 
627                                                             * __n));
628               br.used = static_cast<size_t*>(::operator new(sizeof(size_t) 
629                                                             * __n));
630               br.mutex = static_cast<__gthread_mutex_t*>(::operator new(sizeof(__gthread_mutex_t)));
631
632 #ifdef __GTHREAD_MUTEX_INIT
633               {
634                 // Do not copy a POSIX/gthr mutex once in use.
635                 __gthread_mutex_t __tmp = __GTHREAD_MUTEX_INIT;
636                 *br.mutex = __tmp;
637               }
638 #else
639               { __GTHREAD_MUTEX_INIT_FUNCTION(br.mutex); }
640 #endif
641             }
642 #endif
643
644           for (size_t thread = 0; thread < __n; thread++)
645             {
646               br.first[thread] = NULL;
647 #ifdef __GTHREADS
648               if (__gthread_active_p())
649                 {
650                   br.free[thread] = 0;
651                   br.used[thread] = 0;
652                 }
653 #endif
654             }
655         }
656       _S_init = true;
657     }
658
659 #ifdef __GTHREADS
660   template<typename _Tp>
661     void
662     __mt_alloc<_Tp>::
663     _S_destroy_thread_key(void* freelist_pos)
664     {
665       // Return this thread id record to front of thread_freelist.
666       __gthread_mutex_lock(&_S_thread_freelist_mutex);
667       ((thread_record*)freelist_pos)->next = _S_thread_freelist_first;
668       _S_thread_freelist_first = (thread_record*)freelist_pos;
669       __gthread_mutex_unlock(&_S_thread_freelist_mutex);
670     }
671
672   template<typename _Tp>
673     size_t
674     __mt_alloc<_Tp>::
675     _S_get_thread_id()
676     {
677       // If we have thread support and it's active we check the thread
678       // key value and return it's id or if it's not set we take the
679       // first record from _S_thread_freelist and sets the key and
680       // returns it's id.
681       if (__gthread_active_p())
682         {
683           thread_record* freelist_pos = static_cast<thread_record*>(__gthread_getspecific(_S_thread_key)); 
684           if (freelist_pos == NULL)
685             {
686               // Since _S_options._M_max_threads must be larger than
687               // the theoretical max number of threads of the OS the
688               // list can never be empty.
689               __gthread_mutex_lock(&_S_thread_freelist_mutex);
690               freelist_pos = _S_thread_freelist_first;
691               _S_thread_freelist_first = _S_thread_freelist_first->next;
692               __gthread_mutex_unlock(&_S_thread_freelist_mutex);
693
694               __gthread_setspecific(_S_thread_key, 
695                                     static_cast<void*>(freelist_pos));
696             }
697           return freelist_pos->id;
698         }
699
700       // Otherwise (no thread support or inactive) all requests are
701       // served from the global pool 0.
702       return 0;
703     }
704 #endif
705
706   template<typename _Tp>
707     inline bool
708     operator==(const __mt_alloc<_Tp>&, const __mt_alloc<_Tp>&)
709     { return true; }
710   
711   template<typename _Tp>
712     inline bool
713     operator!=(const __mt_alloc<_Tp>&, const __mt_alloc<_Tp>&)
714     { return false; }
715
716   template<typename _Tp> 
717     bool __mt_alloc<_Tp>::_S_init = false;
718
719   template<typename _Tp> 
720     typename __mt_alloc<_Tp>::tune __mt_alloc<_Tp>::_S_options;
721
722   template<typename _Tp> 
723     typename __mt_alloc<_Tp>::binmap_type* __mt_alloc<_Tp>::_S_binmap;
724
725   template<typename _Tp> 
726     typename __mt_alloc<_Tp>::bin_record* volatile __mt_alloc<_Tp>::_S_bin;
727
728   template<typename _Tp> 
729     size_t __mt_alloc<_Tp>::_S_bin_size = 1;
730
731   // Actual initialization in _S_initialize().
732 #ifdef __GTHREADS
733   template<typename _Tp> 
734     __gthread_once_t __mt_alloc<_Tp>::_S_once = __GTHREAD_ONCE_INIT;
735
736   template<typename _Tp> 
737     typename __mt_alloc<_Tp>::thread_record*
738     volatile __mt_alloc<_Tp>::_S_thread_freelist_first = NULL;
739
740   template<typename _Tp> 
741     __gthread_key_t __mt_alloc<_Tp>::_S_thread_key;
742
743   template<typename _Tp> 
744     __gthread_mutex_t
745 #ifdef __GTHREAD_MUTEX_INIT
746     __mt_alloc<_Tp>::_S_thread_freelist_mutex = __GTHREAD_MUTEX_INIT;
747 #else
748   // XXX
749     __mt_alloc<_Tp>::_S_thread_freelist_mutex;
750 #endif
751 #endif
752 } // namespace __gnu_cxx
753
754 #endif