From 2f9f6cef1cdc149a05754839ca27af001c8d3d1e Mon Sep 17 00:00:00 2001
From: Benjamin Kosnik <bkoz@gcc.gnu.org>
Date: Mon, 12 Sep 2005 04:49:11 +0000
Subject: [PATCH] [multiple changes]

2005-09-11  Benjamin Kosnik  <bkoz@redhat.com>

	PR libstdc++/19265
	PR libstdc++/22309
	* include/ext/mt_allocator.h
	(__gnu_cxx::__create_handler): Remove.
	(__pool<true>::_M_destroy_thread_key): Compatibility only.
	(__pool<true>::_M_initialize(__destroy): Same.
	(__pool<true>::_M_initialize): New.
	(__pool<true>::_M_initialize_once): Nothing fancy.
	(__pool<true>::_M_once): Remove.
	(__common_pool): New.
	(__common_pool_base): New.
	(__per_type_pool): New.
	(__per_type_pool_base): New.
	* src/mt_allocator.cc: Same.
	* config/linker-map.gnu (__pool<true>::_M_initialize()): Add.

2005-09-11  Jakub Jelinek  <jakub@redhat.com>

	PR libstdc++/19265
	PR libstdc++/22309
	* src/mt_allocator.cc (__gnu_internal::freelist_mutex): Make static.
	(__gnu_internal::__freelist): New type.
	(__gnu_internal::freelist): New variable.
	(__gnu_internal::_M_destroy_thread_key): New function.
	(__gnu_cxx::__pool<true>::_M_destroy): Don't delete
	_M_thread_freelist_initial.
	(__gnu_cxx::__pool<true>::_M_initialize): Make argument nameless.
	Don't use _M_thread_freelist and _M_thread_freelist_initial
	__pool<true> fields, instead use __gnu_internal::freelist fields, call
	gthread_key_create just once.  Use
	__gnu_internal::_M_destroy_thread_key as key destructor.
	(__gnu_cxx::__pool<true>::_M_get_thread_id): Store size_t id
	rather than _Thread_record* in the thread specific value.  Don't
	use _M_thread_freelist __pool<true> field, instead use
	__gnu_internal::freelist fields.
	(__gnu_cxx::__pool<true>::_M_destroy_thread_key): Do nothing.

2005-09-11  Benjamin Kosnik  <bkoz@redhat.com>
            Jakub Jelinek  <jakub@redhat.com>

	PR libstdc++/19265
	PR libstdc++/22309
	* testsuite/testsuite_shared.cc: New.
	* testsuite/lib/dg-options.exp (dg-require-sharedlib): New.
	* testsuite/lib/libstdc++.exp (libstdc++_init): Look for shared
	library, and set v3-sharedlib based on this.
	(check_v3_target_sharedlib): New.
	(proc v3-build_support): Build shared objects.
	* testsuite/ext/mt_allocator/22309_thread.cc: New, use above.

From-SVN: r104161
---
 libstdc++-v3/ChangeLog                        |  52 +++
 libstdc++-v3/config/linker-map.gnu            |   6 +-
 libstdc++-v3/include/ext/mt_allocator.h       | 238 ++++++-------
 libstdc++-v3/src/mt_allocator.cc              | 327 +++++++++++++++---
 .../ext/mt_allocator/22309_thread.cc          | 101 ++++++
 libstdc++-v3/testsuite/lib/dg-options.exp     |   9 +
 libstdc++-v3/testsuite/lib/libstdc++.exp      |  67 +++-
 libstdc++-v3/testsuite/testsuite_shared.cc    |  36 ++
 8 files changed, 633 insertions(+), 203 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/ext/mt_allocator/22309_thread.cc
 create mode 100644 libstdc++-v3/testsuite/testsuite_shared.cc

diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index ad1fb5d54131..cb6c84cc9b88 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,55 @@
+2005-09-11  Benjamin Kosnik  <bkoz@redhat.com>
+
+	PR libstdc++/19265
+	PR libstdc++/22309
+	* include/ext/mt_allocator.h
+	(__gnu_cxx::__create_handler): Remove.
+	(__pool<true>::_M_destroy_thread_key): Compatibility only.
+	(__pool<true>::_M_initialize(__destroy): Same.
+	(__pool<true>::_M_initialize): New.
+	(__pool<true>::_M_initialize_once): Nothing fancy.
+	(__pool<true>::_M_once): Remove.
+	(__common_pool): New.
+	(__common_pool_base): New.	
+	(__per_type_pool): New.
+	(__per_type_pool_base): New.
+	* src/mt_allocator.cc: Same.
+	* config/linker-map.gnu (__pool<true>::_M_initialize()): Add.
+	
+2005-09-11  Jakub Jelinek  <jakub@redhat.com>
+
+	PR libstdc++/19265
+	PR libstdc++/22309
+	* src/mt_allocator.cc (__gnu_internal::freelist_mutex): Make static.
+	(__gnu_internal::__freelist): New type.
+	(__gnu_internal::freelist): New variable.
+	(__gnu_internal::_M_destroy_thread_key): New function.
+	(__gnu_cxx::__pool<true>::_M_destroy): Don't delete
+	_M_thread_freelist_initial.
+	(__gnu_cxx::__pool<true>::_M_initialize): Make argument nameless.
+	Don't use _M_thread_freelist and _M_thread_freelist_initial
+	__pool<true> fields, instead use __gnu_internal::freelist fields, call
+	gthread_key_create just once.  Use
+	__gnu_internal::_M_destroy_thread_key as key destructor.	
+	(__gnu_cxx::__pool<true>::_M_get_thread_id): Store size_t id
+	rather than _Thread_record* in the thread specific value.  Don't
+	use _M_thread_freelist __pool<true> field, instead use
+	__gnu_internal::freelist fields.
+	(__gnu_cxx::__pool<true>::_M_destroy_thread_key): Do nothing.
+
+2005-09-11  Benjamin Kosnik  <bkoz@redhat.com>
+            Jakub Jelinek  <jakub@redhat.com>
+
+	PR libstdc++/19265
+	PR libstdc++/22309	
+	* testsuite/testsuite_shared.cc: New.
+	* testsuite/lib/dg-options.exp (dg-require-sharedlib): New.
+	* testsuite/lib/libstdc++.exp (libstdc++_init): Look for shared
+	library, and set v3-sharedlib based on this.
+	(check_v3_target_sharedlib): New.
+	(proc v3-build_support): Build shared objects.
+	* testsuite/ext/mt_allocator/22309_thread.cc: New, use above.
+
 2005-09-11  Paolo Carlini  <pcarlini@suse.de>
 
 	PR libstdc++/23781
diff --git a/libstdc++-v3/config/linker-map.gnu b/libstdc++-v3/config/linker-map.gnu
index 3099bfb68171..eb8fab60c4c8 100644
--- a/libstdc++-v3/config/linker-map.gnu
+++ b/libstdc++-v3/config/linker-map.gnu
@@ -515,8 +515,8 @@ GLIBCXX_3.4.3 {
 } GLIBCXX_3.4.2;
 
 GLIBCXX_3.4.4 {
- 
-   _ZN9__gnu_cxx6__poolILb0EE13_M_initializeEv;
+
+    _ZN9__gnu_cxx6__poolILb0EE13_M_initializeEv;
     _ZN9__gnu_cxx6__poolILb1EE13_M_initializeEPFvPvE;
     _ZN9__gnu_cxx6__poolILb1EE21_M_destroy_thread_keyEPv;
     _ZN9__gnu_cxx6__poolILb1EE16_M_get_thread_idEv;
@@ -571,6 +571,8 @@ GLIBCXX_3.4.6 {
 
     _ZNKSt15basic_stringbufIwSt11char_traitsIwESaIwEE3strEv;
 
+   _ZN9__gnu_cxx6__poolILb1EE13_M_initializeEv;
+
 } GLIBCXX_3.4.5;
 
 # Symbols in the support library (libsupc++) have their own tag.
diff --git a/libstdc++-v3/include/ext/mt_allocator.h b/libstdc++-v3/include/ext/mt_allocator.h
index b2cf89b562cd..311e219aaee5 100644
--- a/libstdc++-v3/include/ext/mt_allocator.h
+++ b/libstdc++-v3/include/ext/mt_allocator.h
@@ -43,7 +43,6 @@
 namespace __gnu_cxx
 {
   typedef void (*__destroy_handler)(void*);
-  typedef void (*__create_handler)(void);
 
   /// @brief  Base class for pool object.
   struct __pool_base
@@ -185,12 +184,6 @@ namespace __gnu_cxx
   template<bool _Thread>
     class __pool;
 
-  template<>
-    class __pool<true>;
-
-  template<>
-    class __pool<false>;
-
   /// Specialization for single thread.
   template<>
     class __pool<false> : public __pool_base
@@ -313,24 +306,15 @@ namespace __gnu_cxx
 	__gthread_mutex_t*              _M_mutex;
       };
       
+      // XXX GLIBCXX_ABI Deprecated
       void
-      _M_initialize(__destroy_handler __d);
+      _M_initialize(__destroy_handler);
 
       void
-      _M_initialize_once(__create_handler __c)
+      _M_initialize_once()
       {
-	// 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 (__builtin_expect(_M_init == false, false))
-	  {
-	    if (__gthread_active_p())
-	      __gthread_once(&_M_once, __c);
-	    if (!_M_init)
-	      __c();
-	  }
+	  _M_initialize();
       }
 
       void
@@ -358,28 +342,21 @@ namespace __gnu_cxx
 	  }
       }
 
+      // XXX GLIBCXX_ABI Deprecated
       void 
-      _M_destroy_thread_key(void* __freelist_pos);
+      _M_destroy_thread_key(void*);
 
       size_t 
       _M_get_thread_id();
 
       explicit __pool() 
       : _M_bin(NULL), _M_bin_size(1), _M_thread_freelist(NULL) 
-      {
-	// On some platforms, __gthread_once_t is an aggregate.
-	__gthread_once_t __tmp = __GTHREAD_ONCE_INIT;
-	_M_once = __tmp;
-      }
+      { }
 
       explicit __pool(const __pool_base::_Tune& __tune) 
       : __pool_base(__tune), _M_bin(NULL), _M_bin_size(1), 
       _M_thread_freelist(NULL) 
-      {
-	// On some platforms, __gthread_once_t is an aggregate.
-	__gthread_once_t __tmp = __GTHREAD_ONCE_INIT;
-	_M_once = __tmp;
-      }
+      { }
 
     private:
       // An "array" of bin_records each of which represents a specific
@@ -390,39 +367,39 @@ namespace __gnu_cxx
       // Actual value calculated in _M_initialize().
       size_t 	       	     	_M_bin_size;
 
-      __gthread_once_t 		_M_once;
-      
       _Thread_record* 		_M_thread_freelist;
       void*			_M_thread_freelist_initial;
+
+      void
+      _M_initialize();
     };
 #endif
 
