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;
WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);
// Define a new application type
// ----------------------------------------------------------------------------
// the application object
// ----------------------------------------------------------------------------
class MyApp : public wxApp
{
public:
@ -82,7 +85,10 @@ public:
bool m_shuttingDown;
};
// Define a new frame type
// ----------------------------------------------------------------------------
// the main application frame
// ----------------------------------------------------------------------------
class MyFrame: public wxFrame
{
public:
@ -119,8 +125,8 @@ private:
void OnResumeThread(wxCommandEvent& event);
void OnStartWorker(wxCommandEvent& event);
void OnExecMain(wxCommandEvent& event);
void OnStartGUIThread(wxCommandEvent& event);
void OnShowCPUs(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
@ -163,9 +169,7 @@ private:
// was the worker thread cancelled by user?
bool m_cancelled;
// protects m_cancelled
wxCriticalSection m_critsectWork;
wxCriticalSection m_csCancelled; // protects m_cancelled
DECLARE_EVENT_TABLE()
};
@ -186,18 +190,19 @@ enum
THREAD_STOP_THREAD,
THREAD_PAUSE_THREAD,
THREAD_RESUME_THREAD,
THREAD_START_WORKER,
THREAD_START_WORKER,
THREAD_EXEC_MAIN,
THREAD_EXEC_THREAD,
THREAD_START_GUI_THREAD,
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
@ -221,7 +226,7 @@ public:
};
// ----------------------------------------------------------------------------
// worker thread
// a worker thread
// ----------------------------------------------------------------------------
class MyWorkerThread : public wxThread
@ -241,6 +246,53 @@ public:
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
// ============================================================================
@ -290,8 +342,10 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)
EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
EVT_MENU(THREAD_START_GUI_THREAD, MyFrame::OnStartGUIThread)
EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)
@ -327,6 +381,7 @@ MyFrame::MyFrame(wxFrame *frame, const wxString& title,
menuThread->AppendSeparator();
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_START_GUI_THREAD, _T("Launch a &GUI thread\tF6"));
menuBar->Append(menuThread, _T("&Thread"));
wxMenu *menuHelp = new wxMenu;
@ -392,6 +447,53 @@ MyThread *MyFrame::CreateThread()
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) )
{
static long s_num;
@ -528,42 +630,6 @@ void MyFrame::OnIdle(wxIdleEvent& event)
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) )
{
Close(true);
@ -681,21 +747,99 @@ void MyFrame::OnWorkerEvent(wxThreadEvent& event)
{
if ( !m_dlgProgress->Update(n) )
{
wxCriticalSectionLocker lock(m_critsectWork);
wxCriticalSectionLocker lock(m_csCancelled);
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
// ----------------------------------------------------------------------------
@ -727,7 +871,7 @@ MyThread::~MyThread()
}
}
void *MyThread::Entry()
wxThread::ExitCode MyThread::Entry()
{
wxString text;
@ -769,6 +913,12 @@ void *MyThread::Entry()
// 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)
: wxThread()
{
@ -780,13 +930,7 @@ void MyWorkerThread::OnExit()
{
}
// 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
void *MyWorkerThread::Entry()
wxThread::ExitCode MyWorkerThread::Entry()
{
#if TEST_YIELD_RACE_CONDITION
if ( TestDestroy() )
@ -824,3 +968,43 @@ void *MyWorkerThread::Entry()
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;
}