show wxMutexGuiEnter/Leave by drawing into a bitmap from a secondary thread; show the result while it's being created by a MyImageDialog dialog; small cleanup

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@59059 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Francesco Montorsi
2009-02-20 16:07:50 +00:00
parent 8bf30d0f8b
commit c1b8f155dc

View File

@@ -59,7 +59,10 @@
class MyThread; class MyThread;
WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread); WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
// Define a new application type // ----------------------------------------------------------------------------
// the application object
// ----------------------------------------------------------------------------
class MyApp : public wxApp class MyApp : public wxApp
{ {
public: public:
@@ -82,7 +85,10 @@ public:
bool m_shuttingDown; bool m_shuttingDown;
}; };
// Define a new frame type // ----------------------------------------------------------------------------
// the main application frame
// ----------------------------------------------------------------------------
class MyFrame: public wxFrame class MyFrame: public wxFrame
{ {
public: public:
@@ -119,8 +125,8 @@ private:
void OnResumeThread(wxCommandEvent& event); void OnResumeThread(wxCommandEvent& event);
void OnStartWorker(wxCommandEvent& event); void OnStartWorker(wxCommandEvent& event);
void OnExecMain(wxCommandEvent& event); void OnExecMain(wxCommandEvent& event);
void OnStartGUIThread(wxCommandEvent& event);
void OnShowCPUs(wxCommandEvent& event); void OnShowCPUs(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event); void OnAbout(wxCommandEvent& event);
@@ -163,9 +169,7 @@ private:
// was the worker thread cancelled by user? // was the worker thread cancelled by user?
bool m_cancelled; bool m_cancelled;
wxCriticalSection m_csCancelled; // protects m_cancelled
// protects m_cancelled
wxCriticalSection m_critsectWork;
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };
@@ -186,18 +190,19 @@ enum
THREAD_STOP_THREAD, THREAD_STOP_THREAD,
THREAD_PAUSE_THREAD, THREAD_PAUSE_THREAD,
THREAD_RESUME_THREAD, THREAD_RESUME_THREAD,
THREAD_START_WORKER,
THREAD_START_WORKER,
THREAD_EXEC_MAIN, THREAD_EXEC_MAIN,
THREAD_EXEC_THREAD, THREAD_START_GUI_THREAD,
THREAD_SHOWCPUS, THREAD_SHOWCPUS,
WORKER_EVENT = wxID_HIGHEST+1 // this one gets sent from the worker thread WORKER_EVENT = wxID_HIGHEST+1, // this one gets sent from MyWorkerThread
GUITHREAD_EVENT // this one gets sent from MyGUIThread
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// GUI thread // a simple thread
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class MyThread : public wxThread class MyThread : public wxThread
@@ -221,7 +226,7 @@ public:
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// worker thread // a worker thread
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class MyWorkerThread : public wxThread class MyWorkerThread : public wxThread
@@ -241,6 +246,53 @@ public:
unsigned m_count; unsigned m_count;
}; };
// ----------------------------------------------------------------------------
// a thread which executes GUI calls using wxMutexGuiEnter/Leave
// ----------------------------------------------------------------------------
#define GUITHREAD_BMP_SIZE 300
#define GUITHREAD_NUM_UPDATES 50
class MyImageDialog;
class MyGUIThread : public wxThread
{
public:
MyGUIThread(MyImageDialog *dlg) : wxThread(wxTHREAD_JOINABLE)
{
m_dlg = dlg;
}
virtual ExitCode Entry();
private:
MyImageDialog *m_dlg;
};
// ----------------------------------------------------------------------------
// an helper dialog used by MyFrame::OnStartGUIThread
// ----------------------------------------------------------------------------
class MyImageDialog: public wxDialog
{
public:
// ctor
MyImageDialog(wxFrame *frame);
~MyImageDialog();
// stuff used by MyGUIThread:
wxBitmap m_bmp; // the bitmap drawn by MyGUIThread
wxCriticalSection m_csBmp; // protects m_bmp
private:
void OnGUIThreadEvent(wxThreadEvent& event);
void OnPaint(wxPaintEvent&);
MyGUIThread m_thread;
int m_nCurrentProgress;
DECLARE_EVENT_TABLE()
};
// ============================================================================ // ============================================================================
// implementation // implementation
// ============================================================================ // ============================================================================
@@ -290,8 +342,10 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread) EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread) EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread) EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker) EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain) EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs) EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout) EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
@@ -327,6 +381,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString& title,
menuThread->AppendSeparator(); menuThread->AppendSeparator();
menuThread->Append(THREAD_START_WORKER, _T("Start a &worker thread\tCtrl-W")); menuThread->Append(THREAD_START_WORKER, _T("Start a &worker thread\tCtrl-W"));
menuThread->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5")); menuThread->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5"));
menuThread->Append(THREAD_START_GUI_THREAD, _T("Launch a &GUI thread\tF6"));
menuBar->Append(menuThread, _T("&Thread")); menuBar->Append(menuThread, _T("&Thread"));
wxMenu *menuHelp = new wxMenu; wxMenu *menuHelp = new wxMenu;
@@ -392,6 +447,53 @@ MyThread *MyFrame::CreateThread()
return thread; return thread;
} }
void MyFrame::DoLogThreadMessages()
{
wxCriticalSectionLocker lock(m_csMessages);
const size_t count = m_messages.size();
for ( size_t n = 0; n < count; n++ )
{
m_txtctrl->AppendText(m_messages[n]);
}
m_messages.clear();
}
void MyFrame::UpdateThreadStatus()
{
wxCriticalSectionLocker enter(wxGetApp().m_critsect);
// update the counts of running/total threads
size_t nRunning = 0,
nCount = wxGetApp().m_threads.Count();
for ( size_t n = 0; n < nCount; n++ )
{
if ( wxGetApp().m_threads[n]->IsRunning() )
nRunning++;
}
if ( nCount != m_nCount || nRunning != m_nRunning )
{
m_nRunning = nRunning;
m_nCount = nCount;
wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
}
//else: avoid flicker - don't print anything
}
bool MyFrame::Cancelled()
{
wxCriticalSectionLocker lock(m_csCancelled);
return m_cancelled;
}
// ----------------------------------------------------------------------------
// MyFrame - event handlers
// ----------------------------------------------------------------------------
void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
{ {
static long s_num; static long s_num;
@@ -528,42 +630,6 @@ void MyFrame::OnIdle(wxIdleEvent& event)
event.Skip(); event.Skip();
} }
void MyFrame::DoLogThreadMessages()
{
wxCriticalSectionLocker lock(m_csMessages);
const size_t count = m_messages.size();
for ( size_t n = 0; n < count; n++ )
{
m_txtctrl->AppendText(m_messages[n]);
}
m_messages.clear();
}
void MyFrame::UpdateThreadStatus()
{
wxCriticalSectionLocker enter(wxGetApp().m_critsect);
// update the counts of running/total threads
size_t nRunning = 0,
nCount = wxGetApp().m_threads.Count();
for ( size_t n = 0; n < nCount; n++ )
{
if ( wxGetApp().m_threads[n]->IsRunning() )
nRunning++;
}
if ( nCount != m_nCount || nRunning != m_nRunning )
{
m_nRunning = nRunning;
m_nCount = nCount;
wxLogStatus(this, wxT("%u threads total, %u running."), unsigned(nCount), unsigned(nRunning));
}
//else: avoid flicker - don't print anything
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) ) void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
{ {
Close(true); Close(true);
@@ -681,21 +747,99 @@ void MyFrame::OnWorkerEvent(wxThreadEvent& event)
{ {
if ( !m_dlgProgress->Update(n) ) if ( !m_dlgProgress->Update(n) )
{ {
wxCriticalSectionLocker lock(m_critsectWork); wxCriticalSectionLocker lock(m_csCancelled);
m_cancelled = true; m_cancelled = true;
} }
} }
} }
bool MyFrame::Cancelled() void MyFrame::OnStartGUIThread(wxCommandEvent& WXUNUSED(event))
{ {
wxCriticalSectionLocker lock(m_critsectWork); MyImageDialog dlg(this);
return m_cancelled; dlg.ShowModal();
} }
// ----------------------------------------------------------------------------
// MyImageDialog
// ----------------------------------------------------------------------------
BEGIN_EVENT_TABLE(MyImageDialog, wxDialog)
EVT_THREAD(GUITHREAD_EVENT, MyImageDialog::OnGUIThreadEvent)
EVT_PAINT(MyImageDialog::OnPaint)
END_EVENT_TABLE()
MyImageDialog::MyImageDialog(wxFrame *parent)
: wxDialog(parent, wxID_ANY, "Image created by a secondary thread",
wxDefaultPosition, wxSize(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE)*1.5, wxDEFAULT_DIALOG_STYLE),
m_thread(this)
{
m_nCurrentProgress = 0;
CentreOnScreen();
// NOTE: no need to lock m_csBmp until the thread isn't started:
// create the bitmap
if (!m_bmp.Create(GUITHREAD_BMP_SIZE,GUITHREAD_BMP_SIZE) || !m_bmp.IsOk())
{
wxLogError("Couldn't create the bitmap!");
return;
}
// clean it
wxMemoryDC dc(m_bmp);
dc.SetBackground(*wxBLACK_BRUSH);
dc.Clear();
// draw the bitmap from a secondary thread
if ( m_thread.Create() != wxTHREAD_NO_ERROR ||
m_thread.Run() != wxTHREAD_NO_ERROR )
{
wxLogError(wxT("Can't create/run thread!"));
return;
}
}
MyImageDialog::~MyImageDialog()
{
// in case our thread is still running and for some reason we are destroyed,
// do wait for the thread to complete as it assumes that its MyImageDialog
// pointer is always valid
m_thread.Delete();
}
void MyImageDialog::OnGUIThreadEvent(wxThreadEvent& event)
{
m_nCurrentProgress = int(((float)event.GetInt()*100)/GUITHREAD_NUM_UPDATES);
Refresh();
}
void MyImageDialog::OnPaint(wxPaintEvent& WXUNUSED(evt))
{
wxPaintDC dc(this);
const wxSize& sz = dc.GetSize();
{
// paint the bitmap
wxCriticalSectionLocker locker(m_csBmp);
dc.DrawBitmap(m_bmp, (sz.GetWidth()-GUITHREAD_BMP_SIZE)/2,
(sz.GetHeight()-GUITHREAD_BMP_SIZE)/2);
}
// paint a sort of progress bar with a 10px border:
dc.SetBrush(*wxRED_BRUSH);
dc.DrawRectangle(10,10, 10+m_nCurrentProgress*(GUITHREAD_BMP_SIZE-20)/100,30);
dc.SetTextForeground(*wxBLUE);
dc.DrawText(wxString::Format("%d%%", m_nCurrentProgress),
(sz.GetWidth()-dc.GetCharWidth()*2)/2,
25-dc.GetCharHeight()/2);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// MyThread // MyThread
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -727,7 +871,7 @@ MyThread::~MyThread()
} }
} }
void *MyThread::Entry() wxThread::ExitCode MyThread::Entry()
{ {
wxString text; wxString text;
@@ -769,6 +913,12 @@ void *MyThread::Entry()
// MyWorkerThread // MyWorkerThread
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update
// function provokes a race condition in which the second wxThreadEvent posted by
// MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
// and results in the destruction of the progress dialog itself, resulting in a crash later.
#define TEST_YIELD_RACE_CONDITION 0
MyWorkerThread::MyWorkerThread(MyFrame *frame) MyWorkerThread::MyWorkerThread(MyFrame *frame)
: wxThread() : wxThread()
{ {
@@ -780,13 +930,7 @@ void MyWorkerThread::OnExit()
{ {
} }
// define this symbol to 1 to test if the YieldFor() call in the wxProgressDialog::Update wxThread::ExitCode MyWorkerThread::Entry()
// function provokes a race condition in which the second wxThreadEvent posted by
// MyWorkerThread::Entry is processed by the YieldFor() call of wxProgressDialog::Update
// and results in the destruction of the progress dialog itself, resulting in a crash later.
#define TEST_YIELD_RACE_CONDITION 0
void *MyWorkerThread::Entry()
{ {
#if TEST_YIELD_RACE_CONDITION #if TEST_YIELD_RACE_CONDITION
if ( TestDestroy() ) if ( TestDestroy() )
@@ -824,3 +968,43 @@ void *MyWorkerThread::Entry()
return NULL; return NULL;
} }
// ----------------------------------------------------------------------------
// MyGUIThread
// ----------------------------------------------------------------------------
wxThread::ExitCode MyGUIThread::Entry()
{
for (int i=0; i<GUITHREAD_NUM_UPDATES && !TestDestroy(); i++)
{
// inform the GUI toolkit that we're going to use GUI functions
// from a secondary thread:
wxMutexGuiEnter();
{
wxCriticalSectionLocker lock(m_dlg->m_csBmp);
// draw some more stuff on the bitmap
wxMemoryDC dc(m_dlg->m_bmp);
dc.SetBrush((i%2)==0 ? *wxBLUE_BRUSH : *wxGREEN_BRUSH);
dc.DrawRectangle(rand()%GUITHREAD_BMP_SIZE, rand()%GUITHREAD_BMP_SIZE, 30, 30);
// simulate long drawing time:
wxMilliSleep(200);
}
// if we don't release the GUI mutex the MyImageDialog won't be able to refresh
wxMutexGuiLeave();
// notify the dialog that another piece of our masterpiece is complete:
wxThreadEvent event( wxEVT_COMMAND_THREAD, GUITHREAD_EVENT );
event.SetInt(i);
wxQueueEvent( m_dlg, event.Clone() );
// give the main thread the time to refresh before we lock the GUI mutex again
// FIXME: find a better way to do this!
wxMilliSleep(100);
}
return (ExitCode)0;
}