-
-  /// @brief  Policy for shared __pool objects.
   template<template <bool> class _PoolTp, bool _Thread>
-    struct __common_pool_policy;
-
-  /// Partial specialization for single thread.
-  template<template <bool> class _PoolTp>
-    struct __common_pool_policy<_PoolTp, false>
+    struct __common_pool
     {
-      typedef _PoolTp<false> pool_type;
+      typedef _PoolTp<_Thread> 		pool_type;
       
-      template<typename _Tp1, template <bool> class _PoolTp1 = _PoolTp, 
-	       bool _Thread1 = false>
-        struct _M_rebind
-        { typedef __common_pool_policy<_PoolTp1, _Thread1> other; };
-
       static pool_type&
       _S_get_pool()
       { 
 	static pool_type _S_pool;
 	return _S_pool;
       }
+    };
+
+  template<template <bool> class _PoolTp, bool _Thread>
+    struct __common_pool_base;
+
+  template<template <bool> class _PoolTp>
+    struct __common_pool_base<_PoolTp, false> 
+    : public __common_pool<_PoolTp, false>
+    {
+      using  __common_pool<_PoolTp, false>::_S_get_pool;
 
       static void
-      _S_initialize_once() 
-      { 
+      _S_initialize_once()
+      {
 	static bool __init;
 	if (__builtin_expect(__init == false, false))
 	  {
@@ -433,76 +410,67 @@ namespace __gnu_cxx
     };
 
 #ifdef __GTHREADS
-  /// Partial specialization for thread enabled, via gthreads.h.
   template<template <bool> class _PoolTp>
-    struct __common_pool_policy<_PoolTp, true>
+    struct __common_pool_base<_PoolTp, true>
+    : public __common_pool<_PoolTp, true>
     {
-      typedef _PoolTp<true> pool_type;
+      using  __common_pool<_PoolTp, true>::_S_get_pool;
       
-      template<typename _Tp1, template <bool> class _PoolTp1 = _PoolTp, 
-	       bool _Thread1 = true>
-        struct _M_rebind
-        { typedef __common_pool_policy<_PoolTp1, _Thread1> other; };
-
-      static pool_type&
-      _S_get_pool()
-      { 
-	static pool_type _S_pool;
-	return _S_pool;
-      }
+      static void
+      _S_initialize() 
+      { _S_get_pool()._M_initialize_once(); }
 
       static void
-      _S_initialize_once() 
+      _S_initialize_once()
       { 
 	static bool __init;
 	if (__builtin_expect(__init == false, false))
 	  {
-	    _S_get_pool()._M_initialize_once(_S_initialize); 
+	    if (__gthread_active_p())
+	      {
+		// On some platforms, __gthread_once_t is an aggregate.
+		static __gthread_once_t __once = __GTHREAD_ONCE_INIT;
+		__gthread_once(&__once, _S_initialize);
+	      }
+	    else
+	      _S_get_pool()._M_initialize_once(); 
 	    __init = true;
 	  }
       }
-
-    private:
-      static void
-      _S_destroy_thread_key(void* __freelist_pos)
-      { _S_get_pool()._M_destroy_thread_key(__freelist_pos); }
-      
-      static void
-      _S_initialize() 
-      { _S_get_pool()._M_initialize(_S_destroy_thread_key); }
-   };
+    };
 #endif
 
- 
-  /// @brief  Policy for individual __pool objects.
-  template<typename _Tp, template <bool> class _PoolTp, bool _Thread>
-    struct __per_type_pool_policy;
-
-  /// Partial specialization for single thread.
-  template<typename _Tp, template <bool> class _PoolTp>
-    struct __per_type_pool_policy<_Tp, _PoolTp, false>
+  /// @brief  Policy for shared __pool objects.
+  template<template <bool> class _PoolTp, bool _Thread>
+    struct __common_pool_policy : public __common_pool_base<_PoolTp, _Thread>
     {
-      typedef _Tp value_type;
-      typedef _PoolTp<false> pool_type;
-
       template<typename _Tp1, template <bool> class _PoolTp1 = _PoolTp, 
-	       bool _Thread1 = false>
+	       bool _Thread1 = _Thread>
         struct _M_rebind
-        { typedef __per_type_pool_policy<_Tp1, _PoolTp1, _Thread1> other; };
+        { typedef __common_pool_policy<_PoolTp1, _Thread1> other; };
 
+      using  __common_pool_base<_PoolTp, _Thread>::_S_get_pool;
+      using  __common_pool_base<_PoolTp, _Thread>::_S_initialize_once;
+  };
+ 
+
+  template<typename _Tp, template <bool> class _PoolTp, bool _Thread>
+    struct __per_type_pool
+    {
+      typedef _Tp 			value_type;
+      typedef _PoolTp<_Thread> 		pool_type;
+      
       static pool_type&
       _S_get_pool()
       { 
 	// Sane defaults for the _PoolTp.
 	typedef typename pool_type::_Block_record _Block_record;
-	const static size_t __align = (__alignof__(_Tp) >= sizeof(_Block_record)
-				       ? __alignof__(_Tp)
-				       : sizeof(_Block_record));
+	const static size_t __a = (__alignof__(_Tp) >= sizeof(_Block_record)
+				   ? __alignof__(_Tp) : sizeof(_Block_record));
 
 	typedef typename __pool_base::_Tune _Tune;
-	static _Tune _S_tune(__align, sizeof(_Tp) * 64,
-			     sizeof(_Tp) * 2 >= __align ? sizeof(_Tp) * 2
-			                                : __align,
+	static _Tune _S_tune(__a, sizeof(_Tp) * 64,
+			     sizeof(_Tp) * 2 >= __a ? sizeof(_Tp) * 2 : __a,
 			     sizeof(_Tp) * size_t(_Tune::_S_chunk_size),
 			     _Tune::_S_max_threads,
 			     _Tune::_S_freelist_headroom,
@@ -510,10 +478,20 @@ namespace __gnu_cxx
 	static pool_type _S_pool(_S_tune);
 	return _S_pool;
       }
+    };
+
+  template<typename _Tp, template <bool> class _PoolTp, bool _Thread>
+    struct __per_type_pool_base;
+
+  template<typename _Tp, template <bool> class _PoolTp>
+    struct __per_type_pool_base<_Tp, _PoolTp, false> 
+    : public __per_type_pool<_Tp, _PoolTp, false> 
+    {
+      using  __per_type_pool<_Tp, _PoolTp, false>::_S_get_pool;
 
       static void
       _S_initialize_once()
-      { 
+      {
 	static bool __init;
 	if (__builtin_expect(__init == false, false))
 	  {
@@ -523,39 +501,16 @@ namespace __gnu_cxx
       }
     };
 
-#ifdef __GTHREADS
-  /// Partial specialization for thread enabled, via gthreads.h.
-  template<typename _Tp, template <bool> class _PoolTp>
-    struct __per_type_pool_policy<_Tp, _PoolTp, true>
+ #ifdef __GTHREADS
+ template<typename _Tp, template <bool> class _PoolTp>
+    struct __per_type_pool_base<_Tp, _PoolTp, true> 
+    : public __per_type_pool<_Tp, _PoolTp, true> 
     {
-      typedef _Tp value_type;
-      typedef _PoolTp<true> pool_type;
+      using  __per_type_pool<_Tp, _PoolTp, true>::_S_get_pool;
 
-     template<typename _Tp1, template <bool> class _PoolTp1 = _PoolTp, 
-	       bool _Thread1 = true>
-        struct _M_rebind
-        { typedef __per_type_pool_policy<_Tp1, _PoolTp1, _Thread1> other; };
-
-      static pool_type&
-      _S_get_pool()
-      { 
-	// Sane defaults for the _PoolTp.
-	typedef typename pool_type::_Block_record _Block_record;
-	const static size_t __align = (__alignof__(_Tp) >= sizeof(_Block_record)
-				       ? __alignof__(_Tp)
-				       : sizeof(_Block_record));
-
-	typedef typename __pool_base::_Tune _Tune;
-	static _Tune _S_tune(__align, sizeof(_Tp) * 64,
-			     sizeof(_Tp) * 2 >= __align ? sizeof(_Tp) * 2
-                                                        : __align,
-			     sizeof(_Tp) * size_t(_Tune::_S_chunk_size),
-			     _Tune::_S_max_threads,
-			     _Tune::_S_freelist_headroom,
-			     getenv("GLIBCXX_FORCE_NEW") ? true : false);
-	static pool_type _S_pool(_S_tune);
-	return _S_pool;
-      }
+      static void
+      _S_initialize() 
+      { _S_get_pool()._M_initialize_once(); }
 
       static void
       _S_initialize_once()
@@ -563,22 +518,35 @@ namespace __gnu_cxx
 	static bool __init;
 	if (__builtin_expect(__init == false, false))
 	  {
-	    _S_get_pool()._M_initialize_once(_S_initialize); 
+	    if (__gthread_active_p())
+	      {
+		// On some platforms, __gthread_once_t is an aggregate.
+		static __gthread_once_t __once = __GTHREAD_ONCE_INIT;
+		__gthread_once(&__once, _S_initialize);
+	      }
+	    else
+	      _S_get_pool()._M_initialize_once(); 
 	    __init = true;
 	  }
       }
-
-    private:
-      static void
-      _S_destroy_thread_key(void* __freelist_pos)
-      { _S_get_pool()._M_destroy_thread_key(__freelist_pos); }
-      
-      static void
-      _S_initialize() 
-      { _S_get_pool()._M_initialize(_S_destroy_thread_key); }
     };
 #endif
 
+  /// @brief  Policy for individual __pool objects.
+  template<typename _Tp, template <bool> class _PoolTp, bool _Thread>
+    struct __per_type_pool_policy 
+    : public __per_type_pool_base<_Tp, _PoolTp, _Thread>
+    {
+      template<typename _Tp1, template <bool> class _PoolTp1 = _PoolTp, 
+	       bool _Thread1 = _Thread>
+        struct _M_rebind
+        { typedef __per_type_pool_policy<_Tp1, _PoolTp1, _Thread1> other; };
+
+      using  __per_type_pool_base<_Tp, _PoolTp, _Thread>::_S_get_pool;
+      using  __per_type_pool_base<_Tp, _PoolTp, _Thread>::_S_initialize_once;
+  };
+
+
   /// @brief  Base class for _Tp dependent member functions.
   template<typename _Tp>
     class __mt_alloc_base 
diff --git a/libstdc++-v3/src/mt_allocator.cc b/libstdc++-v3/src/mt_allocator.cc
index 87161915c545..1f4ec3ff1e00 100644
--- a/libstdc++-v3/src/mt_allocator.cc
+++ b/libstdc++-v3/src/mt_allocator.cc
@@ -1,8 +1,8 @@
 // Allocator details.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
-// This file is part of the GNU ISO C++ Librarbooly.  This library is free
+// 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)
@@ -37,10 +37,42 @@
 
 namespace __gnu_internal
 {
+#ifdef __GTHREADS
+  struct __freelist
+  {
+    typedef __gnu_cxx::__pool<true>::_Thread_record _Thread_record;
+    _Thread_record* 	_M_thread_freelist;
+    _Thread_record* 	_M_thread_freelist_array;
+    size_t 		_M_max_threads;
+    __gthread_key_t 	_M_key;
+
+    ~__freelist()
+    {
+      if (_M_thread_freelist_array)
+	{
+	  __gthread_key_delete(_M_key);
+	  ::operator delete(static_cast<void*>(_M_thread_freelist_array));
+	}
+    }
+  };
+
+  // Ensure freelist is constructed first.
+  static __freelist freelist;
   static __glibcxx_mutex_define_initialized(freelist_mutex);
 
-#ifdef __GTHREADS
-  __gthread_key_t freelist_key;
+  static void 
+  _M_destroy_thread_key(void* __id)
+  {
+    // Return this thread id record to front of thread_freelist.
+    __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);
+    size_t _M_id = reinterpret_cast<size_t>(__id);
+
+    using namespace __gnu_internal;
+    typedef __gnu_cxx::__pool<true>::_Thread_record _Thread_record;
+    _Thread_record* __tr = &freelist._M_thread_freelist_array[_M_id - 1];
+    __tr->_M_next = freelist._M_thread_freelist;
+    freelist._M_thread_freelist = __tr;
+  }
 #endif
 }
 
@@ -171,6 +203,7 @@ namespace __gnu_cxx
       }
     _M_init = true;
   }
+
   
 #ifdef __GTHREADS
   void
@@ -194,7 +227,6 @@ namespace __gnu_cxx
 		::operator delete(__bin._M_used);
 		::operator delete(__bin._M_mutex);
 	      }
-	    ::operator delete(_M_thread_freelist_initial);
 	  }
 	else
 	  {
@@ -386,8 +418,8 @@ namespace __gnu_cxx
     return reinterpret_cast<char*>(__block) + __options._M_align;
   }
 
