Boost logo

Boost-Commit :

From: anthony_at_[hidden]
Date: 2007-11-23 18:09:37


Author: anthonyw
Date: 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
New Revision: 41320
URL: http://svn.boost.org/trac/boost/changeset/41320

Log:
Integrate TSS with thread data; test to ensure cleanup done for native threads as well as boost::thread-launched threads now runs for pthread API as well as win32 API
Removed:
   trunk/libs/thread/src/pthread/tss.cpp
Text files modified:
   trunk/boost/thread/pthread/thread_data.hpp | 4
   trunk/boost/thread/pthread/tss.hpp | 183 +++++++++++++++++++--------------------
   trunk/libs/thread/build/Jamfile.v2 | 1
   trunk/libs/thread/src/pthread/thread.cpp | 137 ++++++++++++++++++++++++++++-
   trunk/libs/thread/test/test_tss.cpp | 103 +++++++++++++--------
   5 files changed, 284 insertions(+), 144 deletions(-)

Modified: trunk/boost/thread/pthread/thread_data.hpp
==============================================================================
--- trunk/boost/thread/pthread/thread_data.hpp (original)
+++ trunk/boost/thread/pthread/thread_data.hpp 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
@@ -20,6 +20,7 @@
     namespace detail
     {
         struct thread_exit_callback_node;
+ struct tss_data_node;
         
         struct thread_data_base
         {
@@ -33,13 +34,14 @@
             bool join_started;
             bool joined;
             boost::detail::thread_exit_callback_node* thread_exit_callbacks;
+ boost::detail::tss_data_node* tss_data;
             bool interrupt_enabled;
             bool interrupt_requested;
             pthread_cond_t* current_cond;
 
             thread_data_base():
                 done(false),join_started(false),joined(false),
- thread_exit_callbacks(0),
+ thread_exit_callbacks(0),tss_data(0),
                 interrupt_enabled(true),
                 interrupt_requested(false),
                 current_cond(0)

Modified: trunk/boost/thread/pthread/tss.hpp
==============================================================================
--- trunk/boost/thread/pthread/tss.hpp (original)
+++ trunk/boost/thread/pthread/tss.hpp 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
@@ -1,109 +1,102 @@
 #ifndef BOOST_THREAD_PTHREAD_TSS_HPP
 #define BOOST_THREAD_PTHREAD_TSS_HPP
 
-// Copyright (C) 2001-2003 William E. Kempf
-// Copyright (C) 2006 Roland Schwarz
-// Copyright (C) 2007 Anthony Williams
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-#include <boost/thread/detail/config.hpp>
-#include <boost/utility.hpp>
-#include <boost/function.hpp>
-#include <boost/thread/exceptions.hpp>
-
-#include <pthread.h>
-
-namespace boost {
-
-// disable warnings about non dll import
-// see: http://www.boost.org/more/separate_compilation.html#dlls
-#ifdef BOOST_MSVC
-# pragma warning(push)
-# pragma warning(disable: 4251 4231 4660 4275)
-#endif
-
-namespace detail {
-
-class BOOST_THREAD_DECL tss : private noncopyable
-{
-public:
- tss(boost::function1<void, void*>* pcleanup) {
- if (pcleanup == 0) throw boost::thread_resource_error();
- try
- {
- init(pcleanup);
- }
- catch (...)
- {
- delete pcleanup;
- throw boost::thread_resource_error();
- }
- }
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+// (C) Copyright 2007 Anthony Williams
 
- ~tss();
- void* get() const;
- void set(void* value);
- void cleanup(void* p);
+#include <boost/shared_ptr.hpp>
 
-private:
- unsigned int m_slot; //This is a "pseudo-slot", not a native slot
-
- void init(boost::function1<void, void*>* pcleanup);
-};
-
-template <typename T>
-struct tss_adapter
-{
- template <typename F>
- tss_adapter(const F& cleanup) : m_cleanup(cleanup) { }
- void operator()(void* p) { m_cleanup(static_cast<T*>(p)); }
- boost::function1<void, T*> m_cleanup;
-};
-
-} // namespace detail
-
-template <typename T>
-class thread_specific_ptr : private noncopyable
+namespace boost
 {
-public:
- thread_specific_ptr()
- : m_tss(new boost::function1<void, void*>(
- boost::detail::tss_adapter<T>(
- &thread_specific_ptr<T>::cleanup)))
- {
- }
- thread_specific_ptr(void (*clean)(T*))
- : m_tss(new boost::function1<void, void*>(
- boost::detail::tss_adapter<T>(clean)))
+ namespace detail
     {
+ struct tss_cleanup_function
+ {
+ virtual ~tss_cleanup_function()
+ {}
+
+ virtual void operator()(void* data)=0;
+ };
+
+ BOOST_THREAD_DECL void set_tss_data(void const* key,boost::shared_ptr<tss_cleanup_function> func,void* tss_data,bool cleanup_existing);
+ BOOST_THREAD_DECL void* get_tss_data(void const* key);
     }
- ~thread_specific_ptr() { reset(); }
 
- T* get() const { return static_cast<T*>(m_tss.get()); }
- T* operator->() const { return get(); }
- T& operator*() const { return *get(); }
- T* release() { T* temp = get(); if (temp) m_tss.set(0); return temp; }
- void reset(T* p=0)
+ template <typename T>
+ class thread_specific_ptr
     {
- T* cur = get();
- if (cur == p) return;
- m_tss.set(p);
- if (cur) m_tss.cleanup(cur);
- }
-
-private:
- static void cleanup(T* p) { delete p; }
- detail::tss m_tss;
-};
-
-#ifdef BOOST_MSVC
-# pragma warning(pop)
-#endif
-
-} // namespace boost
+ private:
+ thread_specific_ptr(thread_specific_ptr&);
+ thread_specific_ptr& operator=(thread_specific_ptr&);
 
+ struct delete_data:
+ detail::tss_cleanup_function
+ {
+ void operator()(void* data)
+ {
+ delete static_cast<T*>(data);
+ }
+ };
+
+ struct run_custom_cleanup_function:
+ detail::tss_cleanup_function
+ {
+ void (*cleanup_function)(T*);
+
+ explicit run_custom_cleanup_function(void (*cleanup_function_)(T*)):
+ cleanup_function(cleanup_function_)
+ {}
+
+ void operator()(void* data)
+ {
+ cleanup_function(data);
+ }
+ };
+
+
+ boost::shared_ptr<detail::tss_cleanup_function> cleanup;
+
+ public:
+ thread_specific_ptr():
+ cleanup(new delete_data)
+ {}
+ explicit thread_specific_ptr(void (*func_)(T*)):
+ cleanup(new run_custom_cleanup_function(func_))
+ {}
+ ~thread_specific_ptr()
+ {
+ reset();
+ }
 
+ T* get() const
+ {
+ return static_cast<T*>(detail::get_tss_data(this));
+ }
+ T* operator->() const
+ {
+ return get();
+ }
+ T& operator*() const
+ {
+ return *get();
+ }
+ T* release()
+ {
+ T* const temp=get();
+ detail::set_tss_data(this,0,0,false);
+ return temp;
+ }
+ void reset(T* new_value=0)
+ {
+ T* const current_value=get();
+ if(current_value!=new_value)
+ {
+ detail::set_tss_data(this,cleanup,new_value,true);
+ }
+ }
+ };
+}
 
 #endif

