Please help with GUI crash problem?

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
liubl
Experienced Solver
Experienced Solver
Posts: 63
Joined: Sun Apr 11, 2010 11:25 am

Please help with GUI crash problem?

Post by liubl » Sun Oct 02, 2011 1:17 pm

Hi, all,

I had a program (written in wxWidgets 2.8.10) which is occasionally crashed. I debugged into the program and found that it mainly crash on wxGrid updating. I don't have much experience on GUI coding; I doubt I may have used wxWidgets in the wrong way. I will try to explain clearly the main workflow of my app and the current way I used wxWidgets, please help me to figure out where the problem lies in.

I think my problem lies in that I update one wxGrid object simultaneously in many threads directly, but I am not sure if I should make a queue for wxGrid, and the threads only put data in the queue, and I will update the wxGrid object from the queue. I think my situation is a common case, there should be proven method to do it. Can you guide me to the right way?

Thanks in advance.
-Bruce
**************************************************************************************
The main work flow is as follows:

my app is a automatically futures trading system.

1) There are two kinds of data comes in by two separate threads, one is the market data thread (MDThread), and the other is the trade responses (TradeThread). The two threads work independently. They are both TCP connections to two separate server. And the package comes sequentially in each thread.

2) In my program, there are many strategies which computes based on the market data and decides if to send order to the market. Each strategy has a wxWidgets GUI windows to correspond with trader (mainly for traders to controls when to start/stop the strategy), and also there are information such as indexes related to the strategy showed on the GUI windows, which are mainly wxStaticText. Each strategy computes the data passed by the MDThread and if certain conditions are met, it will send order through TradeThread and got response from the TradeThread. Basically, each strategy work independently. But in certain condition, on market data will trigger several strategies to send order simultaneously.

3) There are also several common windows but only in two kinds, the first kind is a window contains a wxGrid and the other kind is wxLog window. These windows record information for all the strategies. So if there are changes triggered by some strategies, the data in these common windows got updated. There are three conditions which triggered the data update, the data from MDThread (to compute profit), the order sent by strategies, and responses from TradeThread. As you can imagine, the crash resides in these common windows.

Let me describes in detail how I implement the common window. I used a wxNotebook to hold several wxGrids, each wxGrid is a page.

I insert a new row as follows:

Code: Select all

int InsertOneRow(wxGrid *pGrid, int nRow)
{
	pGrid->BeginBatch();
	pGrid->InsertRows(nRow);
	pGrid->EndBatch();
	return nRow;
}
The delete row is as follows:

Code: Select all

bool DeleteOneRow(wxGrid *pGrid, int nRow)
{
	bool flag = false;
	pGrid->BeginBatch();
	flag = pGrid->DeleteRows(nRow, 1);
	pGrid->EndBatch();
	return flag;
}
I updated the value of one row as follows:

Code: Select all

void UpdatePosition(int row, CPositionSummary *pPosSummary)
{
	char price[64] = "";
	wxGridTableBase *pTable = m_pPosition->GetTable();			// m_pPosition is a wxGrid object
	if (NULL == pTable)
	{
		return ;
	}
	pTable->SetValue(row, PS_InstrumentID, wxString(pPosSummary->InstrumentID));

	pTable->SetValue(row, PS_TotalLongPos, GetPos(pPosSummary->TotalLongPos));
	
	FormatPrice(price, pPosSummary->AvgLongPrice.getValue());
	pTable->SetValue(row, PS_LongAvgPosPrice, _T(price));

	m_pPosition->ProcessTableMessage(wxGridTableMessage(pTable, wxGRIDTABLE_REQUEST_VIEW_GET_VALUES));
}
These functions of the common windows may be called simultaneously.

Some typical crash stack are as follows:

Code: Select all

TraderMax.exe!wxTrap()  row 678	C++
TraderMax.exe!ShowAssertDialog(const char * szFile=0x00c39434, int nLine=352, const char * szFunc=0x023420d4, const char * szCond=0x00c39484, const char * szMsg=0x00c394b0, wxAppTraits * traits=0x0230b8b0)  row 825	C++
TraderMax.exe!wxAppConsole::OnAssertFailure(const char * file=0x00c39434, int line=352, const char * func=0x023420d4, const char * cond=0x00c39484, const char * msg=0x00c394b0)  row 445 + 0x22 bytes	C++
TraderMax.exe!wxOnAssert(const char * szFile=0x00c39434, int nLine=352, const char * szFunc=0x00c39464, const char * szCond=0x00c39484, const char * szMsg=0x00c394b0)  row 713	C++
TraderMax.exe!wxStringBase::wxStringBase(const wxStringBase & stringSrc={...})  row 351 + 0x36 bytes	C++
TraderMax.exe!wxString::wxString(const wxString & stringSrc={...})  row 690 + 0x2f bytes	C++
TraderMax.exe!wxGridStringTable::GetValue(int row=0, int col=1)  row 3492 + 0x23 bytes	C++
TraderMax.exe!tmPositionPane::UpdateProfits(const CWriteableMarketData * pMarketData=0x02cc02f8)  row 488 + 0x1c bytes	C++
TraderMax.exe!tmMarketDataSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField * pDepthMarketData=0x022cfb90)  row 212	C++

