I started off running valgrind on some simple tests and the deeper I
dug, the more dirt I found. :-)
Basically, for each call to pthread_create(), there should be a matching
call to pthread_join() to recover system resources.
The current code for ThreadBase::Wait() checks IsRunning() to prevent
thread_join() from being called before pthread_create() is called.
Having the running/not-running state cleared by the child thread when
runnable->Run() returns in proxyFunc() means that if the child has
finnished, pthread_join() is never called.
Looking at the implementation more closely, I decided to simplify things
by removing the use of ThreadStartParams and Barrier, and making the
isRunning flag be set only by Start().
Or in other words, isRunning means threadHandle is valid.
I think this change also allows the thread to be Stop()'ed and Start()'ed
more than once although I haven't tested that.
I made similar changes to the win32 threads and I'm hoping
one of you will test it for me. :-)
If you can't apply it easily due to white space mangling, I can
resend as a tar or zip file.
Index: include/csutil/threading/pthread_thread.h
===================================================================
--- include/csutil/threading/pthread_thread.h (revision 39557)
+++ include/csutil/threading/pthread_thread.h (working copy)
@@ -41,11 +41,16 @@
public:
ThreadBase (Runnable* runnable);
+ ~ThreadBase ();
+
void Start ();
void Stop ();
- bool IsRunning () const;
+ bool IsRunning () const
+ {
+ return isRunning;
+ }
bool SetPriority (ThreadPriority prio);
@@ -55,6 +60,11 @@
static ThreadID GetThreadID ();
+ Runnable* GetRunnable () const
+ {
+ return runnable;
+ }
+
ThreadPriority GetPriority () const
{
return priority;
@@ -65,9 +75,8 @@
pthread_t threadHandle;
- int32 isRunning;
+ mutable bool isRunning;
ThreadPriority priority;
- Barrier startupBarrier;
};
Index: libs/csutil/threading/pthread_thread.cpp
===================================================================
--- libs/csutil/threading/pthread_thread.cpp (revision 39557)
+++ libs/csutil/threading/pthread_thread.cpp (working copy)
@@ -33,36 +33,12 @@
namespace
{
-
- class ThreadStartParams : public CS::Memory::CustomAllocated
+ static void* proxyFunc (void* param)
{
- public:
- ThreadStartParams (ThreadBase* thread, Runnable* runner, int32* isRunningPtr,
- Barrier* startupBarrier)
- : thread (thread), runnable (runner), isRunningPtr (isRunningPtr),
- startupBarrier (startupBarrier)
- {
- }
-
- ThreadBase* thread;
- Runnable* runnable;
- int32* isRunningPtr;
- Barrier* startupBarrier;
- };
-
- void* proxyFunc (void* param)
- {
// Extract the parameters
- ThreadStartParams* tp = static_cast<ThreadStartParams*> (param);
- csRef<ThreadBase> thread (tp->thread);
- int32* isRunningPtr = tp->isRunningPtr;
- Runnable* runnable = tp->runnable;
- Barrier* startupBarrier = tp->startupBarrier;
+ ThreadBase* tb = static_cast<ThreadBase*> (param);
+ Runnable* runnable = tb->GetRunnable();
- // Set as running and wait for main thread to catch up
- AtomicOperations::Set (isRunningPtr, 1);
- startupBarrier->Wait ();
-
#ifdef CS_HAVE_PTHREAD_SETNAME_NP
{
// Set the name, for debugging
@@ -71,13 +47,10 @@
pthread_setname_np (pthread_self(), threadName);
}
#endif
-
- // Run
+
+ // Run
runnable->Run ();
- // Set as non-running
- AtomicOperations::Set (isRunningPtr, 0);
-
return 0;
}
@@ -85,27 +58,33 @@
ThreadBase::ThreadBase (Runnable* runnable)
- : runnable (runnable), isRunning (0), priority (THREAD_PRIO_NORMAL),
- startupBarrier (2)
+ : runnable (runnable), isRunning (false), priority (THREAD_PRIO_NORMAL)
{
}
+ ThreadBase::~ThreadBase ()
+ {
+ if (IsRunning ())
+ {
+ pthread_join (threadHandle, 0);
+ }
+ }
+
void ThreadBase::Start ()
{
if (!IsRunning ())
- {
- ThreadStartParams param (this, runnable, &isRunning, &startupBarrier);
+ {
+ int res;
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_create(&threadHandle, &attr, proxyFunc, ¶m);
- pthread_attr_destroy(&attr);
-
- startupBarrier.Wait ();
+ // The default is a joinable thread.
+ res = pthread_create(&threadHandle, 0, proxyFunc, this);
+ if (res == 0)
+ {
+ isRunning = true;
- // Set priority to make sure its updated if we set it before starting
- SetPriority (priority);
+ // Set priority to make sure its updated if we set it before starting
+ SetPriority (priority);
+ }
}
}
@@ -113,25 +92,18 @@
{
if (IsRunning ())
{
- int res = pthread_cancel (threadHandle);
- if (res == 0)
- {
- AtomicOperations::Set (&isRunning, 0);
- }
+ isRunning = false;
+ pthread_cancel (threadHandle);
+ pthread_join (threadHandle, 0);
}
}
- bool ThreadBase::IsRunning () const
+ bool ThreadBase::SetPriority (ThreadPriority prio)
{
- return (AtomicOperations::Read ((int32*)&isRunning) != 0);
- }
+ int res = 0;
- bool ThreadBase::SetPriority (ThreadPriority prio)
- {
- int res = 1;
-
if (IsRunning ())
- {
+ {
struct sched_param SchedulerProperties;
// Clear the properties initially
@@ -156,23 +128,24 @@
}
}
- if (res != 0)
+ if (res == 0)
{
priority = prio;
}
- return res != 0;
+ return res == 0;
}
void ThreadBase::Wait () const
{
if (IsRunning ())
{
- pthread_join (threadHandle,0);
+ isRunning = false;
+ pthread_join (threadHandle, 0);
}
}
- void ThreadBase::Yield ()
+ void ThreadBase::Yield ()
{
sched_yield ();
}
Index: include/csutil/threading/win32_thread.h
===================================================================
--- include/csutil/threading/win32_thread.h (revision 39570)
+++ include/csutil/threading/win32_thread.h (working copy)
@@ -64,6 +64,11 @@
static CS::Threading::ThreadID GetThreadID ();
+ Runnable* GetRunnable () const
+ {
+ return runnable;
+ }
+
ThreadPriority GetPriority () const
{
return priority;
@@ -82,9 +87,7 @@
mutable void* threadHandle;
uint threadId;
- int32 isRunning;
ThreadPriority priority;
- Barrier startupBarrier;
static unsigned int __stdcall proxyFunc (void* param);
};
Index: libs/csutil/threading/win32_thread.cpp
===================================================================
--- libs/csutil/threading/win32_thread.cpp (revision 39565)
+++ libs/csutil/threading/win32_thread.cpp (working copy)
@@ -107,40 +107,12 @@
namespace Implementation
{
- namespace
+ static unsigned int __stdcall ThreadBase::proxyFunc (void* param)
{
-
- class ThreadStartParams : public CS::Memory::CustomAllocated
- {
- public:
- ThreadStartParams (ThreadBase* thread, Runnable* runner, int32* isRunningPtr,
- Barrier* startupBarrier)
- : thread (thread), runnable (runner), isRunningPtr (isRunningPtr),
- startupBarrier (startupBarrier)
- {
- }
-
- ThreadBase* thread;
- Runnable* runnable;
- int32* isRunningPtr;
- Barrier* startupBarrier;
- };
-
- }
-
- unsigned int __stdcall ThreadBase::proxyFunc (void* param)
- {
// Extract the parameters
- ThreadStartParams* tp = static_cast<ThreadStartParams*> (param);
- csRef<ThreadBase> thread (tp->thread);
- int32* isRunningPtr = tp->isRunningPtr;
- Runnable* runnable = tp->runnable;
- Barrier* startupBarrier = tp->startupBarrier;
+ ThreadBase* tb = static_cast<ThreadBase*> (param);
+ Runnable* runnable = tb->GetRunnable ();
- // Set as running and wait for main thread to catch up
- AtomicOperations::Set (isRunningPtr, 1);
- startupBarrier->Wait ();
-
// Set the name, for debugging
SetThreadName ((DWORD)-1, runnable->GetName ());
@@ -150,15 +122,12 @@
// Clean up TLSed objects
CleanupAllTlsInstances ();
- // Set as non-running
- AtomicOperations::Set (isRunningPtr, 0);
-
return 0;
}
ThreadBase::ThreadBase (Runnable* runnable)
- : runnable (runnable), threadHandle (0), threadId (0), isRunning (0),
- priority (THREAD_PRIO_NORMAL), startupBarrier (2)
+ : runnable (runnable), threadHandle (0), threadId (0),
+ priority (THREAD_PRIO_NORMAL)
{
}
@@ -171,16 +140,12 @@
{
if (!threadHandle)
{
- ThreadStartParams param (this, runnable, &isRunning, &startupBarrier);
-
// _beginthreadex does not always return a void*,
// on some versions of MSVC it gives uintptr_t
// and therefor needs a reinterpret_cast.
threadHandle = reinterpret_cast<void*> (_beginthreadex (0, 0, &proxyFunc,
- ¶m, 0, &threadId));
+ this, 0, &threadId));
- startupBarrier.Wait ();
-
// Set priority to make sure its updated if we set it before starting
SetPriority (priority);
}
@@ -194,14 +159,13 @@
if (res == 0)
{
threadHandle = 0;
- AtomicOperations::Set (&isRunning, 0);
}
}
}
bool ThreadBase::IsRunning () const
{
- return (AtomicOperations::Read ((int32*)&isRunning) != 0);
+ return threadHandle != 0;
}
bool ThreadBase::SetPriority (ThreadPriority prio)