Modified: trunk/libs/thread/build/Jamfile.v2
==============================================================================
--- trunk/libs/thread/build/Jamfile.v2 (original)
+++ trunk/libs/thread/build/Jamfile.v2 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
@@ -190,7 +190,6 @@
     : ## pthread sources ##
       pthread/thread.cpp
       pthread/exceptions.cpp
- pthread/tss.cpp
       pthread/once.cpp
     : ## requirements ##
       <threadapi>pthread

Modified: trunk/libs/thread/src/pthread/thread.cpp
==============================================================================
--- trunk/libs/thread/src/pthread/thread.cpp (original)
+++ trunk/libs/thread/src/pthread/thread.cpp 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
@@ -12,6 +12,7 @@
 #include <boost/thread/condition.hpp>
 #include <boost/thread/locks.hpp>
 #include <boost/thread/once.hpp>
+#include <boost/thread/tss.hpp>
 
 #include "timeconv.inl"
 
@@ -23,6 +24,24 @@
         {
             boost::detail::thread_exit_function_base* func;
             thread_exit_callback_node* next;
+
+ thread_exit_callback_node(boost::detail::thread_exit_function_base* func_,
+ thread_exit_callback_node* next_):
+ func(func_),next(next_)
+ {}
+ };
+
+ struct tss_data_node
+ {
+ void const* key;
+ boost::shared_ptr<boost::detail::tss_cleanup_function> func;
+ void* value;
+ tss_data_node* next;
+
+ tss_data_node(void const* key_,boost::shared_ptr<boost::detail::tss_cleanup_function> func_,void* value_,
+ tss_data_node* next_):
+ key(key_),func(func_),value(value_),next(next_)
+ {}
         };
 
         namespace