Code: Select all

user32.dll!77d1904e() 	 
uxtheme.dll!5adc4a83() 	 
user32.dll!77d19096() 	 
TraderMax.exe!wxWindow::SetScrollbar(int orient, int pos, int pageSize, int range, bool refresh)  row 1006	 
TraderMax.exe!wxScrollHelper::AdjustScrollbars()  row 775	 
TraderMax.exe!wxScrollHelper::SetScrollbars(int pixelsPerUnitX, int pixelsPerUnitY, int noUnitsX, int noUnitsY, int xPos, int yPos, bool noRefresh)  row 401	 
TraderMax.exe!wxGrid::CalcDimensions()  row 4793	 
TraderMax.exe!wxGrid::EndBatch()  row 8371	 
TraderMax.exe!tmPane::DeleteOneRow(wxGrid * pGrid, int nRow)  row 377	 
TraderMax.exe!tmOrderPane::LogOrder(const CWriteableOrders * pOrder)  row 43

Code: Select all

user32.dll!77d194be() 	 
user32.dll!77d2c174() 	 
user32.dll!77d3b0b0() 	 
TraderMax.exe!wxGetWindowText(void * hWnd)  row 366 + 0x34 bytes	 
TraderMax.exe!wxTextCtrl::GetRange(long from, long to)  row 818 + 0x12 bytes	 
TraderMax.exe!wxTextCtrl::GetValue()  row 713 + 0x18 bytes	 
TraderMax.exe!wxTextCtrl::DoSetValue(const wxString & value, int flags)  row 841 + 0x23 bytes	 
TraderMax.exe!wxTextCtrlBase::SetValue(const wxString & value)  row 304 + 0x3b bytes	 
TraderMax.exe!tmDoubleSpinCtrl::NotifyValue()  row 142 + 0x66 bytes	 
TraderMax.exe!tmDoubleSpinCtrl::SetValue(double value)  row 90	 
TraderMax.exe!tmChannelPane::UpdateManualPrice(CThostFtdcDepthMarketDataField * pMarketData)  row 1376	 
TraderMax.exe!tmChannelPane::Handle(CThostFtdcDepthMarketDataField * pMarketData)  row 627	 
TraderMax.exe!tmHandler::HandleMarketData(CThostFtdcDepthMarketDataField * pMarketData)  row 114 + 0x16 bytes	

User avatar
doublemax
Moderator
Moderator
Posts: 15676
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Please help with GUI crash problem?

Post by doublemax » Sun Oct 02, 2011 2:22 pm

I think my problem lies in that I update one wxGrid object simultaneously in many threads directly
That definitely sounds like a problem. wxWidgets does not allow access to GUI elements from secondary threads.

From the code snippets it's not clear which of this methods are called from secondary threads and/or if there are any mutexes to avoid simultaneous access to shared objects from multiple threads.
but I am not sure if I should make a queue for wxGrid, and the threads only put data in the queue, and I will update the wxGrid object from the queue.
That's what i would have suggested.

You also must take care when using wxString, as it's not thread-safe. If you copy a wxString in a secondary thread to another wxString that will (directly or indirectly) be used from another thread, you have a problem. wxString uses copy-on-write and problems occur if this copy operation is interrupted by a thread switch.

To solve this you can use wxString::c_str() to enforce a "deep copy" when copying a wxString.

Code: Select all

wxString a="hello";
wxString b=a;        // dangerous when b will be accessed later from a different thread, because a and b internally will be pointing to the same string data in memory.
wxString b(a.c_str()); // by passing a "const wxChar *" the wxString ctor will copy the whole data
If you use a custom event to send data from a secondary thread to the main thread, it's important that you use this method to create deep copies of all wxStrings in the Clone() method of your event class.

Another option is to tell wxWidgets to use std::string internally which does not use copy-on-write. How to do that depends on your compiler/ide. When using Visual Studio for example, you would set wxUSE_STD_STRING in setup.h to 1. For other build methods this may have to be passed as a parameter to a configure or make call.
Use the source, Luke!

RainRat
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Thu Jan 06, 2011 11:26 pm

Re: Please help with GUI crash problem?

Post by RainRat » Sun Oct 02, 2011 6:00 pm