- void
-  __pool<true>::_M_initialize(__destroy_handler __d)
+  void
+  __pool<true>::_M_initialize()
   {
     // _M_force_new must not change after the first allocate(),
     // which in turn calls this method, so if it's false, it's false
@@ -397,7 +429,7 @@ namespace __gnu_cxx
 	_M_init = true;
 	return;
       }
-      
+
     // Create the bins.
     // Calculate the number of bins required based on _M_max_bytes.
     // _M_bin_size is statically-initialized to one.
@@ -433,29 +465,70 @@ namespace __gnu_cxx
     // directly and have no need for this.
     if (__gthread_active_p())
       {
-	const size_t __k = sizeof(_Thread_record) * _M_options._M_max_threads;
-	__v = ::operator new(__k);
-	_M_thread_freelist = static_cast<_Thread_record*>(__v);
-	_M_thread_freelist_initial = __v;
-	  
-	// NOTE! The first assignable thread id is 1 since the
-	// global pool uses id 0
-	size_t __i;
-	for (__i = 1; __i < _M_options._M_max_threads; ++__i)
-	  {
-	    _Thread_record& __tr = _M_thread_freelist[__i - 1];
-	    __tr._M_next = &_M_thread_freelist[__i];
-	    __tr._M_id = __i;
-	  }
-	  
-	// Set last record.
-	_M_thread_freelist[__i - 1]._M_next = NULL;
-	_M_thread_freelist[__i - 1]._M_id = __i;
-	  
-	// Initialize per thread key to hold pointer to
-	// _M_thread_freelist.
-	__gthread_key_create(&__gnu_internal::freelist_key, __d);
-	  
+	{
+	  __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);
+
+	  if (!__gnu_internal::freelist._M_thread_freelist_array
+	      || __gnu_internal::freelist._M_max_threads
+		 < _M_options._M_max_threads)
+	    {
+	      const size_t __k = sizeof(_Thread_record)
+				 * _M_options._M_max_threads;
+	      __v = ::operator new(__k);
+	      _Thread_record* _M_thread_freelist
+		= static_cast<_Thread_record*>(__v);
+
+	      // NOTE! The first assignable thread id is 1 since the
+	      // global pool uses id 0
+	      size_t __i;
+	      for (__i = 1; __i < _M_options._M_max_threads; ++__i)
+		{
+		  _Thread_record& __tr = _M_thread_freelist[__i - 1];
+		  __tr._M_next = &_M_thread_freelist[__i];
+		  __tr._M_id = __i;
+		}
+
+	      // Set last record.
+	      _M_thread_freelist[__i - 1]._M_next = NULL;
+	      _M_thread_freelist[__i - 1]._M_id = __i;
+
+	      if (!__gnu_internal::freelist._M_thread_freelist_array)
+		{
+		  // Initialize per thread key to hold pointer to
+		  // _M_thread_freelist.
+		  __gthread_key_create(&__gnu_internal::freelist._M_key,
+				       __gnu_internal::_M_destroy_thread_key);
+		  __gnu_internal::freelist._M_thread_freelist
+		    = _M_thread_freelist;
+		}
+	      else
+		{
+		  _Thread_record* _M_old_freelist
+		    = __gnu_internal::freelist._M_thread_freelist;
+		  _Thread_record* _M_old_array
+		    = __gnu_internal::freelist._M_thread_freelist_array;
+		  __gnu_internal::freelist._M_thread_freelist
+		    = &_M_thread_freelist[_M_old_freelist - _M_old_array];
+		  while (_M_old_freelist)
+		    {
+		      size_t next_id;
+		      if (_M_old_freelist->_M_next)
+			next_id = _M_old_freelist->_M_next - _M_old_array;
+		      else
+			next_id = __gnu_internal::freelist._M_max_threads;
+		      _M_thread_freelist[_M_old_freelist->_M_id - 1]._M_next
+			= &_M_thread_freelist[next_id];
+		      _M_old_freelist = _M_old_freelist->_M_next;
+		    }
+		  ::operator delete(static_cast<void*>(_M_old_array));
+		}
+	      __gnu_internal::freelist._M_thread_freelist_array
+		= _M_thread_freelist;
+	      __gnu_internal::freelist._M_max_threads
+		= _M_options._M_max_threads;
+	    }
+	}
+
 	const size_t __max_threads = _M_options._M_max_threads + 1;
 	for (size_t __n = 0; __n < _M_bin_size; ++__n)
 	  {
@@ -514,23 +587,24 @@ namespace __gnu_cxx
     // returns it's id.
     if (__gthread_active_p())
       {
-	void* v = __gthread_getspecific(__gnu_internal::freelist_key);
-	_Thread_record* __freelist_pos = static_cast<_Thread_record*>(v); 
-	if (__freelist_pos == NULL)
+	void* v = __gthread_getspecific(__gnu_internal::freelist._M_key);
+	size_t _M_id = (size_t)v;
+	if (_M_id == 0)
 	  {
-	    // Since _M_options._M_max_threads must be larger than
-	    // the theoretical max number of threads of the OS the
-	    // list can never be empty.
 	    {
 	      __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);
-	      __freelist_pos = _M_thread_freelist;
-	      _M_thread_freelist = _M_thread_freelist->_M_next;
+	      if (__gnu_internal::freelist._M_thread_freelist)
+		{
+		  _M_id = __gnu_internal::freelist._M_thread_freelist->_M_id;
+		  __gnu_internal::freelist._M_thread_freelist
+		    = __gnu_internal::freelist._M_thread_freelist->_M_next;
+		}
 	    }
-	      
-	    __gthread_setspecific(__gnu_internal::freelist_key, 
-				  static_cast<void*>(__freelist_pos));
+
+	    __gthread_setspecific(__gnu_internal::freelist._M_key,
+				  (void*)_M_id);
 	  }
-	return __freelist_pos->_M_id;
+	return _M_id >= _M_options._M_max_threads ? 0 : _M_id;
       }
 
     // Otherwise (no thread support or inactive) all requests are
