I am using .NET 2.0 and the finally block does not seem to be getting executed if the Thread times out. For example, if I see the message "Child Thread Timed Out...", I will not see the message "Finally block started...". This means that the database objects (Oracle.DataAccess) may not be getting cleaned up properly. Is there a way to force the cleanup inside the child thread, or should the cleanup be moved to the main thread and pass in the database objects to the child thread?
private void runThread(string strSP, object objThreadParameter)
{
try
{
bool blnThreadCompletedOK = true;
Thread threadHelper = new Thread(getData);
threadHelper.Start(objThreadParameter);
// Wait for called thread.
blnThreadCompletedOK = threadHelper.Join(THREAD_TIMEOUT);
if (blnThreadCompletedOK)
{
// Thread has completed and should have stopped running.
// i.e. the thread has processed normally or an exception has been copied to the objExceptionThread object.
if (objExceptionThread != null)
{
throw objExceptionThread;
}
}
else
{
System.Diagnostics.EventLog.WriteEntry("Main thread", "Child Thread Timed Out...", System.Diagnostics.EventLogEntryType.Warning);
// Main thread has timed out waiting for the child thread. Likely the child thread is still running.
if (threadHelper.IsAlive)
{
threadHelper.Abort(); // This will trigger the exception handling in the child thread and cause the finally
// block to be executed.
}
throw (new Exception("The call to " + strSP + "() timed out as it exceeded " + (THREAD_TIMEOUT / 1000).ToString() + " seconds"));
}
}
catch (Exception exc)
{
throw new PivotalApplicationException(exc.Message, exc, mrsysSystem);
}
}
private void getData(object objThreadParameter)
{
OracleCommand oraCmd = null;
OracleConnection oraConn = null;
OracleDataReader dr = null;
try
{
// Initialization.
int intMAX_RETRIES = 20; // Maximum number of retries.
int intRETRY_DROP_POOL = 5; // At some point, if connections are still failing, try clearing the pool.
// Other initialization stuff...
// Now execute the SP.
for (int i = 1; i <= intMAX_RETRIES; i++)
{
try
{
try
{
// Setup Oracle connection and initialize Oracle command object.
getOracleConnection(out oraConn, connString);
}
catch (Exception exc)
{
throw new Exception("Error in getData() setting up connection - " + exc.Message);
}
try
{
oraCmd = new OracleCommand(strSP, oraConn);
setupCommand (out oraCmd);
}
catch (Exception exc)
{
throw new Exception("Error in getData() setting up parameters - " + exc.Message);
}
try
{
dr = oraCmd.ExecuteReader();
break; // Success, so, leave the for loop.
}
catch (Exception exc)
{
throw new Exception("Error in getData() executing command.\n\n" + strParametersMsg + " \n\n" + exc.Message);
}
}
catch (Exception excInner)
{
if (i >= intMAX_RETRIES)
{
throw new Exception(excInner.Message);
}
else
{
// Cleanup oraCmd, oraConn, oraDr...
}
}
}
try
{
// Process results...
}
catch (Exception exc)
{
throw new Exception("Error in getData() processing results - " + exc.Message);
}
// Now set the variables that are shared between the Main thread and this thread...
}
catch (Exception exc)
{
logMessage(exc.Source + " " + exc.Message);
objExceptionThread = exc; // Initialize exception in Main Thread...
}
finally
{
System.Diagnostics.EventLog.WriteEntry("Child Thread", "Finally block started...", System.Diagnostics.EventLogEntryType.Warning);
// With .NET 2.0 and later, the finally block should always be executed correctly for a Thread.Abort()
if (!(dr == null))
{
dr.Dispose();
}
if (!(oraCmd == null))
{
oraCmd.Dispose();
}
if (!(oraConn == null))
{
oraConn.Close();
oraConn.Dispose();
}
System.Diagnostics.EventLog.WriteEntry("Child Thread", "Finally block completed...", System.Diagnostics.EventLogEntryType.Warning);
}
}