liubl wrote: but I am not sure if I should make a queue for wxGrid, and the threads only put data in the queue, and I will update the wxGrid object from the queue.
Yes, you should use a queue.

I use a queue of unprocessed data and a queue of processed data. The queues actually just hold a pointer to an object. So the GUI thread creates the object using new, pushes the pointer onto the queue, sends a wxCondition::Signal() (or Broadcast). The worker thread wakes up, pops the pointer to the object off the queue and processes it. It then pushes the processed data (pointer) onto the output queue and sends an event using QueueEvent to the GUI thread. The GUI thread then handles this by popping the data pointer, displaying it, then deleting the data using delete.

This can be facilitated by creating a class that contains a std::queue, wxMutex and wxCondition, so that you can lock the queue and signal when it is non-empty between the threads.

There is a brief example of using queues with two threads in the GUI Programming with WxWidgets book.

liubl
Experienced Solver
Experienced Solver
Posts: 63
Joined: Sun Apr 11, 2010 11:25 am

Re: Please help with GUI crash problem?

Post by liubl » Mon Oct 03, 2011 2:49 am

Thanks a lot, I will first do the queue thread and then check the wxString usage. I think I should recompile wxWidgets using the std:string with Visual Studio.

Is it the 17th chapter (Writing Multithreaded Applications) of the GUI Programming with WxWidgets book? I will check it up and then do my code modification.

I will let you know the result:-)

RainRat
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Thu Jan 06, 2011 11:26 pm

Re: Please help with GUI crash problem?

Post by RainRat » Mon Oct 03, 2011 5:47 am

liubl wrote:Is it the 17th chapter (Writing Multithreaded Applications) of the GUI Programming with WxWidgets book? I will check it up and then do my code modification.
Yes, the "wxCondition Example" is a good starting point. The general outline is

Code: Select all

while( not_done ) {
  queue_mutex.Lock()
  while( input_queue.empty() ) {
    queue_condition.Wait();
  }
  // Not empty, so...
  some_data =  input_queue.front();
  input_queue.pop();
  queue_condition.Unlock
   // Process the some_data
}
The problem with this is you need to wake the thread to exit the program gracefully, but if you wake it and the queue is empty, it just goes back into Wait(). So you need to have a flag or check in the while( input_queue.empty() ) loop to break out of the outer loop before attempting to use front() or pop() on the empty queue.

Seems like there should be a standard way of handling this sort of queue processing thing, but I haven't found a good example, so had to work out some details myself.

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Re: Please help with GUI crash problem?

Post by Auria » Mon Oct 03, 2011 2:37 pm

RainRat wrote: Seems like there should be a standard way of handling this sort of queue processing thing, but I haven't found a good example, so had to work out some details myself.
In wx 2.9 there is : http://docs.wxwidgets.org/trunk/classwx ... _01_4.html
"Keyboard not detected. Press F1 to continue"
-- Windows

RainRat
I live to help wx-kind
I live to help wx-kind
Posts: 178
Joined: Thu Jan 06, 2011 11:26 pm

Re: Please help with GUI crash problem?

Post by RainRat » Mon Oct 03, 2011 6:21 pm

Excellent! How did I miss this? :oops: This should simplify things a bit.

I looked at wx/msgqueue.h. As I suspected this class wraps a std::queue, wxMutex and wxCondition into one class and handles the locking, waiting, and signaling in Receive and Post. And as the docs point out,
Notice that often there is a some special message indicating that the thread should terminate as there is no other way to gracefully shutdown a thread waiting on the message queue.
I guess the way to wake a thread in order to end it, would be to Post() a special end_the_thread message. This could be a null pointer for example that the worker thread would check to see if it should end. The message queue could be emptied using Clear() before the end message if we need to end the thread as soon as possible.

Code: Select all

// worker thread
while(1) {
  in_message_queue.Receive(data_ptr);
  if(data_ptr == NULL)
	  break;
   // Process the data_ptr and post something to out_message_queue
   // or attach output to event sent to GUI using QueueEvent
}
It seems like the GUI thread should receive messages using QueueEvent rather than having a wxMessageQueue for output. Otherwise, the GUI would need to worry about periodically checking the output wxMessageQueue without blocking. Or have an output wxMessageQueue, but require the worker thread to send an event using QueueEvent to signal that there is a new message at which point the GUI thread would attempt to ReceiveTimeout() the message.

Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Re: Please help with GUI crash problem?

Post by Auria » Tue Oct 04, 2011 12:39 am

Yes, to communicate from a worker thread back to the main thread you would use the "normal" wxWidgets event loop ( the usual wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication#Sending_events_to_the_main_thread )
"Keyboard not detected. Press F1 to continue"
-- Windows

Post Reply