@@ -37,17 +56,31 @@
                     boost::detail::thread_data_base* thread_info=static_cast<boost::detail::thread_data_base*>(data);
                     if(thread_info)
                     {
- while(thread_info->thread_exit_callbacks)
+ while(thread_info->tss_data || thread_info->thread_exit_callbacks)
                         {
- boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks;
- thread_info->thread_exit_callbacks=current_node->next;
- if(current_node->func)
+ while(thread_info->thread_exit_callbacks)
                             {
- (*current_node->func)();
- delete current_node->func;
+ detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks;
+ thread_info->thread_exit_callbacks=current_node->next;
+ if(current_node->func)
+ {
+ (*current_node->func)();
+ delete current_node->func;
+ }
+ delete current_node;
+ }
+ while(thread_info->tss_data)
+ {
+ detail::tss_data_node* const current_node=thread_info->tss_data;
+ thread_info->tss_data=current_node->next;
+ if(current_node->func)
+ {
+ (*current_node->func)(current_node->value);
+ }
+ delete current_node;
                             }
- delete current_node;
                         }
+ thread_info->self.reset();
                     }
                 }
             }
@@ -55,7 +88,7 @@
 
             void create_current_thread_tls_key()
             {
- BOOST_VERIFY(!pthread_key_create(&current_thread_tls_key,NULL));
+ BOOST_VERIFY(!pthread_key_create(&current_thread_tls_key,&tls_destructor));
             }
         }
         
@@ -100,8 +133,39 @@
                 thread_info->done_condition.notify_all();
                 return 0;
             }
+ }
+
+ struct externally_launched_thread:
+ detail::thread_data_base
+ {
+ externally_launched_thread()
+ {
+ interrupt_enabled=false;
+ }
+
+ void run()
+ {}
+ };
 
+ detail::thread_data_base* make_external_thread_data()
+ {
+ detail::thread_data_base* const me(new externally_launched_thread());
+ me->self.reset(me);
+ set_current_thread_data(me);
+ return me;
+ }
+
+
+ detail::thread_data_base* get_or_make_current_thread_data()
+ {
+ detail::thread_data_base* current_thread_data(detail::get_current_thread_data());
+ if(!current_thread_data)
+ {
+ current_thread_data=make_external_thread_data();
+ }
+ return current_thread_data;
         }
+
     }
 
 
@@ -428,6 +492,63 @@
         }
     }
 