@@ -538,14 +612,169 @@ namespace __gnu_cxx
     return 0;
   }
 
+  // XXX GLIBCXX_ABI Deprecated
+  void 
+  __pool<true>::_M_destroy_thread_key(void*) { }
+
+  // XXX GLIBCXX_ABI Deprecated
   void
-  __pool<true>::_M_destroy_thread_key(void* __freelist_pos)
+  __pool<true>::_M_initialize(__destroy_handler)
   {
-    // Return this thread id record to front of thread_freelist.
-    __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);
-    _Thread_record* __tr = static_cast<_Thread_record*>(__freelist_pos);
-    __tr->_M_next = _M_thread_freelist; 
-    _M_thread_freelist = __tr;
+    // _M_force_new must not change after the first allocate(),
+    // which in turn calls this method, so if it's false, it's false
+    // forever and we don't need to return here ever again.
+    if (_M_options._M_force_new) 
+      {
+	_M_init = true;
+	return;
+      }
+
+    // Create the bins.
+    // Calculate the number of bins required based on _M_max_bytes.
+    // _M_bin_size is statically-initialized to one.
+    size_t __bin_size = _M_options._M_min_bin;
+    while (_M_options._M_max_bytes > __bin_size)
+      {
+	__bin_size <<= 1;
+	++_M_bin_size;
+      }
+      
+    // Setup the bin map for quick lookup of the relevant bin.
+    const size_t __j = (_M_options._M_max_bytes + 1) * sizeof(_Binmap_type);
+    _M_binmap = static_cast<_Binmap_type*>(::operator new(__j));
+    _Binmap_type* __bp = _M_binmap;
+    _Binmap_type __bin_max = _M_options._M_min_bin;
+    _Binmap_type __bint = 0;
+    for (_Binmap_type __ct = 0; __ct <= _M_options._M_max_bytes; ++__ct)
+      {
+	if (__ct > __bin_max)
+	  {
+	    __bin_max <<= 1;
+	    ++__bint;
+	  }
+	*__bp++ = __bint;
+      }
+      
+    // Initialize _M_bin and its members.
+    void* __v = ::operator new(sizeof(_Bin_record) * _M_bin_size);
+    _M_bin = static_cast<_Bin_record*>(__v);
+      
+    // 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.
+    if (__gthread_active_p())
+      {
+	{
+	  __gnu_cxx::lock sentry(__gnu_internal::freelist_mutex);
+
+	  if (!__gnu_internal::freelist._M_thread_freelist_array
+	      || __gnu_internal::freelist._M_max_threads
+		 < _M_options._M_max_threads)
+	    {
+	      const size_t __k = sizeof(_Thread_record)
+				 * _M_options._M_max_threads;
+	      __v = ::operator new(__k);
+	      _Thread_record* _M_thread_freelist
+		= static_cast<_Thread_record*>(__v);
+
+	      // NOTE! The first assignable thread id is 1 since the
+	      // global pool uses id 0
+	      size_t __i;
+	      for (__i = 1; __i < _M_options._M_max_threads; ++__i)
+		{
+		  _Thread_record& __tr = _M_thread_freelist[__i - 1];
+		  __tr._M_next = &_M_thread_freelist[__i];
+		  __tr._M_id = __i;
+		}
+
+	      // Set last record.
+	      _M_thread_freelist[__i - 1]._M_next = NULL;
+	      _M_thread_freelist[__i - 1]._M_id = __i;
+
+	      if (!__gnu_internal::freelist._M_thread_freelist_array)
+		{
+		  // Initialize per thread key to hold pointer to
+		  // _M_thread_freelist.
+		  __gthread_key_create(&__gnu_internal::freelist._M_key,
+				       __gnu_internal::_M_destroy_thread_key);
+		  __gnu_internal::freelist._M_thread_freelist
+		    = _M_thread_freelist;
+		}
+	      else
+		{
+		  _Thread_record* _M_old_freelist
+		    = __gnu_internal::freelist._M_thread_freelist;
+		  _Thread_record* _M_old_array
+		    = __gnu_internal::freelist._M_thread_freelist_array;
+		  __gnu_internal::freelist._M_thread_freelist
+		    = &_M_thread_freelist[_M_old_freelist - _M_old_array];
+		  while (_M_old_freelist)
+		    {
+		      size_t next_id;
+		      if (_M_old_freelist->_M_next)
+			next_id = _M_old_freelist->_M_next - _M_old_array;
+		      else
+			next_id = __gnu_internal::freelist._M_max_threads;
+		      _M_thread_freelist[_M_old_freelist->_M_id - 1]._M_next
+			= &_M_thread_freelist[next_id];
+		      _M_old_freelist = _M_old_freelist->_M_next;
+		    }
+		  ::operator delete(static_cast<void*>(_M_old_array));
+		}
+	      __gnu_internal::freelist._M_thread_freelist_array
+		= _M_thread_freelist;
+	      __gnu_internal::freelist._M_max_threads
+		= _M_options._M_max_threads;
+	    }
+	}
+
+	const size_t __max_threads = _M_options._M_max_threads + 1;
+	for (size_t __n = 0; __n < _M_bin_size; ++__n)
+	  {
+	    _Bin_record& __bin = _M_bin[__n];
+	    __v = ::operator new(sizeof(_Block_record*) * __max_threads);
+	    __bin._M_first = static_cast<_Block_record**>(__v);
+
+	    __bin._M_address = NULL;
+
+	    __v = ::operator new(sizeof(size_t) * __max_threads);
+	    __bin._M_free = static_cast<size_t*>(__v);
+	      
+	    __v = ::operator new(sizeof(size_t) * __max_threads);
+	    __bin._M_used = static_cast<size_t*>(__v);
+	      
+	    __v = ::operator new(sizeof(__gthread_mutex_t));
+	    __bin._M_mutex = static_cast<__gthread_mutex_t*>(__v);
+	      
+#ifdef __GTHREAD_MUTEX_INIT
+	    {
+	      // Do not copy a POSIX/gthr mutex once in use.
+	      __gthread_mutex_t __tmp = __GTHREAD_MUTEX_INIT;
+	      *__bin._M_mutex = __tmp;
+	    }
+#else
+	    { __GTHREAD_MUTEX_INIT_FUNCTION(__bin._M_mutex); }
+#endif
+	    for (size_t __threadn = 0; __threadn < __max_threads; ++__threadn)
+	      {
+		__bin._M_first[__threadn] = NULL;
+		__bin._M_free[__threadn] = 0;
+		__bin._M_used[__threadn] = 0;
+	      }
+	  }
+      }
+    else
+      {
+	for (size_t __n = 0; __n < _M_bin_size; ++__n)
+	  {
+	    _Bin_record& __bin = _M_bin[__n];
+	    __v = ::operator new(sizeof(_Block_record*));
+	    __bin._M_first = static_cast<_Block_record**>(__v);
+	    __bin._M_first[0] = NULL;
+	    __bin._M_address = NULL;
+	  }
+      }
+    _M_init = true;
   }
 #endif
 
