Fix tinythread memory leak in two ways:

1. update to tinythread version 1.1, which provides a detach method
2. move the thread disassociation to the join function, which allows
attached threads to be joined even after they finish (this was the main
leak)

Also update RemoteServer.cpp to detach threads and delete the thread
objects instead of leaking them (although they are much smaller than the
actual threads).
develop
Ben Lubar 2016-05-21 19:17:08 -05:00
parent dabf443260
commit 700392ba55
4 changed files with 98 additions and 42 deletions

@ -1,5 +1,5 @@
/* /* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010 Marcus Geelnard Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -47,7 +47,15 @@ freely, subject to the following restrictions:
#endif #endif
#if defined(_TTHREAD_WIN32_) #if defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include <windows.h> #include <windows.h>
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#else #else
#ifdef _FAST_MUTEX_ASM_ #ifdef _FAST_MUTEX_ASM_
#include <sched.h> #include <sched.h>
@ -237,3 +245,4 @@ class fast_mutex {
} }
#endif // _FAST_MUTEX_H_ #endif // _FAST_MUTEX_H_

@ -1,5 +1,5 @@
/* /* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010 Marcus Geelnard Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -171,13 +171,17 @@ void * thread::wrapper_function(void * aArg)
catch(...) catch(...)
{ {
// Uncaught exceptions will terminate the application (default behavior // Uncaught exceptions will terminate the application (default behavior
// according to the C++0x draft) // according to C++11)
std::terminate(); std::terminate();
} }
#if 0
// DFHack fix: this code prevents join from freeing thread resources.
// The thread is no longer executing // The thread is no longer executing
lock_guard<mutex> guard(ti->mThread->mDataMutex); lock_guard<mutex> guard(ti->mThread->mDataMutex);
ti->mThread->mNotAThread = true; ti->mThread->mNotAThread = true;
#endif
// The thread is responsible for freeing the startup information // The thread is responsible for freeing the startup information
delete ti; delete ti;
@ -228,9 +232,16 @@ void thread::join()
{ {
#if defined(_TTHREAD_WIN32_) #if defined(_TTHREAD_WIN32_)
WaitForSingleObject(mHandle, INFINITE); WaitForSingleObject(mHandle, INFINITE);
CloseHandle(mHandle);
#elif defined(_TTHREAD_POSIX_) #elif defined(_TTHREAD_POSIX_)
pthread_join(mHandle, NULL); pthread_join(mHandle, NULL);
#endif #endif
#if 1
// DFHack patch: moved here from the wrapper function
lock_guard<mutex> guard(mDataMutex);
mNotAThread = true;
#endif
} }
} }
@ -242,6 +253,21 @@ bool thread::joinable() const
return result; return result;
} }
void thread::detach()
{
mDataMutex.lock();
if(!mNotAThread)
{
#if defined(_TTHREAD_WIN32_)
CloseHandle(mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_detach(mHandle);
#endif
mNotAThread = true;
}
mDataMutex.unlock();
}
thread::id thread::get_id() const thread::id thread::get_id() const
{ {
if(!joinable()) if(!joinable())

@ -1,5 +1,5 @@
/* /* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010 Marcus Geelnard Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages
@ -31,7 +31,7 @@ freely, subject to the following restrictions:
/// TinyThread++ is a minimal, portable implementation of basic threading /// TinyThread++ is a minimal, portable implementation of basic threading
/// classes for C++. /// classes for C++.
/// ///
/// They closely mimic the functionality and naming of the C++0x standard, and /// They closely mimic the functionality and naming of the C++11 standard, and
/// should be easily replaceable with the corresponding std:: variants. /// should be easily replaceable with the corresponding std:: variants.
/// ///
/// @section port_sec Portability /// @section port_sec Portability
@ -39,7 +39,7 @@ freely, subject to the following restrictions:
/// classes, while for other systems, the POSIX threads API (pthread) is used. /// classes, while for other systems, the POSIX threads API (pthread) is used.
/// ///
/// @section class_sec Classes /// @section class_sec Classes
/// In order to mimic the threading API of the C++0x standard, subsets of /// In order to mimic the threading API of the C++11 standard, subsets of
/// several classes are provided. The fundamental classes are: /// several classes are provided. The fundamental classes are:
/// @li tthread::thread /// @li tthread::thread
/// @li tthread::mutex /// @li tthread::mutex
@ -67,8 +67,15 @@ freely, subject to the following restrictions:
// Platform specific includes // Platform specific includes
#if defined(_TTHREAD_WIN32_) #if defined(_TTHREAD_WIN32_)
#define NOMINMAX #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include <windows.h> #include <windows.h>
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#else #else
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
@ -82,22 +89,22 @@ freely, subject to the following restrictions:
/// TinyThread++ version (major number). /// TinyThread++ version (major number).
#define TINYTHREAD_VERSION_MAJOR 1 #define TINYTHREAD_VERSION_MAJOR 1
/// TinyThread++ version (minor number). /// TinyThread++ version (minor number).
#define TINYTHREAD_VERSION_MINOR 0 #define TINYTHREAD_VERSION_MINOR 1
/// TinyThread++ version (full version). /// TinyThread++ version (full version).
#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) #define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR)
// Do we have a fully featured C++0x compiler? // Do we have a fully featured C++11 compiler?
#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) #if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L))
#define _TTHREAD_CPP0X_ #define _TTHREAD_CPP11_
#endif #endif
// ...at least partial C++0x? // ...at least partial C++11?
#if defined(_TTHREAD_CPP0X_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) #if defined(_TTHREAD_CPP11_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__)
#define _TTHREAD_CPP0X_PARTIAL_ #define _TTHREAD_CPP11_PARTIAL_
#endif #endif
// Macro for disabling assignments of objects. // Macro for disabling assignments of objects.
#ifdef _TTHREAD_CPP0X_PARTIAL_ #ifdef _TTHREAD_CPP11_PARTIAL_
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \ #define _TTHREAD_DISABLE_ASSIGNMENT(name) \
name(const name&) = delete; \ name(const name&) = delete; \
name& operator=(const name&) = delete; name& operator=(const name&) = delete;
@ -109,18 +116,18 @@ freely, subject to the following restrictions:
/// @def thread_local /// @def thread_local
/// Thread local storage keyword. /// Thread local storage keyword.
/// A variable that is declared with the \c thread_local keyword makes the /// A variable that is declared with the @c thread_local keyword makes the
/// value of the variable local to each thread (known as thread-local storage, /// value of the variable local to each thread (known as thread-local storage,
/// or TLS). Example usage: /// or TLS). Example usage:
/// @code /// @code
/// // This variable is local to each thread. /// // This variable is local to each thread.
/// thread_local int variable; /// thread_local int variable;
/// @endcode /// @endcode
/// @note The \c thread_local keyword is a macro that maps to the corresponding /// @note The @c thread_local keyword is a macro that maps to the corresponding
/// compiler directive (e.g. \c __declspec(thread)). While the C++0x standard /// compiler directive (e.g. @c __declspec(thread)). While the C++11 standard
/// allows for non-trivial types (e.g. classes with constructors and /// allows for non-trivial types (e.g. classes with constructors and
/// destructors) to be declared with the \c thread_local keyword, most pre-C++0x /// destructors) to be declared with the @c thread_local keyword, most pre-C++11
/// compilers only allow for trivial types (e.g. \c int). So, to guarantee /// compilers only allow for trivial types (e.g. @c int). So, to guarantee
/// portable code, only use trivial types for thread local storage. /// portable code, only use trivial types for thread local storage.
/// @note This directive is currently not supported on Mac OS X (it will give /// @note This directive is currently not supported on Mac OS X (it will give
/// a compiler error), since compile-time TLS is not supported in the Mac OS X /// a compiler error), since compile-time TLS is not supported in the Mac OS X
@ -128,7 +135,7 @@ freely, subject to the following restrictions:
/// not support this directive. /// not support this directive.
/// @hideinitializer /// @hideinitializer
#if !defined(_TTHREAD_CPP0X_) && !defined(thread_local) #if !defined(_TTHREAD_CPP11_) && !defined(thread_local)
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define thread_local __thread #define thread_local __thread
#else #else
@ -138,8 +145,8 @@ freely, subject to the following restrictions:
/// Main name space for TinyThread++. /// Main name space for TinyThread++.
/// This namespace is more or less equivalent to the \c std namespace for the /// This namespace is more or less equivalent to the @c std namespace for the
/// C++0x thread classes. For instance, the tthread::mutex class corresponds to /// C++11 thread classes. For instance, the tthread::mutex class corresponds to
/// the std::mutex class. /// the std::mutex class.
namespace tthread { namespace tthread {
@ -176,7 +183,7 @@ class mutex {
/// Lock the mutex. /// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can /// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until \c unlock() is called. /// be obtained. The mutex remains locked until @c unlock() is called.
/// @see lock_guard /// @see lock_guard
inline void lock() inline void lock()
{ {
@ -192,7 +199,7 @@ class mutex {
/// Try to lock the mutex. /// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will /// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking). /// return immediately (non-blocking).
/// @return \c true if the lock was acquired, or \c false if the lock could /// @return @c true if the lock was acquired, or @c false if the lock could
/// not be acquired. /// not be acquired.
inline bool try_lock() inline bool try_lock()
{ {
@ -268,7 +275,7 @@ class recursive_mutex {
/// Lock the mutex. /// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can /// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until \c unlock() is called. /// be obtained. The mutex remains locked until @c unlock() is called.
/// @see lock_guard /// @see lock_guard
inline void lock() inline void lock()
{ {
@ -282,7 +289,7 @@ class recursive_mutex {
/// Try to lock the mutex. /// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will /// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking). /// return immediately (non-blocking).
/// @return \c true if the lock was acquired, or \c false if the lock could /// @return @c true if the lock was acquired, or @c false if the lock could
/// not be acquired. /// not be acquired.
inline bool try_lock() inline bool try_lock()
{ {
@ -406,7 +413,7 @@ class condition_variable {
/// Wait for the condition. /// Wait for the condition.
/// The function will block the calling thread until the condition variable /// The function will block the calling thread until the condition variable
/// is woken by \c notify_one(), \c notify_all() or a spurious wake up. /// is woken by @c notify_one(), @c notify_all() or a spurious wake up.
/// @param[in] aMutex A mutex that will be unlocked when the wait operation /// @param[in] aMutex A mutex that will be unlocked when the wait operation
/// starts, an locked again as soon as the wait operation is finished. /// starts, an locked again as soon as the wait operation is finished.
template <class _mutexT> template <class _mutexT>
@ -482,7 +489,7 @@ class thread {
class id; class id;
/// Default constructor. /// Default constructor.
/// Construct a \c thread object without an associated thread of execution /// Construct a @c thread object without an associated thread of execution
/// (i.e. non-joinable). /// (i.e. non-joinable).
thread() : mHandle(0), mNotAThread(true) thread() : mHandle(0), mNotAThread(true)
#if defined(_TTHREAD_WIN32_) #if defined(_TTHREAD_WIN32_)
@ -491,7 +498,7 @@ class thread {
{} {}
/// Thread starting constructor. /// Thread starting constructor.
/// Construct a \c thread object with a new thread of execution. /// Construct a @c thread object with a new thread of execution.
/// @param[in] aFunction A function pointer to a function of type: /// @param[in] aFunction A function pointer to a function of type:
/// <tt>void fun(void * arg)</tt> /// <tt>void fun(void * arg)</tt>
/// @param[in] aArg Argument to the thread function. /// @param[in] aArg Argument to the thread function.
@ -501,24 +508,34 @@ class thread {
thread(void (*aFunction)(void *), void * aArg); thread(void (*aFunction)(void *), void * aArg);
/// Destructor. /// Destructor.
/// @note If the thread is joinable upon destruction, \c std::terminate() /// @note If the thread is joinable upon destruction, @c std::terminate()
/// will be called, which terminates the process. It is always wise to do /// will be called, which terminates the process. It is always wise to do
/// \c join() before deleting a thread object. /// @c join() before deleting a thread object.
~thread(); ~thread();
/// Wait for the thread to finish (join execution flows). /// Wait for the thread to finish (join execution flows).
/// After calling @c join(), the thread object is no longer associated with
/// a thread of execution (i.e. it is not joinable, and you may not join
/// with it nor detach from it).
void join(); void join();
/// Check if the thread is joinable. /// Check if the thread is joinable.
/// A thread object is joinable if it has an associated thread of execution. /// A thread object is joinable if it has an associated thread of execution.
bool joinable() const; bool joinable() const;
/// Detach from the thread.
/// After calling @c detach(), the thread object is no longer assicated with
/// a thread of execution (i.e. it is not joinable). The thread continues
/// execution without the calling thread blocking, and when the thread
/// ends execution, any owned resources are released.
void detach();
/// Return the thread ID of a thread object. /// Return the thread ID of a thread object.
id get_id() const; id get_id() const;
/// Get the native handle for this thread. /// Get the native handle for this thread.
/// @note Under Windows, this is a \c HANDLE, and under POSIX systems, this /// @note Under Windows, this is a @c HANDLE, and under POSIX systems, this
/// is a \c pthread_t. /// is a @c pthread_t.
inline native_handle_type native_handle() inline native_handle_type native_handle()
{ {
return mHandle; return mHandle;
@ -613,18 +630,18 @@ class thread::id {
// Related to <ratio> - minimal to be able to support chrono. // Related to <ratio> - minimal to be able to support chrono.
typedef long long __intmax_t; typedef long long __intmax_t;
/// Minimal implementation of the \c ratio class. This class provides enough /// Minimal implementation of the @c ratio class. This class provides enough
/// functionality to implement some basic \c chrono classes. /// functionality to implement some basic @c chrono classes.
template <__intmax_t N, __intmax_t D = 1> class ratio { template <__intmax_t N, __intmax_t D = 1> class ratio {
public: public:
static double _as_double() { return double(N) / double(D); } static double _as_double() { return double(N) / double(D); }
}; };
/// Minimal implementation of the \c chrono namespace. /// Minimal implementation of the @c chrono namespace.
/// The \c chrono namespace provides types for specifying time intervals. /// The @c chrono namespace provides types for specifying time intervals.
namespace chrono { namespace chrono {
/// Duration template class. This class provides enough functionality to /// Duration template class. This class provides enough functionality to
/// implement \c this_thread::sleep_for(). /// implement @c this_thread::sleep_for().
template <class _Rep, class _Period = ratio<1> > class duration { template <class _Rep, class _Period = ratio<1> > class duration {
private: private:
_Rep rep_; _Rep rep_;
@ -652,7 +669,7 @@ namespace chrono {
typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours.
} }
/// The namespace \c this_thread provides methods for dealing with the /// The namespace @c this_thread provides methods for dealing with the
/// calling thread. /// calling thread.
namespace this_thread { namespace this_thread {
/// Return the thread ID of the calling thread. /// Return the thread ID of the calling thread.

@ -117,6 +117,7 @@ ServerConnection::ServerConnection(CActiveSocket *socket)
core_service->finalize(this, &functions); core_service->finalize(this, &functions);
thread = new tthread::thread(threadFn, (void*)this); thread = new tthread::thread(threadFn, (void*)this);
thread->detach();
} }
ServerConnection::~ServerConnection() ServerConnection::~ServerConnection()
@ -124,6 +125,7 @@ ServerConnection::~ServerConnection()
in_error = true; in_error = true;
socket->Close(); socket->Close();
delete socket; delete socket;
delete thread;
for (auto it = plugin_services.begin(); it != plugin_services.end(); ++it) for (auto it = plugin_services.begin(); it != plugin_services.end(); ++it)
delete it->second; delete it->second;
@ -363,6 +365,7 @@ ServerMain::~ServerMain()
{ {
socket->Close(); socket->Close();
delete socket; delete socket;
delete thread;
} }
bool ServerMain::listen(int port) bool ServerMain::listen(int port)
@ -376,6 +379,7 @@ bool ServerMain::listen(int port)
return false; return false;
thread = new tthread::thread(threadFn, this); thread = new tthread::thread(threadFn, this);
thread->detach();
return true; return true;
} }