+ namespace detail
+ {
+ void add_thread_exit_function(thread_exit_function_base* func)
+ {
+ detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data());
+ thread_exit_callback_node* const new_node=
+ new thread_exit_callback_node(func,current_thread_data->thread_exit_callbacks);
+ current_thread_data->thread_exit_callbacks=new_node;
+ }
+
+ tss_data_node* find_tss_data(void const* key)
+ {
+ detail::thread_data_base* const current_thread_data(get_current_thread_data());
+ if(current_thread_data)
+ {
+ detail::tss_data_node* current_node=current_thread_data->tss_data;
+ while(current_node)
+ {
+ if(current_node->key==key)
+ {
+ return current_node;
+ }
+ current_node=current_node->next;
+ }
+ }
+ return NULL;
+ }
+
+ void* get_tss_data(void const* key)
+ {
+ if(tss_data_node* const current_node=find_tss_data(key))
+ {
+ return current_node->value;
+ }
+ return NULL;
+ }
+
+ void set_tss_data(void const* key,boost::shared_ptr<tss_cleanup_function> func,void* tss_data,bool cleanup_existing)
+ {
+ if(tss_data_node* const current_node=find_tss_data(key))
+ {
+ if(cleanup_existing && current_node->func)
+ {
+ (*current_node->func)(current_node->value);
+ }
+ current_node->func=func;
+ current_node->value=tss_data;
+ }
+ else
+ {
+ detail::thread_data_base* const current_thread_data(get_or_make_current_thread_data());
+ tss_data_node* const new_node=new tss_data_node(key,func,tss_data,current_thread_data->tss_data);
+ current_thread_data->tss_data=new_node;
+ }
+ }
+ }
+
     thread_group::thread_group()
     {
     }