diff --git a/libstdc++-v3/testsuite/ext/mt_allocator/22309_thread.cc b/libstdc++-v3/testsuite/ext/mt_allocator/22309_thread.cc
new file mode 100644
index 000000000000..ce4024eb8768
--- /dev/null
+++ b/libstdc++-v3/testsuite/ext/mt_allocator/22309_thread.cc
@@ -0,0 +1,101 @@
+// { dg-require-sharedlib "" }
+// { dg-do run { target *-*-linux* } }
+// { dg-options "-g -O2 -pthread -ldl" { target *-*-linux* } }
+
+// Copyright (C) 2004, 2005 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+// USA.
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <cstdlib>
+#include <stdexcept>
+
+void
+check_dlopen(void*& h)
+{
+  dlerror();
+  void* tmp = dlopen("./testsuite_shared.so", RTLD_LAZY);
+  if (!tmp) 
+    {
+      try 
+	{
+	  // Throws std::logic_error on NULL string.
+	  std::string error(dlerror());
+	  throw std::runtime_error(error);
+	}
+      catch (const std::logic_error&)
+	{ }
+    }
+  h = tmp;
+}
+
+void
+check_dlsym(void*& h)
+{
+  dlerror();
+
+  typedef void (*function_type) (void);
+  function_type fn;
+  fn = reinterpret_cast<function_type>(dlsym(h, "foo"));
+
+  try 
+    {
+      std::string error(dlerror());
+      throw std::runtime_error(error);
+    }
+  catch (const std::logic_error&)
+    { }
+
+  fn();
+}
+
+void
+check_dlclose(void*& h)
+{
+  dlerror();
+  if (dlclose(h) != 0)
+    {
+      try 
+	{
+	  std::string error(dlerror());
+	  throw std::runtime_error(error);
+	}
+      catch (const std::logic_error&)
+	{ }
+    }
+}
+
+void*
+tf(void* arg)
+{
+  void* h;
+  check_dlopen(h);
+  check_dlsym(h);
+  check_dlclose(h);
+  return 0;
+}
+
+// libstdc++/22309
+int
+main (void)
+{
+  pthread_t th;
+  pthread_create(&th, NULL, tf, NULL);
+  pthread_join(th, NULL);
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/lib/dg-options.exp b/libstdc++-v3/testsuite/lib/dg-options.exp
index 05d2ff38ba2a..2ab8e425d342 100644
--- a/libstdc++-v3/testsuite/lib/dg-options.exp
+++ b/libstdc++-v3/testsuite/lib/dg-options.exp
@@ -44,3 +44,12 @@ proc dg-require-cxa-atexit { args } {
     }
     return
 }
