Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r49201 - trunk/boost/asio/detail
From: chris_at_[hidden]
Date: 2008-10-09 02:33:35


Author: chris_kohlhoff
Date: 2008-10-09 02:33:34 EDT (Thu, 09 Oct 2008)
New Revision: 49201
URL: http://svn.boost.org/trac/boost/changeset/49201

Log:
Only use TerminateThread when explicitly requested by the user by calling
asio::detail::thread::set_terminate_threads(true). This fixes a memory leak
that may occur with internally created threads.

Text files modified:
   trunk/boost/asio/detail/null_thread.hpp | 5 -
   trunk/boost/asio/detail/posix_thread.hpp | 5 -
   trunk/boost/asio/detail/win_thread.hpp | 117 +++++++++++++++++++++++++--------------
   trunk/boost/asio/detail/wince_thread.hpp | 5 -
   4 files changed, 78 insertions(+), 54 deletions(-)

Modified: trunk/boost/asio/detail/null_thread.hpp
==============================================================================
--- trunk/boost/asio/detail/null_thread.hpp (original)
+++ trunk/boost/asio/detail/null_thread.hpp 2008-10-09 02:33:34 EDT (Thu, 09 Oct 2008)
@@ -39,12 +39,9 @@
   : private noncopyable
 {
 public:
- // The purpose of the thread.
- enum purpose { internal, external };
-
   // Constructor.
   template <typename Function>
- null_thread(Function f, purpose = internal)
+ null_thread(Function f)
   {
     boost::system::system_error e(
         boost::asio::error::operation_not_supported, "thread");

Modified: trunk/boost/asio/detail/posix_thread.hpp
==============================================================================
--- trunk/boost/asio/detail/posix_thread.hpp (original)
+++ trunk/boost/asio/detail/posix_thread.hpp 2008-10-09 02:33:34 EDT (Thu, 09 Oct 2008)
@@ -43,12 +43,9 @@
   : private noncopyable
 {
 public:
- // The purpose of the thread.
- enum purpose { internal, external };
-
   // Constructor.
   template <typename Function>
- posix_thread(Function f, purpose = internal)
+ posix_thread(Function f)
     : joined_(false)
   {
     std::auto_ptr<func_base> arg(new func<Function>(f));

Modified: trunk/boost/asio/detail/win_thread.hpp
==============================================================================
--- trunk/boost/asio/detail/win_thread.hpp (original)
+++ trunk/boost/asio/detail/win_thread.hpp 2008-10-09 02:33:34 EDT (Thu, 09 Oct 2008)
@@ -40,50 +40,67 @@
 
 unsigned int __stdcall win_thread_function(void* arg);
 
-class win_thread
- : private noncopyable
+#if (WINVER < 0x0500)
+void __stdcall apc_function(ULONG data);
+#else
+void __stdcall apc_function(ULONG_PTR data);
+#endif
+
+template <typename T>
+class win_thread_base
 {
 public:
- // The purpose of the thread.
- enum purpose { internal, external };
+ static bool terminate_threads()
+ {
+ return ::InterlockedExchangeAdd(&terminate_threads_, 0) != 0;
+ }
+
+ static void set_terminate_threads(bool b)
+ {
+ ::InterlockedExchange(&terminate_threads_, b ? 1 : 0);
+ }
+
+private:
+ static long terminate_threads_;
+};
 
+template <typename T>
+long win_thread_base<T>::terminate_threads_ = 0;
+
+class win_thread
+ : private noncopyable,
+ public win_thread_base<win_thread>
+{
+public:
   // Constructor.
   template <typename Function>
- win_thread(Function f, purpose p = internal)
+ win_thread(Function f)
     : exit_event_(0)
   {
     std::auto_ptr<func_base> arg(new func<Function>(f));
 
     ::HANDLE entry_event = 0;
- if (p == internal)
+ arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0);
+ if (!entry_event)
     {
- arg->entry_event_ = entry_event = ::CreateEvent(0, true, false, 0);
- if (!entry_event)
- {
- DWORD last_error = ::GetLastError();
- boost::system::system_error e(
- boost::system::error_code(last_error,
- boost::asio::error::get_system_category()),
- "thread.entry_event");
- boost::throw_exception(e);
- }
-
- arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0);
- if (!exit_event_)
- {
- DWORD last_error = ::GetLastError();
- ::CloseHandle(entry_event);
- boost::system::system_error e(
- boost::system::error_code(last_error,
- boost::asio::error::get_system_category()),
- "thread.exit_event");
- boost::throw_exception(e);
- }
+ DWORD last_error = ::GetLastError();
+ boost::system::system_error e(
+ boost::system::error_code(last_error,
+ boost::asio::error::get_system_category()),
+ "thread.entry_event");
+ boost::throw_exception(e);
     }
- else
+
+ arg->exit_event_ = exit_event_ = ::CreateEvent(0, true, false, 0);
+ if (!exit_event_)
     {
- arg->entry_event_ = 0;
- arg->exit_event_ = 0;
+ DWORD last_error = ::GetLastError();
+ ::CloseHandle(entry_event);
+ boost::system::system_error e(
+ boost::system::error_code(last_error,
+ boost::asio::error::get_system_category()),
+ "thread.exit_event");
+ boost::throw_exception(e);
     }
 
     unsigned int thread_id = 0;
@@ -123,14 +140,15 @@
   // Wait for the thread to exit.
   void join()
   {
- if (exit_event_)
+ ::WaitForSingleObject(exit_event_, INFINITE);
+ ::CloseHandle(exit_event_);
+ if (terminate_threads())
     {
- ::WaitForSingleObject(exit_event_, INFINITE);
- ::CloseHandle(exit_event_);
       ::TerminateThread(thread_, 0);
     }
     else
     {
+ ::QueueUserAPC(apc_function, thread_, 0);
       ::WaitForSingleObject(thread_, INFINITE);
     }
   }
@@ -138,6 +156,12 @@
 private:
   friend unsigned int __stdcall win_thread_function(void* arg);
 
+#if (WINVER < 0x0500)
+ friend void __stdcall apc_function(ULONG);
+#else
+ friend void __stdcall apc_function(ULONG_PTR);
+#endif
+
   class func_base
   {
   public:
@@ -175,21 +199,30 @@
   std::auto_ptr<win_thread::func_base> func(
       static_cast<win_thread::func_base*>(arg));
 
- if (func->entry_event_)
- ::SetEvent(func->entry_event_);
+ ::SetEvent(func->entry_event_);
 
   func->run();
 
- if (HANDLE exit_event = func->exit_event_)
- {
- func.reset();
- ::SetEvent(exit_event);
- ::Sleep(INFINITE);
- }
+ // Signal that the thread has finished its work, but rather than returning go
+ // to sleep to put the thread into a well known state. If the thread is being
+ // joined during global object destruction then it may be killed using
+ // TerminateThread (to avoid a deadlock in DllMain). Otherwise, the SleepEx
+ // call will be interrupted using QueueUserAPC and the thread will shut down
+ // cleanly.
+ HANDLE exit_event = func->exit_event_;
+ func.reset();
+ ::SetEvent(exit_event);
+ ::SleepEx(INFINITE, TRUE);
 
   return 0;
 }
 
+#if (WINVER < 0x0500)
+inline void __stdcall apc_function(ULONG) {}
+#else
+inline void __stdcall apc_function(ULONG_PTR) {}
+#endif
+
 } // namespace detail
 } // namespace asio
 } // namespace boost

Modified: trunk/boost/asio/detail/wince_thread.hpp
==============================================================================
--- trunk/boost/asio/detail/wince_thread.hpp (original)
+++ trunk/boost/asio/detail/wince_thread.hpp 2008-10-09 02:33:34 EDT (Thu, 09 Oct 2008)
@@ -43,12 +43,9 @@
   : private noncopyable
 {
 public:
- // The purpose of the thread.
- enum purpose { internal, external };
-
   // Constructor.
   template <typename Function>
- wince_thread(Function f, purpose = internal)
+ wince_thread(Function f)
   {
     std::auto_ptr<func_base> arg(new func<Function>(f));
     DWORD thread_id = 0;


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