Deleted: trunk/libs/thread/src/pthread/tss.cpp
==============================================================================
--- trunk/libs/thread/src/pthread/tss.cpp 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
+++ (empty file)
@@ -1,187 +0,0 @@
-// Copyright (C) 2001-2003 William E. Kempf
-// Copyright (C) 2006 Roland Schwarz
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-#include <boost/thread/detail/config.hpp>
-
-#include <boost/thread/tss.hpp>
-#ifndef BOOST_THREAD_NO_TSS_CLEANUP
-
-#include <boost/thread/once.hpp>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/exceptions.hpp>
-#include <vector>
-#include <string>
-#include <stdexcept>
-#include <cassert>
-
-namespace {
-
-typedef std::vector<void*> tss_slots;
-typedef std::vector<boost::function1<void, void*>*> tss_data_cleanup_handlers_type;
-
-boost::once_flag tss_data_once = BOOST_ONCE_INIT;
-boost::mutex* tss_data_mutex = 0;
-tss_data_cleanup_handlers_type* tss_data_cleanup_handlers = 0;
- pthread_key_t tss_data_native_key;
-int tss_data_use = 0;
-
-void tss_data_inc_use(boost::mutex::scoped_lock& lk)
-{
- ++tss_data_use;
-}
-
-void tss_data_dec_use(boost::mutex::scoped_lock& lk)
-{
- if (0 == --tss_data_use)
- {
- tss_data_cleanup_handlers_type::size_type i;
- for (i = 0; i < tss_data_cleanup_handlers->size(); ++i)
- {
- delete (*tss_data_cleanup_handlers)[i];
- }
- delete tss_data_cleanup_handlers;
- tss_data_cleanup_handlers = 0;
- lk.unlock();
- delete tss_data_mutex;
- tss_data_mutex = 0;
- pthread_key_delete(tss_data_native_key);
- }
-}
-
-extern "C" void cleanup_slots(void* p)
-{
- tss_slots* slots = static_cast<tss_slots*>(p);
- boost::mutex::scoped_lock lock(*tss_data_mutex);
- for (tss_slots::size_type i = 0; i < slots->size(); ++i)
- {
- (*(*tss_data_cleanup_handlers)[i])((*slots)[i]);
- (*slots)[i] = 0;
- }
- tss_data_dec_use(lock);
- delete slots;
-}
-
-void init_tss_data()
-{
- std::auto_ptr<tss_data_cleanup_handlers_type>
- temp(new tss_data_cleanup_handlers_type);
-
- std::auto_ptr<boost::mutex> temp_mutex(new boost::mutex);
- if (temp_mutex.get() == 0)
- throw boost::thread_resource_error();
-
- int res = pthread_key_create(&tss_data_native_key, &cleanup_slots);
- if (res != 0)
- return;
-
- // The life time of cleanup handlers and mutex is beeing
- // managed by a reference counting technique.
- // This avoids a memory leak by releasing the global data
- // after last use only, since the execution order of cleanup
- // handlers is unspecified on any platform with regards to
- // C++ destructor ordering rules.
- tss_data_cleanup_handlers = temp.release();
- tss_data_mutex = temp_mutex.release();
-}
-
-
-tss_slots* get_slots(bool alloc)
-{
- tss_slots* slots = 0;
-
- slots = static_cast<tss_slots*>(
- pthread_getspecific(tss_data_native_key));
-
- if (slots == 0 && alloc)
- {
- std::auto_ptr<tss_slots> temp(new tss_slots);
-
- if (pthread_setspecific(tss_data_native_key, temp.get()) != 0)
- return 0;
- {
- boost::mutex::scoped_lock lock(*tss_data_mutex);
- tss_data_inc_use(lock);
- }
- slots = temp.release();
- }
-
- return slots;
-}
-
-} // namespace
-
-namespace boost {
-
-namespace detail {
-void tss::init(boost::function1<void, void*>* pcleanup)
-{
- boost::call_once(tss_data_once, &init_tss_data);
- if (tss_data_cleanup_handlers == 0)
- throw thread_resource_error();
- boost::mutex::scoped_lock lock(*tss_data_mutex);
- try
- {
- tss_data_cleanup_handlers->push_back(pcleanup);
- m_slot = tss_data_cleanup_handlers->size() - 1;
- tss_data_inc_use(lock);
- }
- catch (...)
- {
- throw thread_resource_error();
- }
-}
-
-tss::~tss()
-{
- boost::mutex::scoped_lock lock(*tss_data_mutex);
- tss_data_dec_use(lock);
-}
-
-void* tss::get() const
-{
- tss_slots* slots = get_slots(false);
-
- if (!slots)
- return 0;
-
- if (m_slot >= slots->size())
- return 0;
-
- return (*slots)[m_slot];
-}
-
-void tss::set(void* value)
-{
- tss_slots* slots = get_slots(true);
-
- if (!slots)
- throw boost::thread_resource_error();
-
- if (m_slot >= slots->size())
- {
- try
- {
- slots->resize(m_slot + 1);
- }
- catch (...)
- {
- throw boost::thread_resource_error();
- }
- }
-
- (*slots)[m_slot] = value;
-}
-
-void tss::cleanup(void* value)
-{
- boost::mutex::scoped_lock lock(*tss_data_mutex);
- (*(*tss_data_cleanup_handlers)[m_slot])(value);
-}
-
-} // namespace detail
-} // namespace boost
-
-#endif //BOOST_THREAD_NO_TSS_CLEANUP

Modified: trunk/libs/thread/test/test_tss.cpp
==============================================================================
--- trunk/libs/thread/test/test_tss.cpp (original)
+++ trunk/libs/thread/test/test_tss.cpp 2007-11-23 18:09:36 EST (Fri, 23 Nov 2007)
@@ -62,7 +62,7 @@
     }
 }
 
-#if defined(BOOST_HAS_WINTHREADS)
+#if defined(BOOST_THREAD_PLATFORM_WIN32)
     typedef HANDLE native_thread_t;
 
     DWORD WINAPI test_tss_thread_native(LPVOID lpParameter)
