Revision: 22188
http://opalvoip.svn.sourceforge.net/opalvoip/?rev=22188&view=rev
Author: csoutheren
Date: 2009-03-11 02:28:39 +0000 (Wed, 11 Mar 2009)
Log Message:
-----------
Remove thread resource leakage
Modified Paths:
--------------
ptlib/trunk/src/ptlib/unix/tlibthrd.cxx
Modified: ptlib/trunk/src/ptlib/unix/tlibthrd.cxx
===================================================================
--- ptlib/trunk/src/ptlib/unix/tlibthrd.cxx 2009-03-10 04:34:10 UTC (rev 22187)
+++ ptlib/trunk/src/ptlib/unix/tlibthrd.cxx 2009-03-11 02:28:39 UTC (rev 22188)
@@ -74,7 +74,7 @@
unsigned line)
{
if (retval == 0) {
- PTRACE_IF(2, retry > 0, "PWLib\t" << funcname << " required " << retry << " retries!");
+ PTRACE_IF(2, retry > 0, "PTLib\t" << funcname << " required " << retry << " retries!");
return PFalse;
}
@@ -176,7 +176,7 @@
struct rlimit rl;
PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
maxHandles = rl.rlim_cur;
- PTRACE(4, "PWLib\tMaximum per-process file handles is " << maxHandles);
+ PTRACE(4, "PTLib\tMaximum per-process file handles is " << maxHandles);
#else
maxHandles = 500; // arbitrary value
#endif
@@ -206,13 +206,13 @@
PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
maxHandles = rl.rlim_cur;
if (maxHandles == newMax) {
- PTRACE(2, "PWLib\tNew maximum per-process file handles set to " << maxHandles);
+ PTRACE(2, "PTLib\tNew maximum per-process file handles set to " << maxHandles);
return PTrue;
}
}
#endif // !P_RTEMS
- PTRACE(1, "PWLib\tCannot set per-process file handle limit to "
+ PTRACE(1, "PTLib\tCannot set per-process file handle limit to "
<< newMax << " (is " << maxHandles << ") - check permissions");
return PFalse;
}
@@ -222,18 +222,26 @@
{
PreShutdown();
+ PTRACE(5, "PTLib\tAbout to shutdown housekeeping thread");
+
// Don't wait for housekeeper to stop if Terminate() is called from it.
if (housekeepingThread != NULL && PThread::Current() != housekeepingThread) {
housekeepingThread->SetClosing();
+ PTRACE(5, "PTLib\tSetClosing");
SignalTimerChange();
+ PTRACE(5, "PTLib\tSignalTimerChange");
housekeepingThread->WaitForTermination();
+ PTRACE(5, "PTLib\tWaitForTermination");
delete housekeepingThread;
+ PTRACE(5, "PTLib\tdelete housekeepingThread");
}
CommonDestruct();
- PTRACE(5, "PWLib\tDestroyed process " << this);
+ PTRACE(5, "PTLib\tCommonDestruct");
PostShutdown();
+
+ PTRACE(5, "PTLib\tDestroyed process " << this);
}
PBoolean PProcess::PThreadKill(pthread_t id, unsigned sig)
@@ -259,10 +267,21 @@
//////////////////////////////////////////////////////////////////////////////
+//
+// Called to construct a PThread for either:
+//
+// a) The primordial PProcesss thread
+// b) A non-PTLib thread that needs to use PTLib routines, such as PTRACE
+//
+// This is always called in the context of the running thread, so naturally, the thread
+// is not paused
+//
+
PThread::PThread()
{
autoDelete = PFalse;
+ // PX_origStackSize = 0 indicates external thread
PX_origStackSize = 0;
PX_threadId = pthread_self();
PX_priority = NormalPriority;
@@ -296,6 +315,12 @@
}
+//
+// Called to construct a PThread for a normal PTLib thread.
+//
+// This is always called in the context of some other thread, and
+// the PThread is always created in the paused state
+//
PThread::PThread(PINDEX stackSize,
AutoDeleteFlag deletion,
Priority priorityLevel,
@@ -305,11 +330,15 @@
autoDelete = (deletion == AutoDeleteThread);
PAssert(stackSize > 0, PInvalidParameter);
- PX_origStackSize = stackSize;
- PX_threadId = 0;
PX_priority = priorityLevel;
PX_suspendCount = 1;
+ // PX_origStackSize != 0 indicates PTLib created thread
+ PX_origStackSize = stackSize;
+
+ // PX_threadId = 0 indicates thread has not started
+ PX_threadId = 0;
+
#ifndef P_HAS_SEMAPHORES
PX_waitingSemaphore = NULL;
PX_WaitSemMutex = MutexInitialiser;
@@ -327,21 +356,50 @@
// new thread is actually started the first time Resume() is called.
PX_firstTimeStart = PTrue;
- PTRACE(5, "PWLib\tCreated thread " << this << ' ' << threadName);
+ PTRACE(5, "PTLib\tCreated thread " << this << ' ' << threadName);
}
+//
+// Called to destruct a PThread
+//
+// If not called in the context of the thread being destroyed, we need to wait
+// for that thread to stop before continuing
+//
PThread::~PThread()
{
- pthread_t id = PX_threadId;
+ if (PProcessInstance != NULL) {
+ pthread_t id = PX_threadId;
+ PProcess & process = PProcess::Current();
- if (PX_threadId != 0 && PX_threadId != pthread_self())
- Terminate();
+ // need to terminate the thread if it was ever started and it is not us
+ if ((id != 0) && (id != pthread_self()))
+ Terminate();
+ // cause the housekeeping thread to be created, if not already running
+ process.SignalTimerChange();
+
+ // last gasp tracing
+ PTRACE(1, "PTLib\tDestroyed thread " << this << ' ' << threadName << "(id = " << ::hex << id << ::dec << ")");
+
+ // clean up tracing now, because it depends on the PThread::Current which depends on the activeThreads list
#if PTRACING
- PTrace::Cleanup();
+ PTrace::Cleanup();
#endif
+ // if thread was started, remove it from the active thread list and detach it to release thread resources
+ if (id != 0) {
+ process.activeThreadMutex.Wait();
+ pthread_detach(id);
+ process.activeThreads.SetAt((unsigned)id, NULL);
+ process.activeThreadMutex.Signal();
+ }
+
+ // cause the housekeeping thread to wake up (we know it must be running)
+ process.SignalTimerChange();
+ }
+
+ // close I/O unblock pipes
PAssertPTHREAD(::close, (unblockPipe[0]));
PAssertPTHREAD(::close, (unblockPipe[1]));
@@ -353,16 +411,6 @@
pthread_mutex_trylock(&PX_suspendMutex);
pthread_mutex_unlock(&PX_suspendMutex);
pthread_mutex_destroy(&PX_suspendMutex);
-
- // PProcess is a subclass of PThread, if the PProcess instance is deleted,
- // this destructor runs but PProcessInstance is already set to NULL
- if (PProcessInstance != NULL) {
- if (this == PProcessInstance)
- PProcessInstance = NULL;
- else {
- PTRACE_IF(1, !autoDelete, "PWLib\tDestroyed thread " << this << ' ' << threadName << "(id = " << ::hex << id << ::dec << ")");
- }
- }
}
@@ -419,7 +467,7 @@
// unlock the thread list
process.activeThreadMutex.Signal();
- PTRACE_IF(4, newHighWaterMark > 0, "PWLib\tThread high water mark set: " << newHighWaterMark);
+ PTRACE_IF(4, newHighWaterMark > 0, "PTLib\tThread high water mark set: " << newHighWaterMark);
#ifdef P_MACOSX
if (PX_priority == HighestPriority) {
@@ -665,7 +713,7 @@
default:
/* Unknown scheduler. We don't know what priority this thread has. */
- PTRACE(1, "PWLib\tPThread::GetPriority: unknown scheduling policy #" << schedulingPolicy);
+ PTRACE(1, "PTLib\tPThread::GetPriority: unknown scheduling policy #" << schedulingPolicy);
}
#endif
@@ -731,22 +779,29 @@
}
+//
+// Terminate the specified thread
+//
void PThread::Terminate()
{
+ // if thread was not created by PTLib, then don't terminate it
if (PX_origStackSize <= 0)
return;
+ // if thread calls Terminate on itself, then do it
// don't use PThread::Current, as the thread may already not be in the
// active threads list
if (PX_threadId == pthread_self()) {
pthread_exit(0);
- return;
+ return; // keeps compiler happy
}
+ // if the thread is already terminated, then nothing to do
if (IsTerminated())
return;
- PTRACE(2, "PWLib\tForcing termination of thread " << (void *)this);
+ // otherwise force thread to die
+ PTRACE(2, "PTLib\tForcing termination of thread " << (void *)this);
PXAbortBlock();
WaitForTermination(20);
@@ -772,33 +827,38 @@
}
+//
+// See if thread is still running
+// Note PX_threadId = 0 means thread has been created but not yet started
+//
PBoolean PThread::IsTerminated() const
{
pthread_t id = PX_threadId;
- return (id == 0) || !PPThreadKill(id, 0);
+ return (id == 0) || (pthread_kill(id, 0) != 0);
}
+//
+// Wait for thread to terminate
+//
+//
void PThread::WaitForTermination() const
{
- if (this == Current()) {
+ // if thread not started or is the current thread, then nothing to do
+ pthread_t id = PX_threadId;
+ if ((id == 0) || (id == Current()->GetThreadId())) {
PTRACE(2, "WaitForTermination short circuited");
return;
}
- PXAbortBlock(); // this assist in clean shutdowns on some systems
+ // this assist in clean shutdowns on some systems
+ PXAbortBlock();
+ // wait for termination
while (!IsTerminated()) {
Sleep(10); // sleep for 10ms. This slows down the busy loop removing 100%
// CPU usage and also yeilds so other threads can run.
}
-
- if (PX_threadId != 0) {
- // Calling pthread_join with PX_threadId=0 => bang!
- // Inform the helgrind finite state machine that this thread can reclaim memory ownership
- pthread_join(PX_threadId, NULL);
- PX_threadId = 0;
- }
}
@@ -809,7 +869,7 @@
return PTrue;
}
- PTRACE(6, "PWLib\tWaitForTermination(" << maxWait << ')');
+ PTRACE(6, "PTLib\tWaitForTermination(" << maxWait << ')');
PXAbortBlock(); // this assist in clean shutdowns on some systems
PTimer timeout = maxWait;
@@ -820,13 +880,6 @@
// CPU usage and also yeilds so other threads can run.
}
- if (PX_threadId != 0) {
- // Calling pthread_join with 0 => bang!
- // Inform the helgrind finite state machine that this thread can reclaim memory ownership
- pthread_join(PX_threadId, NULL);
- PX_threadId = 0;
- }
-
return PTrue;
}
@@ -834,7 +887,6 @@
void * PThread::PX_ThreadStart(void * arg)
{
PThread * thread = (PThread *)arg;
- //don't need to detach the the thread, it was created in the PTHREAD_CREATE_DETACHED state
// Added this to guarantee that the thread creation (PThread::Restart)
// has completed before we start the thread. Then the PX_threadId has
// been set.
@@ -845,7 +897,7 @@
// make sure the cleanup routine is called when the thread exits
pthread_cleanup_push(&PThread::PX_ThreadEnd, arg);
- PTRACE(5, "PWLib\tStarted thread " << thread << ' ' << thread->GetThreadName());
+ PTRACE(5, "PTLib\tStarted thread " << thread << ' ' << thread->GetThreadName());
PProcess::Current().OnThreadStart(*thread);
@@ -855,11 +907,6 @@
// execute the cleanup routine
pthread_cleanup_pop(1);
-#if PTRACING
- // cleanup the thread-specific tracing
- PTrace::Cleanup();
-#endif
-
// Inform the helgrind finite state machine that this thread has finished
pthread_exit(0);
@@ -871,46 +918,20 @@
{
PThread * thread = (PThread *)arg;
PProcess & process = PProcess::Current();
+PTRACE(1, "Called PX_ThreadEnd with " << thread->autoDelete);
process.OnThreadEnded(*thread);
- // Calls to PTRACE() MUST be made with activeThreadMutex unlocked, or deadlocks may occur.
- // (PTRACE() itself calls PThread::Current()). Also, PTRACE must be done BEFORE removing the
- // thread from the activeThreads dictionary, or PTrace::Current() will create a new
- // PExternalThread instance, causing the very same deadlock
- PTRACE(5, "PWLib\tEnded thread " << thread << ' ' << thread->GetThreadName());
-
- process.activeThreadMutex.Wait();
-
- pthread_t id = thread->GetThreadId();
- if (id == 0) {
- // Don't know why, but pthreads under Linux at least can call this function
- // multiple times! Probably a bug, but we have to allow for it.
- process.activeThreadMutex.Signal();
- PTRACE(2, "PWLib\tAttempted to multiply end thread " << thread << " ThreadID=" << (void *)id);
- return;
- }
-
- // remove this thread from the active thread list
- process.activeThreads.SetAt((unsigned)id, NULL);
-
- bool deleteThread = thread->autoDelete; // Get flag before releasing lock
-
- process.activeThreadMutex.Signal();
-
- if (deleteThread) {
- /* It is now safe to delete this thread. Note that this thread
- is deleted after the process.activeThreadMutex.Signal(), which means
- PWaitAndSignal(process.activeThreadMutex) could not be used */
+ bool deleteThread = thread->autoDelete; // make copy of the flag before releasing lock
+ if (deleteThread)
delete thread;
- }
}
int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout)
{
- PTRACE(7, "PWLib\tPThread::PXBlockOnIO(" << handle << ',' << type << ')');
+ PTRACE(7, "PTLib\tPThread::PXBlockOnIO(" << handle << ',' << type << ')');
if ((handle < 0) || (handle >= PProcess::Current().GetMaxHandles())) {
- PTRACE(2, "PWLib\tAttempt to use illegal handle in PThread::PXBlockOnIO, handle=" << handle);
+ PTRACE(2, "PTLib\tAttempt to use illegal handle in PThread::PXBlockOnIO, handle=" << handle);
errno = EBADF;
return -1;
}
@@ -957,7 +978,7 @@
::read(unblockPipe[0], &ch, 1);
errno = EINTR;
retval = -1;
- PTRACE(6, "PWLib\tUnblocked I/O fd=" << unblockPipe[0]);
+ PTRACE(6, "PTLib\tUnblocked I/O fd=" << unblockPipe[0]);
}
return retval;
@@ -967,7 +988,7 @@
{
static BYTE ch = 0;
::write(unblockPipe[1], &ch, 1);
- PTRACE(6, "PWLib\tUnblocking I/O fd=" << unblockPipe[0] << " thread=" << GetThreadName());
+ PTRACE(6, "PTLib\tUnblocking I/O fd=" << unblockPipe[0] << " thread=" << GetThreadName());
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|