corrections to exiting the program, now we should wait for all threads to finish but not crash nor freeze
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@15992 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -1,19 +1,14 @@
|
|||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// Name: thread.cpp
|
// Name: thread.cpp
|
||||||
// Purpose: wxWindows thread sample
|
// Purpose: wxWindows thread sample
|
||||||
// Author: Julian Smart(minimal)/Guilhem Lavaux(thread test)
|
// Author: Guilhem Lavaux, Vadim Zeitlin
|
||||||
// Modified by:
|
// Modified by:
|
||||||
// Created: 06/16/98
|
// Created: 06/16/98
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) Julian Smart, Markus Holzem, Guilhem Lavaux
|
// Copyright: (c) 1998-2002 wxWindows team
|
||||||
// Licence: wxWindows license
|
// Licence: wxWindows license
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: use worker threads to update progress controls instead of writing
|
|
||||||
messages - it will be more visual
|
|
||||||
*/
|
|
||||||
|
|
||||||
// For compilers that support precompilation, includes "wx/wx.h".
|
// For compilers that support precompilation, includes "wx/wx.h".
|
||||||
#include "wx/wxprec.h"
|
#include "wx/wxprec.h"
|
||||||
|
|
||||||
@@ -35,7 +30,9 @@
|
|||||||
|
|
||||||
#include "wx/progdlg.h"
|
#include "wx/progdlg.h"
|
||||||
|
|
||||||
|
// define this to use wxExecute in the exec tests, otherwise just use system
|
||||||
#define USE_EXECUTE
|
#define USE_EXECUTE
|
||||||
|
|
||||||
#ifdef USE_EXECUTE
|
#ifdef USE_EXECUTE
|
||||||
#define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
|
#define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
|
||||||
#else
|
#else
|
||||||
@@ -49,6 +46,9 @@ WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
|
|||||||
class MyApp : public wxApp
|
class MyApp : public wxApp
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
MyApp();
|
||||||
|
virtual ~MyApp();
|
||||||
|
|
||||||
virtual bool OnInit();
|
virtual bool OnInit();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -58,6 +58,15 @@ public:
|
|||||||
|
|
||||||
// crit section protects access to all of the arrays below
|
// crit section protects access to all of the arrays below
|
||||||
wxCriticalSection m_critsect;
|
wxCriticalSection m_critsect;
|
||||||
|
|
||||||
|
// the (mutex, condition) pair used to wait for the threads to exit, see
|
||||||
|
// MyFrame::OnQuit()
|
||||||
|
wxMutex m_mutexAllDone;
|
||||||
|
wxCondition m_condAllDone;
|
||||||
|
|
||||||
|
// the last exiting thread should signal m_condAllDone if this is true
|
||||||
|
// (protected by the same m_critsect)
|
||||||
|
bool m_waitingUntilAllDone;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a new application object
|
// Create a new application object
|
||||||
@@ -76,6 +85,7 @@ public:
|
|||||||
// accessors for MyWorkerThread (called in its context!)
|
// accessors for MyWorkerThread (called in its context!)
|
||||||
bool Cancelled();
|
bool Cancelled();
|
||||||
|
|
||||||
|
protected:
|
||||||
// callbacks
|
// callbacks
|
||||||
void OnQuit(wxCommandEvent& event);
|
void OnQuit(wxCommandEvent& event);
|
||||||
void OnClear(wxCommandEvent& event);
|
void OnClear(wxCommandEvent& event);
|
||||||
@@ -192,7 +202,21 @@ void MyThread::OnExit()
|
|||||||
{
|
{
|
||||||
wxCriticalSectionLocker locker(wxGetApp().m_critsect);
|
wxCriticalSectionLocker locker(wxGetApp().m_critsect);
|
||||||
|
|
||||||
wxGetApp().m_threads.Remove(this);
|
wxArrayThread& threads = wxGetApp().m_threads;
|
||||||
|
threads.Remove(this);
|
||||||
|
|
||||||
|
if ( threads.IsEmpty() )
|
||||||
|
{
|
||||||
|
// signal the main thread that there are no more threads left if it is
|
||||||
|
// waiting for us
|
||||||
|
if ( wxGetApp().m_waitingUntilAllDone )
|
||||||
|
{
|
||||||
|
wxGetApp().m_waitingUntilAllDone = FALSE;
|
||||||
|
|
||||||
|
wxMutexLocker lock(wxGetApp().m_mutexAllDone);
|
||||||
|
wxGetApp().m_condAllDone.Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *MyThread::Entry()
|
void *MyThread::Entry()
|
||||||
@@ -330,6 +354,22 @@ BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
|||||||
EVT_IDLE(MyFrame::OnIdle)
|
EVT_IDLE(MyFrame::OnIdle)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
|
MyApp::MyApp()
|
||||||
|
: m_condAllDone(m_mutexAllDone)
|
||||||
|
{
|
||||||
|
// the mutex associated with a condition must be initially locked, it will
|
||||||
|
// only be unlocked when we call Wait()
|
||||||
|
m_mutexAllDone.Lock();
|
||||||
|
|
||||||
|
m_waitingUntilAllDone = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyApp::~MyApp()
|
||||||
|
{
|
||||||
|
// the mutex must be unlocked before being destroyed
|
||||||
|
m_mutexAllDone.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
// `Main program' equivalent, creating windows and returning main app frame
|
// `Main program' equivalent, creating windows and returning main app frame
|
||||||
bool MyApp::OnInit()
|
bool MyApp::OnInit()
|
||||||
{
|
{
|
||||||
@@ -542,6 +582,8 @@ void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
|
|||||||
// set the frame title indicating the current number of threads
|
// set the frame title indicating the current number of threads
|
||||||
void MyFrame::OnIdle(wxIdleEvent &event)
|
void MyFrame::OnIdle(wxIdleEvent &event)
|
||||||
{
|
{
|
||||||
|
wxCriticalSectionLocker enter(wxGetApp().m_critsect);
|
||||||
|
|
||||||
// update the counts of running/total threads
|
// update the counts of running/total threads
|
||||||
size_t nRunning = 0,
|
size_t nRunning = 0,
|
||||||
nCount = wxGetApp().m_threads.Count();
|
nCount = wxGetApp().m_threads.Count();
|
||||||
@@ -563,10 +605,45 @@ void MyFrame::OnIdle(wxIdleEvent &event)
|
|||||||
|
|
||||||
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
||||||
{
|
{
|
||||||
const wxArrayThread& threads = wxGetApp().m_threads;
|
// NB: although the OS will terminate all the threads anyhow when the main
|
||||||
while ( !threads.IsEmpty() )
|
// one exits, it's good practice to do it ourselves -- even if it's not
|
||||||
|
// completely trivial in this example
|
||||||
|
|
||||||
|
// tell all the threads to terminate: note that they can't terminate while
|
||||||
|
// we're deleting them because they will block in their OnExit() -- this is
|
||||||
|
// important as otherwise we might access invalid array elements
|
||||||
{
|
{
|
||||||
threads[0]->Delete();
|
wxGetApp().m_critsect.Enter();
|
||||||
|
|
||||||
|
// check if we have any threads running first
|
||||||
|
const wxArrayThread& threads = wxGetApp().m_threads;
|
||||||
|
size_t count = threads.GetCount();
|
||||||
|
|
||||||
|
if ( count )
|
||||||
|
{
|
||||||
|
// we do, ask them to stop
|
||||||
|
for ( size_t n = 0; n < count; n++ )
|
||||||
|
{
|
||||||
|
threads[n]->Delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the flag for MyThread::OnExit()
|
||||||
|
wxGetApp().m_waitingUntilAllDone = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxGetApp().m_critsect.Leave();
|
||||||
|
|
||||||
|
if ( count )
|
||||||
|
{
|
||||||
|
// now wait for them to really terminate but leave the GUI mutex
|
||||||
|
// before doing it as otherwise we might dead lock
|
||||||
|
wxMutexGuiLeave();
|
||||||
|
|
||||||
|
wxGetApp().m_condAllDone.Wait();
|
||||||
|
|
||||||
|
wxMutexGuiEnter();
|
||||||
|
}
|
||||||
|
//else: no threads to terminate, no condition to wait for
|
||||||
}
|
}
|
||||||
|
|
||||||
Close(TRUE);
|
Close(TRUE);
|
||||||
|
Reference in New Issue
Block a user