+
+proc dg-require-sharedlib { args } {
+    if { ![ check_v3_target_sharedlib ] } {
+	upvar dg-do-what dg-do-what
+	set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+	return
+    }
+    return
+}
diff --git a/libstdc++-v3/testsuite/lib/libstdc++.exp b/libstdc++-v3/testsuite/lib/libstdc++.exp
index 1446d87900ed..333d50ccede3 100644
--- a/libstdc++-v3/testsuite/lib/libstdc++.exp
+++ b/libstdc++-v3/testsuite/lib/libstdc++.exp
@@ -14,7 +14,8 @@
 # 
 # You should have received a copy of the GNU General Public License
 # along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
 
 
 # Define callbacks and load other libraries.  This file is loaded relatively
@@ -79,6 +80,7 @@ proc v3-copy-files {srcfiles} {
 # Called once, during runtest.exp setup.
 proc libstdc++_init { testfile } {
     global env
+    global v3-sharedlib 
     global srcdir blddir objdir tool_root_dir
     global cxx cxxflags
     global includes
@@ -127,6 +129,15 @@ proc libstdc++_init { testfile } {
     }
     v3track gccdir 3
 
+    # Look for shared library. (ie libstdc++.so.)
+    set v3-sharedlib 0
+    set sharedlibdir [lookfor_file $blddir src/.libs/libstdc++.so]
+    if {$sharedlibdir != ""} {
+        set v3-sharedlib 1
+	verbose -log "shared library support detected"
+    }
+    v3track v3-sharedlib 3
+
     # Compute what needs to be added to the existing LD_LIBRARY_PATH.
     if {$gccdir != ""} {
 	set ld_library_path ""
@@ -324,6 +335,7 @@ proc v3-build_support {} {
     global v3-threads
     global v3-test_objs
     global v3-symver
+    global v3-sharedlib
 
     # Figure out whether or not the library supports certain features.
     set v3-wchar_t 0
@@ -353,7 +365,7 @@ proc v3-build_support {} {
 
     # Build the support objects.
     set source_files \
-	[list testsuite_abi.cc testsuite_allocator.cc testsuite_character.cc testsuite_hooks.cc]
+	[list testsuite_abi.cc testsuite_allocator.cc testsuite_character.cc testsuite_hooks.cc ]
     foreach f $source_files {
 	set object_file [file rootname $f].o
 	# Compile with "-w" so that warnings issued by the compiler
@@ -365,6 +377,27 @@ proc v3-build_support {} {
 	}
 	append v3-test_objs "$object_file "
     }
+
+    # Build the shared support objects.
+    if { ${v3-sharedlib} == 1 } {
+	set source_files \
+	    [list testsuite_shared.cc]
+	foreach f $source_files {
+	    set object_file [file rootname $f].so
+	    # Compile with "-w" so that warnings issued by the compiler
+	    # do not prevent compilation.
+	    if { [v3_target_compile $srcdir/$f $object_file "executable" \
+	     [list "incdir=$srcdir" "additional_flags=-w -shared -fPIC -DPIC"]]
+		 != "" } {
+		error "could not compile $f"
+	    }
+	}
+    }
+}
+
+proc check_v3_target_sharedlib { } {
+    global v3-sharedlib
+    return ${v3-sharedlib}
 }
 
 proc check_v3_target_namedlocale { } {
@@ -491,27 +524,27 @@ proc check_v3_target_cxa_atexit { } {
 	puts $f "static unsigned int count;"
 	puts $f "struct X"
 	puts $f "{"
-	puts $f "X() { count = 1; }"
-	puts $f "~X()"
-	puts $f "{"
-	puts $f "  if (count != 3)"
-	puts $f "    exit(1);"
-	puts $f "  count = 4;"
-        puts $f "}"
+	puts $f "  X() { count = 1; }"
+	puts $f "  ~X()"
+	puts $f "  {"
+	puts $f "    if (count != 3)"
+	puts $f "      exit(1);"
+	puts $f "    count = 4;"
+        puts $f "  }"
         puts $f "};"
 	puts $f "void f()"
 	puts $f "{"
-	puts $f "static X x;"
+	puts $f "  static X x;"
         puts $f "}"
 	puts $f "struct Y"
 	puts $f "{"
-	puts $f "Y() { f(); count = 2; }"
-	puts $f "~Y()"
-	puts $f "{"
-	puts $f "if (count != 2)"
-	puts $f "  exit(1);"
-	puts $f "count = 3;"
-        puts $f "}"
+	puts $f "  Y() { f(); count = 2; }"
+	puts $f "  ~Y()"
+	puts $f "  {"
+	puts $f "    if (count != 2)"
+	puts $f "      exit(1);"
+	puts $f "    count = 3;"
+        puts $f "  }"
         puts $f "};"
 	puts $f "Y y;"
 	puts $f "int main()"
diff --git a/libstdc++-v3/testsuite/testsuite_shared.cc b/libstdc++-v3/testsuite/testsuite_shared.cc
new file mode 100644
index 000000000000..a829fb47aa74
--- /dev/null
+++ b/libstdc++-v3/testsuite/testsuite_shared.cc
@@ -0,0 +1,36 @@
+// Copyright (C) 2004, 2005 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+// USA.
+
+#include <string>
+#include <ext/mt_allocator.h>
+
+// libstdc++/22309
+extern "C" void
+foo()
+{
+  typedef char value_t;
+
+  typedef __gnu_cxx::__common_pool_policy<__gnu_cxx::__pool, true> policy_t;
+  typedef __gnu_cxx::__mt_alloc<value_t, policy_t> allocator_t;
+
+  typedef std::char_traits<value_t> traits_t; 
+  typedef std::basic_string<value_t, traits_t, allocator_t> string_t;
+
+  string_t s;
+  s += "west beach, indiana dunes";
+}
-- 
GitLab