TestExecWindow
 All Modules Pages
Do not forget to deal with multi threading

Overview:

The problem - unknown threads may call

Within TestExecWindow many interactions start with pushing a button at the GUI. Those actions are executed within the safe main GUI thread (dispatcher thread).

But we also react on asynhronous events which will arrive on some notification thread:

Critical situations may occur when data are accessed both from GUI thread and some notification thread. Unsynchronized changes may lead to corrupt data and program crash. When concerning GUI elements (buttons, list boxes etc.) access is only allowed from GUI thread. Visual Studio will rise an exception when access is tried from some other thread.

A simple solution - synchronize via GUI thread

Assume we want to write a message to the log pane by calling the service function TestExecWindowControl.AddInfoToEventList. We cannot be sure which thread will call this function. But within GUI (TestExecWindowControl.xaml.cs) we can pass the call to the dispatcher thread:

public void AddInfoToEventList(string info)
{
// Redirect to main GUI thread
lstEvents.Dispatcher.Invoke(new Action(() => AddInfoToEventListFromGuiThread(info)));
}
private void AddInfoToEventListFromGuiThread(string info)
{
// here we are sure to be within GUI thread and we can
// safely access the list box
lstEvents.Items.Add(info);
/// ...

An analogous problem occurs when we receive the message about the end of the test process within main class TestExecWindow. In this case we also use the GUI thread for synchronization:

public void OnTestTerminated(bool in_success, string in_args, bool in_memLeaksDetected, TimeSpan in_executionTime)
{
// This function is called when the test app terminates.
// The call may arrive on any system thread and needs to be synchronized.
// Simply pass all data to GUI where the main GUI thread (dispatcher thread)
// can be used for synchronization.
Gui().TestTerminated(in_success, in_args, in_memLeaksDetected, in_executionTime);
}

The GUI again dispatches to the safe GUI thread:

public void TestTerminated(bool in_success, string in_args, bool in_memLeaksDetected, TimeSpan in_executionTime)
{
// For safe updating of status data process within GUI thread
this.Dispatcher.Invoke(new Action(() =>
TestTerminatedWithinGuiThread(in_success, "", in_args, in_memLeaksDetected, in_executionTime)));
}
private void TestTerminatedWithinGuiThread(bool in_success, string infoAboutTest, string in_args, bool in_memLeaksDetected, TimeSpan in_executionTime)
{
m_executionTimer.Stop();
// Display execution status
string info = in_args;
if (info == "")
{
info = "<no args set>";
}
string durationStr;
if (in_executionTime.TotalSeconds < 1.0)
{
durationStr = "<1s ";
}
else
{
double totalNumMinutes = in_executionTime.TotalSeconds / 60.0;
durationStr = totalNumMinutes.ToString("F1")+ "m ";
}
//int totalNumSeconds = Convert.ToInt32(Math.Round(in_executionTime.TotalSeconds));
//int totalNumMinutes = totalNumSeconds / 60;
//int numSeconds = totalNumSeconds % 60;
//int numSecondsAsDecimalMinute = numSeconds / 6;
//string durationStr = totalNumMinutes + "," + numSecondsAsDecimalMinute + "m ";
WriteLine(1, (!in_success ? "FAILED: " : (in_memLeaksDetected ? "FAILED (MEM_LEAKS): " : "OK: "))
+ durationStr + GetInfoAboutCurrentTest() + " " + info);
bool failure = !in_success || in_memLeaksDetected;
int idx = (int)m_curRunMode;
++m_numExecutedTests[idx];
if (failure)
{
++m_numFailedTests[idx];
}
bool testSucceededUpToNow = (m_numFailedTests[idx] == 0);
// Safe update of state variables and refresh
// of GUI controls within GUI thread
SetState(testSucceededUpToNow);
/// ...