@@ -91,6 +91,33 @@
         res = CloseHandle(thread);
         BOOST_CHECK(SUCCEEDED(res));
     }
+#elif defined(BOOST_THREAD_PLATFORM_PTHREAD)
+ typedef pthread_t native_thread_t;
+
+extern "C"
+{
+ void* test_tss_thread_native(void* lpParameter)
+ {
+ test_tss_thread();
+ return 0;
+ }
+}
+
+ native_thread_t create_native_thread()
+ {
+ native_thread_t thread_handle;
+
+ int const res = pthread_create(&thread_handle, 0, &test_tss_thread_native, 0);
+ BOOST_CHECK(!res);
+ return thread_handle;
+ }
+
+ void join_native_thread(native_thread_t thread)
+ {
+ void* result=0;
+ int const res=pthread_join(thread,&result);
+ BOOST_CHECK(!res);
+ }
 #endif
 
 void do_test_tss()
@@ -123,44 +150,42 @@
     BOOST_CHECK_EQUAL(tss_instances, 0);
     BOOST_CHECK_EQUAL(tss_total, 5);
 
- #if defined(BOOST_HAS_WINTHREADS)
- tss_instances = 0;
- tss_total = 0;
-
- native_thread_t thread1 = create_native_thread();
- BOOST_CHECK(thread1 != 0);
-
- native_thread_t thread2 = create_native_thread();
- BOOST_CHECK(thread2 != 0);
-
- native_thread_t thread3 = create_native_thread();
- BOOST_CHECK(thread3 != 0);
-
- native_thread_t thread4 = create_native_thread();
- BOOST_CHECK(thread3 != 0);
-
- native_thread_t thread5 = create_native_thread();
- BOOST_CHECK(thread3 != 0);
-
- join_native_thread(thread5);
- join_native_thread(thread4);
- join_native_thread(thread3);
- join_native_thread(thread2);
- join_native_thread(thread1);
-
- std::cout
- << "tss_instances = " << tss_instances
- << "; tss_total = " << tss_total
- << "\n";
- std::cout.flush();
-
- // The following is not really an error. TSS cleanup support still is available for boost threads.
- // Also this usually will be triggered only when bound to the static version of thread lib.
- // 2006-10-02 Roland Schwarz
- //BOOST_CHECK_EQUAL(tss_instances, 0);
- BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available");
- BOOST_CHECK_EQUAL(tss_total, 5);
- #endif
+ tss_instances = 0;
+ tss_total = 0;
+
+ native_thread_t thread1 = create_native_thread();
+ BOOST_CHECK(thread1 != 0);
+
+ native_thread_t thread2 = create_native_thread();
+ BOOST_CHECK(thread2 != 0);
+
+ native_thread_t thread3 = create_native_thread();
+ BOOST_CHECK(thread3 != 0);
+
+ native_thread_t thread4 = create_native_thread();
+ BOOST_CHECK(thread3 != 0);
+
+ native_thread_t thread5 = create_native_thread();
+ BOOST_CHECK(thread3 != 0);
+
+ join_native_thread(thread5);
+ join_native_thread(thread4);
+ join_native_thread(thread3);
+ join_native_thread(thread2);
+ join_native_thread(thread1);
+
+ std::cout
+ << "tss_instances = " << tss_instances
+ << "; tss_total = " << tss_total
+ << "\n";
+ std::cout.flush();
+
+ // The following is not really an error. TSS cleanup support still is available for boost threads.
+ // Also this usually will be triggered only when bound to the static version of thread lib.
+ // 2006-10-02 Roland Schwarz
+ //BOOST_CHECK_EQUAL(tss_instances, 0);
+ BOOST_CHECK_MESSAGE(tss_instances == 0, "Support of automatic tss cleanup for native threading API not available");
+ BOOST_CHECK_EQUAL(tss_total, 5);
 }
 
 void test_tss()


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk