Our application uses the WPF and .NET Framwork 4.8
The AbortableBackgroundWorkder class inherences from the BackgroundWorker class and used as following.
ProtocolProcessor class performs complicated sequence instrument control actions.
we notice that sometimes due to instrument control error and console application Abort _protocolProcessorBackgroundWorker and event "OnProtocolProcessorRunEvent()" did not get called.
if the event "OnProtocolProcessorRunEvent()" is called, it will show a dialog to indicate there is a instrument error.
when instrument error happens, it called "GenerateInstrumentExceptionWarningOrFault()" event and write several lines into the run log file.
when ProtocolProcessor::Execute() finally block, it writes several lines into run log file.
when the instrument error dialog did not show, we notice that "GenerateInstrumentExceptionWarningOrFault()" event wrote several lines and mixed with writhed lines from ProtocolProcessor::Execute() finally block.
if the code can keep all wrote lines from "GenerateInstrumentExceptionWarningOrFault()" event and after that, it shows wrote lines from ProtocolProcessor::Execute() finally block, the instrument error dialog will display.
Through the code example, are there any problems?
Is this safe to add "Thread.Sleep"? Thx!
public class ProtocolProcessor
{
public void Execute()
{
try
{
// Raise (and log) the Macro Started event
MarkMacroStarted();
// a lot instrument control sequence actions
.......
EvaluateMacroStopPoint( "Macro Finished" );
}
catch( Exception ex )
{
WriteToRunLog( "!!! Exception Occurred in Macro !!! - " + ex.Message );
stoppedByError = true;
throw;
}
finally
{
// Thread.Sleep(200); //is it safe to sleep in finally block?
// handle case where the macro was stopped by the user (not aborted)
// exception occurred, was logged and thrown again
TurnOffAllHeatersAndStopLogging(); //include write sth into run log
}
}
}
private AbortableBackgroundWorker _protocolProcessorBackgroundWorker = null;
private ProtocolProcessor _protocolProcessorMacro = null;
Public void BeginXXXRun()
{
InstrumentControl.Instance.ExceptionThrown += OnInstrumentControlExceptionThrownDuringProtocol;
_protocolProcessorBackgroundWorker = new AbortableBackgroundWorker();
_protocolProcessorBackgroundWorker.WorkerSupportsCancellation = true;
_protocolProcessorBackgroundWorker.DoWork += (_, __) =>
{
try
{
_protocolProcessorMacro = new ProtocolProcessor( );
_protocolProcessorMacro.ProtocolProcessorRunEvent += OnProtocolProcessorRunEvent;
_protocolProcessorMacro.PreExecute();
_protocolProcessorMacro.Execute();
_protocolProcessorMacro.PostExecute();
}
catch ( ThreadAbortException /* taEx */ )
{
}
catch ( Exception ex )
{
GenerateInstrumentExceptionWarningOrFault( ex );
throw;
}
finally
{
_protocolProcessorMacro.StopLoggingToFile();
}
};
_protocolProcessorBackgroundWorker.RunWorkerCompleted += OnProtocolProcessorRunComplete;
_protocolProcessorBackgroundWorker.RunWorkerAsync();
}
}
private void OnInstrumentControlExceptionThrownDuringProtocol( object sender, InstrumentControl.ExceptionThrownEventArgs args )
{
BioCodeException bioEx = args.InstrumentException;
string abortedBy = "Instrument Error";
WriteToRunLog( "!!! AbortRun( " + abortedBy + " )" ); //write into run log
_protocolProcessorBackgroundWorker.Abort(abortedBy);
LogInstrumentExceptionInRunLog( bioEx ); // write logs in run log
GenerateInstrumentExceptionWarningOrFault( bioEx ); // saving warning or error into a file
}
private void LogInstrumentExceptionInRunLog( Exception ex )
{
string message = "!!! " + ex.Message;
}
private void GenerateInstrumentExceptionWarningOrFault( Exception ex )
{
WriteToRunLog( "**** !! Exception - " + ex.Message ); //write into run log
BioCodeException bioEx = PackageBioCodeException( ex );
EventLogger eventLogger = EventLogger.Instance;
if ( eventLogger != null )
{
eventLogger.GenerateFaultLog( bioEx ); //generate event log file
}
}
private void OnProtocolProcessorRunComplete( object sender, RunWorkerCompletedEventArgs e )
{
bool wasAborted = e.Cancelled;
string abortedBy;
if ( wasAborted )
{
abortedBy = _protocolProcessorBackgroundWorker.AbortedBy.ToString();
HaltInstrumentOnBackgroundThread();
}
if ( _protocolProcessorMacro != null )
{
_protocolProcessorMacro.ProtocolProcessorRunEvent -= OnProtocolProcessorRunEvent;
_protocolProcessorMacro = null;
}
RaiseProtocolProcessorRunEvent( "", PROTOCOLFINISH, new string[] { abortedBy } );
}
///-----------------------------------------------------------------------------------------------------------------
/// Class (AbortableBackgroundWorker)
///-----------------------------------------------------------------------------------------------------------------
public class AbortableBackgroundWorker : BackgroundWorker
{
///-------------------------------------------------------------------------------------------------------------
/// Properties
///-------------------------------------------------------------------------------------------------------------
public string AbortedBy { get; set; }
public bool IsAlive { get { return _workerThread != null && _workerThread.IsAlive; } }
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// Handles DoWork event
/// </summary>
///-------------------------------------------------------------------------------------------------------------
protected override void OnDoWork( DoWorkEventArgs args )
{
// get the thread from the base class
_workerThread = Thread.CurrentThread;
try
{
base.OnDoWork( args );
}
catch( ThreadAbortException )
{
args.Cancel = true; // we must set Cancel property to true!
Thread.ResetAbort(); // prevents ThreadAbortException propagation
}
}
[SecurityPermissionAttribute( SecurityAction.Demand, ControlThread = true )]
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// Aborts the background worker.
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public void Abort()
{
Abort( "" );
}
[SecurityPermissionAttribute( SecurityAction.Demand, ControlThread = true )]
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// Aborts the background worker.
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public void Abort( object state = null )
{
AbortedBy = state.ToString();
if ( _workerThread != null )
{
_workerThread.Abort( state );
_workerThread = null;
}
}
[SecurityPermissionAttribute( SecurityAction.Demand, ControlThread = true )]
///-------------------------------------------------------------------------------------------------------------
/// <summary>
/// Aborts the background worker.
/// </summary>
///-------------------------------------------------------------------------------------------------------------
public bool Join( int timeout )
{
if ( _workerThread != null )
{
return _workerThread.Join( timeout );
}
else
{
return true;
}
}
///-------------------------------------------------------------------------------------------------------------
/// Member Variables
///-------------------------------------------------------------------------------------------------------------
private Thread _workerThread = null;
}