summaryrefslogtreecommitdiff
path: root/libstdc++-v3/include/ext/mt_allocator.h
diff options
context:
space:
mode:
Diffstat (limited to 'libstdc++-v3/include/ext/mt_allocator.h')
-rw-r--r--libstdc++-v3/include/ext/mt_allocator.h861
1 files changed, 861 insertions, 0 deletions
diff --git a/libstdc++-v3/include/ext/mt_allocator.h b/libstdc++-v3/include/ext/mt_allocator.h
new file mode 100644
index 00000000000..1e95b55f467
--- /dev/null
+++ b/libstdc++-v3/include/ext/mt_allocator.h
@@ -0,0 +1,861 @@
+// MT-optimized allocator -*- C++ -*-
+
+// Copyright (C) 2003 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 2, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING. If not, write to the Free
+// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+// USA.
+
+// As a special exception, you may use this file as part of a free software
+// library without restriction. Specifically, if other files instantiate
+// templates or use macros or inline functions from this file, or you compile
+// this file and link it with other files to produce an executable, this
+// file does not by itself cause the resulting executable to be covered by
+// the GNU General Public License. This exception does not however
+// invalidate any other reasons why the executable file might be covered by
+// the GNU General Public License.
+
+/** @file ext/mt_allocator.h
+ * This file is a GNU extension to the Standard C++ Library.
+ * You should only include this header if you are using GCC 3 or later.
+ */
+
+#ifndef _MT_ALLOCATOR_H
+#define _MT_ALLOCATOR_H 1
+
+#include <cstdlib>
+#include <bits/functexcept.h>
+#include <bits/stl_threads.h>
+#include <bits/atomicity.h>
+#include <bits/allocator_traits.h>
+
+/**
+ * This is a fixed size (power of 2) allocator which - when compiled
+ * with thread support - will maintain one freelist per size per thread
+ * plus a "global" one. Steps are taken to limit the per thread freelist
+ * sizes (by returning excess back to "global").
+ *
+ * Usage examples:
+ * vector<int, __gnu_cxx::__mt_alloc<0> > v1;
+ *
+ * typedef std::__allocator<char, __gnu_cxx::__mt_alloc<0> > string_alloc;
+ * std::basic_string<char, std::char_traits<char>, string_alloc> s1;
+ */
+
+namespace __gnu_cxx
+{
+ template<int __inst>
+ class __mt_alloc
+ {
+ private:
+ /*
+ * We need to create the initial lists and set up some variables
+ * before we can answer to the first request for memory.
+ * The initialization of these variables is done at file scope
+ * below class declaration.
+ */
+#ifdef __GTHREADS
+ static __gthread_once_t _S_once_mt;
+#endif
+ static bool _S_initialized;
+
+ /*
+ * Using short int as type for the binmap implies we are never caching
+ * blocks larger than 65535 with this allocator
+ */
+ typedef unsigned short int binmap_type;
+ static binmap_type* _S_binmap;
+
+ static void _S_init();
+
+ /*
+ * Variables used to "tune" the behavior of the allocator, assigned
+ * and explained in detail below.
+ */
+ static size_t _S_max_bytes;
+ static size_t _S_chunk_size;
+ static size_t _S_max_threads;
+ static size_t _S_no_of_bins;
+ static size_t _S_freelist_headroom;
+
+ /*
+ * Each requesting thread is assigned an id ranging from 1 to
+ * _S_max_threads. Thread id 0 is used as a global memory pool.
+ * In order to get constant performance on the thread assignment
+ * routine, we keep a list of free ids. When a thread first requests
+ * memory we remove the first record in this list and stores the address
+ * in a __gthread_key. When initializing the __gthread_key
+ * we specify a destructor. When this destructor (i.e. the thread dies)
+ * is called, we return the thread id to the back of this list.
+ */
+#ifdef __GTHREADS
+ struct thread_record
+ {
+ /*
+ * Points to next free thread id record. NULL if last record in list.
+ */
+ thread_record* next;
+
+ /*
+ * Thread id ranging from 1 to _S_max_threads.
+ */
+ size_t id;
+ };
+
+ static thread_record* _S_thread_freelist_first;
+ static thread_record* _S_thread_freelist_last;
+ static __gthread_mutex_t _S_thread_freelist_mutex;
+ static void _S_thread_key_destr(void* freelist_pos);
+ static __gthread_key_t _S_thread_key;
+ static size_t _S_get_thread_id();
+#endif
+
+ struct block_record
+ {
+ /*
+ * Points to the next block_record for its thread_id.
+ */
+ block_record* next;
+
+ /*
+ * The thread id of the thread which has requested this block.
+ * All blocks are initially "owned" by global pool thread id 0.
+ */
+ size_t thread_id;
+ };
+
+ struct bin_record
+ {
+ /*
+ * An "array" of pointers to the first/last free block for each
+ * thread id. Memory to these "arrays" is allocated in _S_init()
+ * for _S_max_threads + global pool 0.
+ */
+ block_record** first;
+ block_record** last;
+
+ /*
+ * An "array" of counters used to keep track of the amount of blocks
+ * that are on the freelist/used for each thread id.
+ * Memory to these "arrays" is allocated in _S_init()
+ * for _S_max_threads + global pool 0.
+ */
+ size_t* free;
+ size_t* used;
+
+ /*
+ * Each bin has its own mutex which is used to ensure data integrity
+ * while changing "ownership" on a block.
+ * The mutex is initialized in _S_init().
+ */
+#ifdef __GTHREADS
+ __gthread_mutex_t* mutex;
+#endif
+ };
+
+ /*
+ * An "array" of bin_records each of which represents a specific
+ * power of 2 size. Memory to this "array" is allocated in _S_init().
+ */
+ static bin_record* _S_bin;
+
+ public:
+ static void*
+ allocate(size_t __n)
+ {
+
+ /*
+ * Requests larger than _S_max_bytes are handled by
+ * malloc/free directly
+ */
+ if (__n > _S_max_bytes)
+ {
+ void* __ret = malloc(__n);
+ if (!__ret)
+ __throw_bad_alloc();
+
+ return __ret;
+ }
+
+ /*
+ * Although the test in __gthread_once() would suffice, we
+ * wrap test of the once condition in our own unlocked
+ * check. This saves one function call to pthread_once()
+ * (which itself only tests for the once value unlocked anyway
+ * and immediately returns if set)
+ */
+ if (!_S_initialized)
+ {
+#ifdef __GTHREADS
+ if (__gthread_active_p())
+ __gthread_once(&_S_once_mt, _S_init);
+ else
+#endif
+ {
+ _S_max_threads = 0;
+ _S_init();
+ }
+ }
+
+ /*
+ * Round up to power of 2 and figure out which bin to use
+ */
+ size_t bin = _S_binmap[__n];
+
+#ifdef __GTHREADS
+ size_t thread_id = _S_get_thread_id();
+#else
+ size_t thread_id = 0;
+#endif
+
+ block_record* block;
+
+ /*
+ * Find out if we have blocks on our freelist.
+ * If so, go ahead and use them directly without
+ * having to lock anything.
+ */
+ if (_S_bin[bin].first[thread_id] == NULL)
+ {
+ /*
+ * Are we using threads?
+ * - Yes, lock and check if there are free blocks on the global
+ * list (and if not add new ones), get the first one
+ * and change owner.
+ * - No, all operations are made directly to global pool 0
+ * no need to lock or change ownership but check for free
+ * blocks on global list (and if not add new ones) and
+ * get the first one.
+ */
+#ifdef __GTHREADS
+ if (__gthread_active_p())
+ {
+ __gthread_mutex_lock(_S_bin[bin].mutex);
+
+ if (_S_bin[bin].first[0] == NULL)
+ {
+ _S_bin[bin].first[0] =
+ (block_record*)malloc(_S_chunk_size);
+
+ if (!_S_bin[bin].first[0])
+ {
+ __gthread_mutex_unlock(_S_bin[bin].mutex);
+ __throw_bad_alloc();
+ }
+
+ size_t bin_t = 1 << bin;
+ size_t block_count =
+ _S_chunk_size /(bin_t + sizeof(block_record));
+
+ _S_bin[bin].free[0] = block_count;
+
+ block_count--;
+ block = _S_bin[bin].first[0];
+
+ while (block_count > 0)
+ {
+ block->next = (block_record*)((char*)block +
+ (bin_t + sizeof(block_record)));
+ block = block->next;
+ block_count--;
+ }
+
+ block->next = NULL;
+ _S_bin[bin].last[0] = block;
+ }
+
+ block = _S_bin[bin].first[0];
+
+ /*
+ * Remove from list and count down the available counter on
+ * global pool 0.
+ */
+ _S_bin[bin].first[0] = _S_bin[bin].first[0]->next;
+ _S_bin[bin].free[0]--;
+
+ __gthread_mutex_unlock(_S_bin[bin].mutex);
+
+ /*
+ * Now that we have removed the block from the global
+ * freelist we can change owner and update the used
+ * counter for this thread without locking.
+ */
+ block->thread_id = thread_id;
+ _S_bin[bin].used[thread_id]++;
+ }
+ else
+#endif
+ {
+ _S_bin[bin].first[0] = (block_record*)malloc(_S_chunk_size);
+
+ if (!_S_bin[bin].first[0])
+ __throw_bad_alloc();
+
+ size_t bin_t = 1 << bin;
+ size_t block_count =
+ _S_chunk_size / (bin_t + sizeof(block_record));
+
+ _S_bin[bin].free[0] = block_count;
+
+ block_count--;
+ block = _S_bin[bin].first[0];
+
+ while (block_count > 0)
+ {
+ block->next = (block_record*)((char*)block +
+ (bin_t + sizeof(block_record)));
+ block = block->next;
+ block_count--;
+ }
+
+ block->next = NULL;
+ _S_bin[bin].last[0] = block;
+
+ block = _S_bin[bin].first[0];
+
+ /*
+ * Remove from list and count down the available counter on
+ * global pool 0 and increase it's used counter.
+ */
+ _S_bin[bin].first[0] = _S_bin[bin].first[0]->next;
+ _S_bin[bin].free[0]--;
+ _S_bin[bin].used[0]++;
+ }
+ }
+ else
+ {
+ /*
+ * "Default" operation - we have blocks on our own freelist
+ * grab the first record and update the counters.
+ */
+ block = _S_bin[bin].first[thread_id];
+
+ _S_bin[bin].first[thread_id] = _S_bin[bin].first[thread_id]->next;
+ _S_bin[bin].free[thread_id]--;
+ _S_bin[bin].used[thread_id]++;
+ }
+
+ return (void*)((char*)block + sizeof(block_record));
+ }
+
+ static void
+ deallocate(void* __p, size_t __n)
+ {
+ /*
+ * Requests larger than _S_max_bytes are handled by
+ * malloc/free directly
+ */
+ if (__n > _S_max_bytes)
+ {
+ free(__p);
+ return;
+ }
+
+ /*
+ * Round up to power of 2 and figure out which bin to use
+ */
+ size_t bin = _S_binmap[__n];
+
+#ifdef __GTHREADS
+ size_t thread_id = _S_get_thread_id();
+#else
+ size_t thread_id = 0;
+#endif
+
+ block_record* block = (block_record*)((char*)__p
+ - sizeof(block_record));
+
+ /*
+ * This block will always be at the back of a list and thus
+ * we set its next pointer to NULL.
+ */
+ block->next = NULL;
+
+#ifdef __GTHREADS
+ if (__gthread_active_p())
+ {
+ /*
+ * Calculate the number of records to remove from our freelist
+ */
+ int remove = _S_bin[bin].free[thread_id] -
+ (_S_bin[bin].used[thread_id] / _S_freelist_headroom);
+
+ /*
+ * The calculation above will almost always tell us to
+ * remove one or two records at a time, but this creates
+ * too much contention when locking and therefore we
+ * wait until the number of records is "high enough".
+ */
+ if (remove > (int)(100 * (_S_no_of_bins - bin)) &&
+ remove > (int)(_S_bin[bin].free[thread_id] /
+ _S_freelist_headroom))
+ {
+ __gthread_mutex_lock(_S_bin[bin].mutex);
+
+ while (remove > 0)
+ {
+ if (_S_bin[bin].first[0] == NULL)
+ _S_bin[bin].first[0] = _S_bin[bin].first[thread_id];
+ else
+ _S_bin[bin].last[0]->next = _S_bin[bin].first[thread_id];
+
+ _S_bin[bin].last[0] = _S_bin[bin].first[thread_id];
+
+ _S_bin[bin].first[thread_id] =
+ _S_bin[bin].first[thread_id]->next;
+
+ _S_bin[bin].free[0]++;
+ _S_bin[bin].free[thread_id]--;
+
+ remove--;
+ }
+
+ _S_bin[bin].last[0]->next = NULL;
+
+ __gthread_mutex_unlock(_S_bin[bin].mutex);
+ }
+
+ /*
+ * Did we allocate this block?
+ * - Yes, return it to our freelist
+ * - No, return it to global pool
+ */
+ if (thread_id == block->thread_id)
+ {
+ if (_S_bin[bin].first[thread_id] == NULL)
+ _S_bin[bin].first[thread_id] = block;
+ else
+ _S_bin[bin].last[thread_id]->next = block;
+
+ _S_bin[bin].last[thread_id] = block;
+
+ _S_bin[bin].free[thread_id]++;
+ _S_bin[bin].used[thread_id]--;
+ }
+ else
+ {
+ __gthread_mutex_lock(_S_bin[bin].mutex);
+
+ if (_S_bin[bin].first[0] == NULL)
+ _S_bin[bin].first[0] = block;
+ else
+ _S_bin[bin].last[0]->next = block;
+
+ _S_bin[bin].last[0] = block;
+
+ _S_bin[bin].free[0]++;
+ _S_bin[bin].used[block->thread_id]--;
+
+ __gthread_mutex_unlock(_S_bin[bin].mutex);
+ }
+ }
+ else
+#endif
+ {
+ /*
+ * Single threaded application - return to global pool
+ */
+ if (_S_bin[bin].first[0] == NULL)
+ _S_bin[bin].first[0] = block;
+ else
+ _S_bin[bin].last[0]->next = block;
+
+ _S_bin[bin].last[0] = block;
+
+ _S_bin[bin].free[0]++;
+ _S_bin[bin].used[0]--;
+ }
+ }
+ };
+
+ template<int __inst>
+ void
+ __mt_alloc<__inst>::
+ _S_init()
+ {
+ /*
+ * Calculate the number of bins required based on _S_max_bytes,
+ * _S_no_of_bins is initialized to 1 below.
+ */
+ {
+ size_t bin_t = 1;
+ while (_S_max_bytes > bin_t)
+ {
+ bin_t = bin_t << 1;
+ _S_no_of_bins++;
+ }
+ }
+
+ /*
+ * Setup the bin map for quick lookup of the relevant bin
+ */
+ _S_binmap = (binmap_type*)
+ malloc ((_S_max_bytes + 1) * sizeof(binmap_type));
+
+ if (!_S_binmap)
+ __throw_bad_alloc();
+
+ binmap_type* bp_t = _S_binmap;
+ binmap_type bin_max_t = 1;
+ binmap_type bin_t = 0;
+ for (binmap_type ct = 0; ct <= _S_max_bytes; ct++)
+ {
+ if (ct > bin_max_t)
+ {
+ bin_max_t <<= 1;
+ bin_t++;
+ }
+ *bp_t++ = bin_t;
+ }
+
+ /*
+ * If __gthread_active_p() create and initialize the list of
+ * free thread ids. Single threaded applications use thread id 0
+ * directly and have no need for this.
+ */
+#ifdef __GTHREADS
+ if (__gthread_active_p())
+ {
+ _S_thread_freelist_first =
+ (thread_record*)malloc(sizeof(thread_record) * _S_max_threads);
+
+ if (!_S_thread_freelist_first)
+ __throw_bad_alloc();
+
+ /*
+ * NOTE! The first assignable thread id is 1 since the global
+ * pool uses id 0
+ */
+ size_t i;
+ for (i = 1; i < _S_max_threads; i++)
+ {
+ _S_thread_freelist_first[i - 1].next =
+ &_S_thread_freelist_first[i];
+
+ _S_thread_freelist_first[i - 1].id = i;
+ }
+
+ /*
+ * Set last record and pointer to this
+ */
+ _S_thread_freelist_first[i - 1].next = NULL;
+ _S_thread_freelist_first[i - 1].id = i;
+ _S_thread_freelist_last = &_S_thread_freelist_first[i - 1];
+
+ /*
+ * Initialize per thread key to hold pointer to
+ * _S_thread_freelist NOTE! Here's an ugly workaround - if
+ * _S_thread_key_destr is not explicitly called at least
+ * once it won't be linked into the application. This is the
+ * behavior of template methods and __gthread_key_create()
+ * takes only a pointer to the function and does not cause
+ * the compiler to create an instance.
+ */
+ _S_thread_key_destr(NULL);
+ __gthread_key_create(&_S_thread_key, _S_thread_key_destr);
+ }
+#endif
+
+ /*
+ * Initialize _S_bin and its members
+ */
+ _S_bin = (bin_record*)malloc(sizeof(bin_record) * _S_no_of_bins);
+
+ if (!_S_bin)
+ __throw_bad_alloc();
+
+ for (size_t bin = 0; bin < _S_no_of_bins; bin++)
+ {
+ _S_bin[bin].first = (block_record**)
+ malloc(sizeof(block_record*) * (_S_max_threads + 1));
+
+ if (!_S_bin[bin].first)
+ __throw_bad_alloc();
+
+ _S_bin[bin].last = (block_record**)
+ malloc(sizeof(block_record*) * (_S_max_threads + 1));
+
+ if (!_S_bin[bin].last)
+ __throw_bad_alloc();
+
+ _S_bin[bin].free = (size_t*)
+ malloc(sizeof(size_t) * (_S_max_threads + 1));
+
+ if (!_S_bin[bin].free)
+ __throw_bad_alloc();
+
+ _S_bin[bin].used = (size_t*)
+ malloc(sizeof(size_t) * (_S_max_threads + 1));
+
+ if (!_S_bin[bin].used)
+ __throw_bad_alloc();
+
+ /*
+ * Ugly workaround of what at the time of writing seems to be
+ * a parser problem - see PR c++/9779 for more info.
+ */
+#ifdef __GTHREADS
+ size_t s = sizeof(__gthread_mutex_t);
+ _S_bin[bin].mutex = (__gthread_mutex_t*)malloc(s);
+
+ if (!_S_bin[bin].mutex)
+ __throw_bad_alloc();
+
+ /*
+ * This is not only ugly - it's extremly non-portable!
+ * However gthr.h does not currently provide a
+ * __gthread_mutex_init() call. The correct solution to
+ * this problem needs to be discussed.
+ */
+ pthread_mutex_init(_S_bin[bin].mutex, NULL);
+#endif
+
+ for (size_t thread = 0; thread <= _S_max_threads; thread++)
+ {
+ _S_bin[bin].first[thread] = NULL;
+ _S_bin[bin].last[thread] = NULL;
+ _S_bin[bin].free[thread] = 0;
+ _S_bin[bin].used[thread] = 0;
+ }
+ }
+
+ _S_initialized = true;
+ }
+
+#ifdef __GTHREADS
+ template<int __inst>
+ void
+ __mt_alloc<__inst>::
+ _S_thread_key_destr(void* freelist_pos)
+ {
+ /*
+ * This is due to the ugly workaround mentioned in _S_init()
+ */
+ if (freelist_pos == NULL)
+ return;
+
+ /*
+ * If the thread - when it dies - still have records on its
+ * freelist we return them to the global pool here.
+ */
+ for (size_t bin = 0; bin < _S_no_of_bins; bin++)
+ {
+ block_record* block =
+ _S_bin[bin].first[((thread_record*)freelist_pos)->id];
+
+ if (block != NULL)
+ {
+ __gthread_mutex_lock(_S_bin[bin].mutex);
+
+ while (block != NULL)
+ {
+ if (_S_bin[bin].first[0] == NULL)
+ _S_bin[bin].first[0] = block;
+ else
+ _S_bin[bin].last[0]->next = block;
+
+ _S_bin[bin].last[0] = block;
+
+ block = block->next;
+
+ _S_bin[bin].free[0]++;
+ }
+
+ _S_bin[bin].last[0]->next = NULL;
+
+ __gthread_mutex_unlock(_S_bin[bin].mutex);
+ }
+ }
+
+ /*
+ * Return this thread id record to thread_freelist
+ */
+ __gthread_mutex_lock(&_S_thread_freelist_mutex);
+
+ _S_thread_freelist_last->next = (thread_record*)freelist_pos;
+ _S_thread_freelist_last = (thread_record*)freelist_pos;
+ _S_thread_freelist_last->next = NULL;
+
+ __gthread_mutex_unlock(&_S_thread_freelist_mutex);
+
+ }
+
+ template<int __inst>
+ size_t
+ __mt_alloc<__inst>::
+ _S_get_thread_id()
+ {
+ /*
+ * If we have thread support and it's active we check the thread
+ * key value and return it's id or if it's not set we take the
+ * first record from _S_thread_freelist and sets the key and
+ * returns it's id.
+ */
+ if (__gthread_active_p())
+ {
+ thread_record* freelist_pos;
+
+ if ((freelist_pos =
+ (thread_record*)__gthread_getspecific(_S_thread_key)) == NULL)
+ {
+ __gthread_mutex_lock(&_S_thread_freelist_mutex);
+
+ /*
+ * Since _S_max_threads must be larger than the
+ * theoretical max number of threads of the OS the list
+ * can never be empty.
+ */
+ freelist_pos = _S_thread_freelist_first;
+ _S_thread_freelist_first = _S_thread_freelist_first->next;
+
+ __gthread_mutex_unlock(&_S_thread_freelist_mutex);
+
+ __gthread_setspecific(_S_thread_key, (void*)freelist_pos);
+
+ /*
+ * Since thread_ids may/will be reused (espcially in
+ * producer/consumer applications) we make sure that the
+ * list pointers and free counter is reset BUT as the
+ * "old" thread may still be owner of some memory (which
+ * is referred to by other threads and thus not freed)
+ * we don't reset the used counter.
+ */
+ for (size_t bin = 0; bin < _S_no_of_bins; bin++)
+ {
+ _S_bin[bin].first[freelist_pos->id] = NULL;
+ _S_bin[bin].last[freelist_pos->id] = NULL;
+ _S_bin[bin].free[freelist_pos->id] = 0;
+ }
+ }
+
+ return freelist_pos->id;
+ }
+
+ /*
+ * Otherwise (no thread support or inactive) all requests are
+ * served from the global pool 0.
+ */
+ return 0;
+ }
+
+ template<int __inst> __gthread_once_t
+ __mt_alloc<__inst>::_S_once_mt = __GTHREAD_ONCE_INIT;
+#endif
+
+ template<int __inst> bool
+ __mt_alloc<__inst>::_S_initialized = false;
+
+ template<int __inst> typename __mt_alloc<__inst>::binmap_type*
+ __mt_alloc<__inst>::_S_binmap = NULL;
+
+ /*
+ * Allocation requests (after round-up to power of 2) below this
+ * value will be handled by the allocator. A raw malloc/free() call
+ * will be used for requests larger than this value.
+ */
+ template<int __inst> size_t
+ __mt_alloc<__inst>::_S_max_bytes = 128;
+
+ /*
+ * In order to avoid fragmenting and minimize the number of malloc()
+ * calls we always request new memory using this value. Based on
+ * previous discussions on the libstdc++ mailing list we have
+ * choosen the value below. See
+ * http://gcc.gnu.org/ml/libstdc++/2001-07/msg00077.html
+ */
+ template<int __inst> size_t
+ __mt_alloc<__inst>::_S_chunk_size = 4096 - 4 * sizeof(void*);
+
+ /*
+ * The maximum number of supported threads. Our Linux 2.4.18 reports
+ * 4070 in /proc/sys/kernel/threads-max
+ */
+ template<int __inst> size_t
+ __mt_alloc<__inst>::_S_max_threads = 4096;
+
+ /*
+ * Actual value calculated in _S_init()
+ */
+ template<int __inst> size_t
+ __mt_alloc<__inst>::_S_no_of_bins = 1;
+
+ /*
+ * Each time a deallocation occurs in a threaded application we make
+ * sure that there are no more than _S_freelist_headroom % of used
+ * memory on the freelist. If the number of additional records is
+ * more than _S_freelist_headroom % of the freelist, we move these
+ * records back to the global pool.
+ */
+ template<int __inst> size_t
+ __mt_alloc<__inst>::_S_freelist_headroom = 10;
+
+ /*
+ * Actual initialization in _S_init()
+ */
+#ifdef __GTHREADS
+ template<int __inst> typename __mt_alloc<__inst>::thread_record*
+ __mt_alloc<__inst>::_S_thread_freelist_first = NULL;
+
+ template<int __inst> typename __mt_alloc<__inst>::thread_record*
+ __mt_alloc<__inst>::_S_thread_freelist_last = NULL;
+
+ template<int __inst> __gthread_mutex_t
+ __mt_alloc<__inst>::_S_thread_freelist_mutex = __GTHREAD_MUTEX_INIT;
+
+ /*
+ * Actual initialization in _S_init()
+ */
+ template<int __inst> __gthread_key_t
+ __mt_alloc<__inst>::_S_thread_key;
+#endif
+
+ template<int __inst> typename __mt_alloc<__inst>::bin_record*
+ __mt_alloc<__inst>::_S_bin = NULL;
+
+ template<int __inst>
+ inline bool
+ operator==(const __mt_alloc<__inst>&, const __mt_alloc<__inst>&)
+ { return true; }
+
+ template<int __inst>
+ inline bool
+ operator!=(const __mt_alloc<__inst>&, const __mt_alloc<__inst>&)
+ { return false; }
+} // namespace __gnu_cxx
+
+namespace std
+{
+ template<typename _Tp, int __inst>
+ struct _Alloc_traits<_Tp, __gnu_cxx::__mt_alloc<__inst> >
+ {
+ static const bool _S_instanceless = true;
+ typedef __gnu_cxx:: __mt_alloc<__inst> base_alloc_type;
+ typedef __simple_alloc<_Tp, base_alloc_type> _Alloc_type;
+ typedef __allocator<_Tp, base_alloc_type> allocator_type;
+ };
+
+ template<typename _Tp, typename _Tp1, int __inst>
+ struct _Alloc_traits<_Tp,
+ __allocator<_Tp1, __gnu_cxx::__mt_alloc<__inst> > >
+ {
+ static const bool _S_instanceless = true;
+ typedef __gnu_cxx:: __mt_alloc<__inst> base_alloc_type;
+ typedef __simple_alloc<_Tp, base_alloc_type> _Alloc_type;
+ typedef __allocator<_Tp, base_alloc_type> allocator_type;
+ };
+} // namespace std
+
+#endif
+