thread fixes for MSW (samples doesn't compile currently under !MSW, will
be fixed a.s.a.p.) git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@1351 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -139,13 +139,13 @@ private:
|
|||||||
class WXDLLEXPORT wxCriticalSectionLocker
|
class WXDLLEXPORT wxCriticalSectionLocker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxCriticalSectionLocker(wxCriticalSection *critsect)
|
wxCriticalSectionLocker(wxCriticalSection& critsect) : m_critsect(critsect)
|
||||||
{ (m_critsect = critsect)->Enter(); }
|
{ m_critsect.Enter(); }
|
||||||
~wxCriticalSectionLocker()
|
~wxCriticalSectionLocker()
|
||||||
{ m_critsect->Leave(); }
|
{ m_critsect.Leave(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxCriticalSection *m_critsect;
|
wxCriticalSection& m_critsect;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -253,6 +253,20 @@ private:
|
|||||||
void WXDLLEXPORT wxMutexGuiEnter();
|
void WXDLLEXPORT wxMutexGuiEnter();
|
||||||
void WXDLLEXPORT wxMutexGuiLeave();
|
void WXDLLEXPORT wxMutexGuiLeave();
|
||||||
|
|
||||||
|
// implementation only
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
// unlock GUI if there are threads waiting for and lock it back when
|
||||||
|
// there are no more of them - should be called periodically by the main
|
||||||
|
// thread
|
||||||
|
void WXDLLEXPORT wxMutexGuiLeaveOrEnter();
|
||||||
|
|
||||||
|
// returns TRUE if the main thread has GUI lock
|
||||||
|
inline bool WXDLLEXPORT wxGuiOwnedByMainThread();
|
||||||
|
|
||||||
|
// wakes up the main thread if it's sleeping inside ::GetMessage()
|
||||||
|
inline void WXDLLEXPORT wxWakeUpMainThread();
|
||||||
|
#endif // MSW
|
||||||
|
|
||||||
#else // !wxUSE_THREADS
|
#else // !wxUSE_THREADS
|
||||||
|
|
||||||
// no thread support
|
// no thread support
|
||||||
@@ -261,4 +275,12 @@ inline void WXDLLEXPORT wxMutexGuiLeave() { }
|
|||||||
|
|
||||||
#endif // wxUSE_THREADS
|
#endif // wxUSE_THREADS
|
||||||
|
|
||||||
|
// automatically unlock GUI mutex in dtor
|
||||||
|
class WXDLLEXPORT wxMutexGuiLocker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
wxMutexGuiLocker() { wxMutexGuiEnter(); }
|
||||||
|
~wxMutexGuiLocker() { wxMutexGuiLeave(); }
|
||||||
|
};
|
||||||
|
|
||||||
#endif // __THREADH__
|
#endif // __THREADH__
|
||||||
|
@@ -9,9 +9,17 @@
|
|||||||
// Licence: wxWindows license
|
// Licence: wxWindows license
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
|
||||||
|
1. show how SetPriority() works.
|
||||||
|
2. use worker threads to update progress controls instead of writing
|
||||||
|
messages - it will be more visual
|
||||||
|
*/
|
||||||
|
|
||||||
#ifdef __GNUG__
|
#ifdef __GNUG__
|
||||||
#pragma implementation "test.cpp"
|
#pragma implementation "test.cpp"
|
||||||
#pragma interface "test.cpp"
|
#pragma interface "test.cpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For compilers that support precompilation, includes "wx/wx.h".
|
// For compilers that support precompilation, includes "wx/wx.h".
|
||||||
@@ -55,26 +63,35 @@ public:
|
|||||||
// callbacks
|
// callbacks
|
||||||
void OnQuit(wxCommandEvent& event);
|
void OnQuit(wxCommandEvent& event);
|
||||||
void OnAbout(wxCommandEvent& event);
|
void OnAbout(wxCommandEvent& event);
|
||||||
|
void OnClear(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnStartThread(wxCommandEvent& event);
|
void OnStartThread(wxCommandEvent& event);
|
||||||
void OnStopThread(wxCommandEvent& event);
|
void OnStopThread(wxCommandEvent& event);
|
||||||
void OnPauseThread(wxCommandEvent& event);
|
void OnPauseThread(wxCommandEvent& event);
|
||||||
void OnResumeThread(wxCommandEvent& event);
|
void OnResumeThread(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnSize(wxSizeEvent &event);
|
|
||||||
void OnIdle(wxIdleEvent &event);
|
void OnIdle(wxIdleEvent &event);
|
||||||
bool OnClose() { return TRUE; }
|
bool OnClose() { return TRUE; }
|
||||||
|
|
||||||
|
// called by dying thread
|
||||||
|
void OnThreadExit(wxThread *thread);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wxArrayThread m_threads;
|
wxArrayThread m_threads;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxTextCtrl *m_txtctrl;
|
void DeleteThread(size_t index);
|
||||||
|
|
||||||
|
// crit section protects access to the array below
|
||||||
|
wxCriticalSection m_critsect;
|
||||||
|
wxArrayInt m_aToDelete;
|
||||||
|
|
||||||
|
wxTextCtrl *m_txtctrl;
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyThread: public wxThread
|
class MyThread : public wxThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MyThread(MyFrame *frame);
|
MyThread(MyFrame *frame);
|
||||||
@@ -82,6 +99,9 @@ public:
|
|||||||
// thread execution starts here
|
// thread execution starts here
|
||||||
virtual void *Entry();
|
virtual void *Entry();
|
||||||
|
|
||||||
|
// called when the thread exits - whether
|
||||||
|
virtual void OnExit();
|
||||||
|
|
||||||
// write something to the text control
|
// write something to the text control
|
||||||
void WriteText(const wxString& text);
|
void WriteText(const wxString& text);
|
||||||
|
|
||||||
@@ -104,9 +124,14 @@ void MyThread::WriteText(const wxString& text)
|
|||||||
|
|
||||||
// before doing any GUI calls we must ensure that this thread is the only
|
// before doing any GUI calls we must ensure that this thread is the only
|
||||||
// one doing it!
|
// one doing it!
|
||||||
wxMutexGuiEnter();
|
wxMutexGuiLocker guiLocker;
|
||||||
|
|
||||||
m_frame->WriteText(msg);
|
m_frame->WriteText(msg);
|
||||||
wxMutexGuiLeave();
|
}
|
||||||
|
|
||||||
|
void MyThread::OnExit()
|
||||||
|
{
|
||||||
|
m_frame->OnThreadExit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *MyThread::Entry()
|
void *MyThread::Entry()
|
||||||
@@ -118,7 +143,7 @@ void *MyThread::Entry()
|
|||||||
text.Printf("Thread 0x%x started.\n", GetID());
|
text.Printf("Thread 0x%x started.\n", GetID());
|
||||||
WriteText(text);
|
WriteText(text);
|
||||||
|
|
||||||
for ( m_count = 0; m_count < 20; m_count++ )
|
for ( m_count = 0; m_count < 10; m_count++ )
|
||||||
{
|
{
|
||||||
// check if we were asked to exit
|
// check if we were asked to exit
|
||||||
if ( TestDestroy() )
|
if ( TestDestroy() )
|
||||||
@@ -127,7 +152,11 @@ void *MyThread::Entry()
|
|||||||
text.Printf("[%u] Thread 0x%x here.\n", m_count, GetID());
|
text.Printf("[%u] Thread 0x%x here.\n", m_count, GetID());
|
||||||
WriteText(text);
|
WriteText(text);
|
||||||
|
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
::Sleep(1000);
|
||||||
|
#else
|
||||||
wxSleep(1);
|
wxSleep(1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
text.Printf("Thread 0x%x finished.\n", GetID());
|
text.Printf("Thread 0x%x finished.\n", GetID());
|
||||||
@@ -142,22 +171,22 @@ enum
|
|||||||
TEST_QUIT = 1,
|
TEST_QUIT = 1,
|
||||||
TEST_TEXT = 101,
|
TEST_TEXT = 101,
|
||||||
TEST_ABOUT = 102,
|
TEST_ABOUT = 102,
|
||||||
TEST_START_THREAD = 103,
|
TEST_CLEAR = 103,
|
||||||
TEST_STOP_THREAD = 104,
|
TEST_START_THREAD = 203,
|
||||||
TEST_PAUSE_THREAD = 105,
|
TEST_STOP_THREAD = 204,
|
||||||
TEST_RESUME_THREAD = 106
|
TEST_PAUSE_THREAD = 205,
|
||||||
|
TEST_RESUME_THREAD = 206
|
||||||
};
|
};
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
||||||
EVT_MENU(TEST_QUIT, MyFrame::OnQuit)
|
EVT_MENU(TEST_QUIT, MyFrame::OnQuit)
|
||||||
EVT_MENU(TEST_ABOUT, MyFrame::OnAbout)
|
EVT_MENU(TEST_ABOUT, MyFrame::OnAbout)
|
||||||
|
EVT_MENU(TEST_CLEAR, MyFrame::OnClear)
|
||||||
EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread)
|
EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread)
|
||||||
EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread)
|
EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread)
|
||||||
EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread)
|
EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread)
|
||||||
EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread)
|
EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread)
|
||||||
|
|
||||||
EVT_SIZE(MyFrame::OnSize)
|
|
||||||
|
|
||||||
EVT_IDLE(MyFrame::OnIdle)
|
EVT_IDLE(MyFrame::OnIdle)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
@@ -168,12 +197,16 @@ IMPLEMENT_APP (MyApp)
|
|||||||
bool MyApp::OnInit()
|
bool MyApp::OnInit()
|
||||||
{
|
{
|
||||||
// Create the main frame window
|
// Create the main frame window
|
||||||
MyFrame *frame = new MyFrame((wxFrame *)NULL, "", 50, 50, 450, 340);
|
MyFrame *frame = new MyFrame((wxFrame *)NULL, "wxWindows threads sample",
|
||||||
|
50, 50, 450, 340);
|
||||||
|
|
||||||
// Make a menubar
|
// Make a menubar
|
||||||
wxMenu *file_menu = new wxMenu;
|
wxMenu *file_menu = new wxMenu;
|
||||||
|
|
||||||
|
file_menu->Append(TEST_CLEAR, "&Clear log");
|
||||||
|
file_menu->AppendSeparator();
|
||||||
file_menu->Append(TEST_ABOUT, "&About");
|
file_menu->Append(TEST_ABOUT, "&About");
|
||||||
|
file_menu->AppendSeparator();
|
||||||
file_menu->Append(TEST_QUIT, "E&xit");
|
file_menu->Append(TEST_QUIT, "E&xit");
|
||||||
wxMenuBar *menu_bar = new wxMenuBar;
|
wxMenuBar *menu_bar = new wxMenuBar;
|
||||||
menu_bar->Append(file_menu, "&File");
|
menu_bar->Append(file_menu, "&File");
|
||||||
@@ -199,13 +232,11 @@ bool MyApp::OnInit()
|
|||||||
MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
|
MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
|
||||||
: wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
|
: wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
|
||||||
{
|
{
|
||||||
wxPanel *panel = new wxPanel(this, -1, wxPoint(0, 0), wxSize(400, 200),
|
CreateStatusBar();
|
||||||
wxTAB_TRAVERSAL);
|
|
||||||
|
|
||||||
m_txtctrl = new wxTextCtrl(panel, -1, "", wxPoint(10,30), wxSize(390, 190),
|
m_txtctrl = new wxTextCtrl(this, -1, "", wxPoint(0, 0), wxSize(0, 0),
|
||||||
wxTE_MULTILINE);
|
wxTE_MULTILINE | wxTE_READONLY);
|
||||||
|
|
||||||
(void)new wxStaticText(panel, -1, "Log window", wxPoint(10,10));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
|
||||||
@@ -214,35 +245,43 @@ void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
|
|||||||
|
|
||||||
thread->Create();
|
thread->Create();
|
||||||
|
|
||||||
|
wxCriticalSectionLocker enter(m_critsect);
|
||||||
m_threads.Add(thread);
|
m_threads.Add(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
|
||||||
{
|
{
|
||||||
int no_thrd = m_threads.Count()-1;
|
int no_thrd = m_threads.Count() - 1;
|
||||||
|
|
||||||
|
if ( no_thrd < 0 )
|
||||||
|
{
|
||||||
|
wxLogError("No thread to stop!");
|
||||||
|
|
||||||
if (no_thrd < 0)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
delete m_threads[no_thrd];
|
DeleteThread(no_thrd);
|
||||||
m_threads.Remove(no_thrd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
|
||||||
{
|
{
|
||||||
|
wxCriticalSectionLocker enter(m_critsect);
|
||||||
|
|
||||||
// resume first suspended thread
|
// resume first suspended thread
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
while ( n < m_threads.Count() && m_threads[n]->IsPaused() )
|
while ( n < m_threads.Count() && m_threads[n]->IsPaused() )
|
||||||
n--;
|
n--;
|
||||||
|
|
||||||
if ( n < 0 )
|
if ( n < 0 )
|
||||||
wxLogError("No thread to pause!");
|
wxLogError("No thread to resume!");
|
||||||
else
|
else
|
||||||
m_threads[n]->Resume();
|
m_threads[n]->Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
|
||||||
{
|
{
|
||||||
|
wxCriticalSectionLocker enter(m_critsect);
|
||||||
|
|
||||||
// pause last running thread
|
// pause last running thread
|
||||||
int n = m_threads.Count() - 1;
|
int n = m_threads.Count() - 1;
|
||||||
while ( n >= 0 && !m_threads[n]->IsRunning() )
|
while ( n >= 0 && !m_threads[n]->IsRunning() )
|
||||||
@@ -257,6 +296,17 @@ 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)
|
||||||
{
|
{
|
||||||
|
// first remove from the array all the threads which died since last call
|
||||||
|
{
|
||||||
|
wxCriticalSectionLocker enter(m_critsect);
|
||||||
|
|
||||||
|
size_t nCount = m_aToDelete.Count();
|
||||||
|
for ( size_t n = 0; n < nCount; n++ )
|
||||||
|
DeleteThread((size_t)m_aToDelete[n]);
|
||||||
|
|
||||||
|
m_aToDelete.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
size_t nRunning = 0,
|
size_t nRunning = 0,
|
||||||
nCount = m_threads.Count();
|
nCount = m_threads.Count();
|
||||||
for ( size_t n = 0; n < nCount; n++ )
|
for ( size_t n = 0; n < nCount; n++ )
|
||||||
@@ -265,19 +315,7 @@ void MyFrame::OnIdle(wxIdleEvent &event)
|
|||||||
nRunning++;
|
nRunning++;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString title;
|
wxLogStatus(this, "%u threads total, %u running.", nCount, nRunning);
|
||||||
title.Printf("wxWindows thread sample (%u threads, %u running).",
|
|
||||||
nCount, nRunning);
|
|
||||||
SetTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MyFrame::OnSize(wxSizeEvent& event)
|
|
||||||
{
|
|
||||||
wxFrame::OnSize(event);
|
|
||||||
|
|
||||||
wxSize size( GetClientSize() );
|
|
||||||
|
|
||||||
m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
||||||
@@ -291,11 +329,30 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
|||||||
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
|
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
|
||||||
{
|
{
|
||||||
wxMessageDialog dialog(this, "wxThread sample (based on minimal)\n"
|
wxMessageDialog dialog(this, "wxThread sample (based on minimal)\n"
|
||||||
"Julian Smart and Guilhem Lavaux",
|
"Julian Smart, Guilhem Lavaux, Vadim Zeitlin",
|
||||||
"About wxThread sample",
|
"About wxThread sample",
|
||||||
wxOK | wxICON_INFORMATION);
|
wxOK | wxICON_INFORMATION);
|
||||||
|
|
||||||
dialog.ShowModal();
|
dialog.ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
|
||||||
|
{
|
||||||
|
m_txtctrl->Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyFrame::OnThreadExit(wxThread *thread)
|
||||||
|
{
|
||||||
|
int index = m_threads.Index(thread);
|
||||||
|
wxCHECK_RET( index != -1, "unknown thread being deleted??" );
|
||||||
|
|
||||||
|
wxCriticalSectionLocker enter(m_critsect);
|
||||||
|
|
||||||
|
m_aToDelete.Add(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyFrame::DeleteThread(size_t index)
|
||||||
|
{
|
||||||
|
delete m_threads[index];
|
||||||
|
m_threads.Remove(index);
|
||||||
|
}
|
123
src/msw/app.cpp
123
src/msw/app.cpp
@@ -40,8 +40,15 @@
|
|||||||
#include "wx/module.h"
|
#include "wx/module.h"
|
||||||
|
|
||||||
#if wxUSE_THREADS
|
#if wxUSE_THREADS
|
||||||
#include "wx/thread.h"
|
#include "wx/thread.h"
|
||||||
#endif
|
|
||||||
|
// define the array of MSG strutures
|
||||||
|
WX_DECLARE_OBJARRAY(MSG, wxMsgArray);
|
||||||
|
|
||||||
|
#include "wx/arrimpl.cpp"
|
||||||
|
|
||||||
|
WX_DEFINE_OBJARRAY(wxMsgArray);
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
|
|
||||||
#if wxUSE_WX_RESOURCES
|
#if wxUSE_WX_RESOURCES
|
||||||
#include "wx/resource.h"
|
#include "wx/resource.h"
|
||||||
@@ -736,24 +743,82 @@ bool wxApp::Initialized()
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Get and process a message, returning FALSE if WM_QUIT
|
* Get and process a message, returning FALSE if WM_QUIT
|
||||||
* received.
|
* received (and also set the flag telling the app to exit the main loop)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
bool wxApp::DoMessage()
|
bool wxApp::DoMessage()
|
||||||
{
|
{
|
||||||
if (!::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0))
|
BOOL rc = ::GetMessage(&s_currentMsg, (HWND) NULL, 0, 0);
|
||||||
{
|
if ( rc == 0 )
|
||||||
return FALSE;
|
{
|
||||||
}
|
// got WM_QUIT
|
||||||
|
m_keepGoing = FALSE;
|
||||||
|
|
||||||
// Process the message
|
return FALSE;
|
||||||
if (!ProcessMessage((WXMSG *)&s_currentMsg))
|
}
|
||||||
{
|
else if ( rc == -1 )
|
||||||
::TranslateMessage(&s_currentMsg);
|
{
|
||||||
wxApp::sm_lastMessageTime = s_currentMsg.time; /* MATTHEW: timeStamp impl. */
|
// should never happen, but let's test for it nevertheless
|
||||||
::DispatchMessage(&s_currentMsg);
|
wxLogLastError("GetMessage");
|
||||||
}
|
}
|
||||||
return TRUE;
|
else
|
||||||
|
{
|
||||||
|
#if wxUSE_THREADS
|
||||||
|
wxASSERT_MSG( wxThread::IsMain(),
|
||||||
|
"only the main thread can process Windows messages" );
|
||||||
|
|
||||||
|
static bool s_hadGuiLock = TRUE;
|
||||||
|
static wxMsgArray s_aSavedMessages;
|
||||||
|
|
||||||
|
// if a secondary thread owns is doing GUI calls, save all messages for
|
||||||
|
// later processing - we can't process them right now because it will
|
||||||
|
// lead to recursive library calls (and we're not reentrant)
|
||||||
|
if ( !wxGuiOwnedByMainThread() )
|
||||||
|
{
|
||||||
|
s_hadGuiLock = FALSE;
|
||||||
|
|
||||||
|
s_aSavedMessages.Add(s_currentMsg);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// have we just regained the GUI lock? if so, post all of the saved
|
||||||
|
// messages
|
||||||
|
//
|
||||||
|
// FIXME of course, it's not _exactly_ the same as processing the
|
||||||
|
// messages normally - expect some things to break...
|
||||||
|
if ( !s_hadGuiLock )
|
||||||
|
{
|
||||||
|
s_hadGuiLock = TRUE;
|
||||||
|
|
||||||
|
size_t count = s_aSavedMessages.Count();
|
||||||
|
for ( size_t n = 0; n < count; n++ )
|
||||||
|
{
|
||||||
|
MSG& msg = s_aSavedMessages[n];
|
||||||
|
|
||||||
|
if ( !ProcessMessage((WXMSG *)&msg) )
|
||||||
|
{
|
||||||
|
::TranslateMessage(&msg);
|
||||||
|
::DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_aSavedMessages.Empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // wxUSE_THREADS
|
||||||
|
|
||||||
|
// Process the message
|
||||||
|
if ( !ProcessMessage((WXMSG *)&s_currentMsg) )
|
||||||
|
{
|
||||||
|
::TranslateMessage(&s_currentMsg);
|
||||||
|
wxApp::sm_lastMessageTime = s_currentMsg.time; /* MATTHEW: timeStamp impl. */
|
||||||
|
::DispatchMessage(&s_currentMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -773,12 +838,19 @@ bool wxApp::DoMessage()
|
|||||||
int wxApp::MainLoop()
|
int wxApp::MainLoop()
|
||||||
{
|
{
|
||||||
m_keepGoing = TRUE;
|
m_keepGoing = TRUE;
|
||||||
while (m_keepGoing)
|
|
||||||
|
while ( m_keepGoing )
|
||||||
{
|
{
|
||||||
while (!::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) &&
|
#if wxUSE_THREADS
|
||||||
ProcessIdle()) {}
|
wxMutexGuiLeaveOrEnter();
|
||||||
if (!DoMessage())
|
#endif // wxUSE_THREADS
|
||||||
m_keepGoing = FALSE;
|
|
||||||
|
while ( !::PeekMessage(&s_currentMsg, 0, 0, 0, PM_NOREMOVE) &&
|
||||||
|
ProcessIdle() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DoMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
return s_currentMsg.wParam;
|
return s_currentMsg.wParam;
|
||||||
@@ -806,8 +878,7 @@ bool wxApp::Pending()
|
|||||||
|
|
||||||
void wxApp::Dispatch()
|
void wxApp::Dispatch()
|
||||||
{
|
{
|
||||||
if (!DoMessage())
|
DoMessage();
|
||||||
m_keepGoing = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -871,12 +942,6 @@ void wxApp::OnIdle(wxIdleEvent& event)
|
|||||||
// idle events
|
// idle events
|
||||||
event.RequestMore(TRUE);
|
event.RequestMore(TRUE);
|
||||||
}
|
}
|
||||||
#if wxUSE_THREADS
|
|
||||||
// give a chance to all other threads to perform GUI calls
|
|
||||||
wxMutexGuiLeave();
|
|
||||||
::Sleep(0);
|
|
||||||
wxMutexGuiEnter();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s_inOnIdle = FALSE;
|
s_inOnIdle = FALSE;
|
||||||
}
|
}
|
||||||
@@ -1029,7 +1094,7 @@ bool wxYield()
|
|||||||
// if we see a WM_QUIT. (?)
|
// if we see a WM_QUIT. (?)
|
||||||
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) && msg.message != WM_QUIT)
|
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) && msg.message != WM_QUIT)
|
||||||
{
|
{
|
||||||
if (!wxTheApp->DoMessage())
|
if ( !wxTheApp->DoMessage() )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,10 +2,11 @@
|
|||||||
// Name: thread.cpp
|
// Name: thread.cpp
|
||||||
// Purpose: wxThread Implementation
|
// Purpose: wxThread Implementation
|
||||||
// Author: Original from Wolfram Gloger/Guilhem Lavaux
|
// Author: Original from Wolfram Gloger/Guilhem Lavaux
|
||||||
// Modified by:
|
// Modified by: Vadim Zeitlin to make it work :-)
|
||||||
// Created: 04/22/98
|
// Created: 04/22/98
|
||||||
// RCS-ID: $Id$
|
// RCS-ID: $Id$
|
||||||
// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998)
|
// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
|
||||||
|
// Vadim Zeitlin (1999)
|
||||||
// Licence: wxWindows licence
|
// Licence: wxWindows licence
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ enum thread_state
|
|||||||
{
|
{
|
||||||
STATE_IDLE = 0,
|
STATE_IDLE = 0,
|
||||||
STATE_RUNNING,
|
STATE_RUNNING,
|
||||||
|
STATE_PAUSED,
|
||||||
STATE_CANCELED,
|
STATE_CANCELED,
|
||||||
STATE_EXITED
|
STATE_EXITED
|
||||||
};
|
};
|
||||||
@@ -51,12 +53,21 @@ enum thread_state
|
|||||||
|
|
||||||
// id of the main thread - the one which can call GUI functions without first
|
// id of the main thread - the one which can call GUI functions without first
|
||||||
// calling wxMutexGuiEnter()
|
// calling wxMutexGuiEnter()
|
||||||
static HANDLE s_idMainThread;
|
static DWORD s_idMainThread = 0;
|
||||||
|
|
||||||
|
// if it's FALSE, some secondary thread is holding the GUI lock
|
||||||
|
static bool s_bGuiOwnedByMainThread = TRUE;
|
||||||
|
|
||||||
// critical section which controls access to all GUI functions: any secondary
|
// critical section which controls access to all GUI functions: any secondary
|
||||||
// thread (i.e. except the main one) must enter this crit section before doing
|
// thread (i.e. except the main one) must enter this crit section before doing
|
||||||
// any GUI calls
|
// any GUI calls
|
||||||
static wxCriticalSection *s_critsectGui;
|
static wxCriticalSection *s_critsectGui = NULL;
|
||||||
|
|
||||||
|
// critical section which protects s_nWaitingForGui variable
|
||||||
|
static wxCriticalSection *s_critsectWaitingForGui = NULL;
|
||||||
|
|
||||||
|
// number of threads waiting for GUI in wxMutexGuiEnter()
|
||||||
|
static size_t s_nWaitingForGui = 0;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Windows implementation of thread classes
|
// Windows implementation of thread classes
|
||||||
@@ -263,40 +274,43 @@ void wxCriticalSection::Leave()
|
|||||||
class wxThreadInternal
|
class wxThreadInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static DWORD WinThreadStart(LPVOID arg);
|
static DWORD WinThreadStart(wxThread *thread);
|
||||||
|
|
||||||
HANDLE thread_id;
|
HANDLE hThread;
|
||||||
int state;
|
thread_state state;
|
||||||
int prio, defer;
|
int prio, defer;
|
||||||
DWORD tid;
|
DWORD tid;
|
||||||
};
|
};
|
||||||
|
|
||||||
DWORD wxThreadInternal::WinThreadStart(LPVOID arg)
|
DWORD wxThreadInternal::WinThreadStart(wxThread *thread)
|
||||||
{
|
{
|
||||||
wxThread *ptr = (wxThread *)arg;
|
DWORD ret = (DWORD)thread->Entry();
|
||||||
DWORD ret;
|
thread->p_internal->state = STATE_EXITED;
|
||||||
|
thread->OnExit();
|
||||||
ret = (DWORD)ptr->Entry();
|
|
||||||
ptr->p_internal->state = STATE_EXITED;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThreadError wxThread::Create()
|
wxThreadError wxThread::Create()
|
||||||
{
|
{
|
||||||
int prio = p_internal->prio;
|
p_internal->hThread = ::CreateThread
|
||||||
|
(
|
||||||
|
NULL, // default security
|
||||||
|
0, // default stack size
|
||||||
|
(LPTHREAD_START_ROUTINE)
|
||||||
|
wxThreadInternal::WinThreadStart, // entry point
|
||||||
|
(void *)this, // parameter
|
||||||
|
CREATE_SUSPENDED, // flags
|
||||||
|
&p_internal->tid // [out] thread id
|
||||||
|
);
|
||||||
|
|
||||||
p_internal->thread_id = CreateThread(NULL, 0,
|
if ( p_internal->hThread == NULL )
|
||||||
(LPTHREAD_START_ROUTINE)wxThreadInternal::WinThreadStart,
|
|
||||||
(void *)this, CREATE_SUSPENDED, &p_internal->tid);
|
|
||||||
|
|
||||||
if ( p_internal->thread_id == NULL )
|
|
||||||
{
|
{
|
||||||
wxLogSysError(_("Can't create thread"));
|
wxLogSysError(_("Can't create thread"));
|
||||||
return wxTHREAD_NO_RESOURCE;
|
return wxTHREAD_NO_RESOURCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int win_prio;
|
int win_prio, prio = p_internal->prio;
|
||||||
if (prio <= 20)
|
if (prio <= 20)
|
||||||
win_prio = THREAD_PRIORITY_LOWEST;
|
win_prio = THREAD_PRIORITY_LOWEST;
|
||||||
else if (prio <= 40)
|
else if (prio <= 40)
|
||||||
@@ -313,53 +327,65 @@ wxThreadError wxThread::Create()
|
|||||||
win_prio = THREAD_PRIORITY_NORMAL;
|
win_prio = THREAD_PRIORITY_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetThreadPriority(p_internal->thread_id, win_prio);
|
if ( SetThreadPriority(p_internal->hThread, win_prio) == 0 )
|
||||||
|
{
|
||||||
|
wxLogSysError(_("Can't set thread priority"));
|
||||||
|
}
|
||||||
|
|
||||||
ResumeThread(p_internal->thread_id);
|
return Resume();
|
||||||
p_internal->state = STATE_RUNNING;
|
|
||||||
|
|
||||||
return wxTHREAD_NO_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThreadError wxThread::Destroy()
|
wxThreadError wxThread::Destroy()
|
||||||
{
|
{
|
||||||
if (p_internal->state != STATE_RUNNING)
|
if ( p_internal->state != STATE_RUNNING )
|
||||||
return wxTHREAD_NOT_RUNNING;
|
return wxTHREAD_NOT_RUNNING;
|
||||||
|
|
||||||
if ( p_internal->defer )
|
if ( p_internal->defer )
|
||||||
|
{
|
||||||
// soft termination: just set the flag and wait until the thread
|
// soft termination: just set the flag and wait until the thread
|
||||||
// auto terminates
|
// auto terminates
|
||||||
p_internal->state = STATE_CANCELED;
|
p_internal->state = STATE_CANCELED;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
// kill the thread
|
// kill the thread
|
||||||
TerminateThread(p_internal->thread_id, 0);
|
OnExit();
|
||||||
|
if ( ::TerminateThread(p_internal->hThread, 0) == 0 )
|
||||||
|
{
|
||||||
|
wxLogLastError("TerminateThread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return wxTHREAD_NO_ERROR;
|
return wxTHREAD_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThreadError wxThread::Pause()
|
wxThreadError wxThread::Pause()
|
||||||
{
|
{
|
||||||
DWORD nSuspendCount = ::SuspendThread(p_internal->thread_id);
|
DWORD nSuspendCount = ::SuspendThread(p_internal->hThread);
|
||||||
if ( nSuspendCount == (DWORD)-1 )
|
if ( nSuspendCount == (DWORD)-1 )
|
||||||
{
|
{
|
||||||
wxLogSysError(_("Can not suspend thread %x"), p_internal->thread_id);
|
wxLogSysError(_("Can not suspend thread %x"), p_internal->hThread);
|
||||||
|
|
||||||
return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
|
return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p_internal->state = STATE_PAUSED;
|
||||||
|
|
||||||
return wxTHREAD_NO_ERROR;
|
return wxTHREAD_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThreadError wxThread::Resume()
|
wxThreadError wxThread::Resume()
|
||||||
{
|
{
|
||||||
DWORD nSuspendCount = ::ResumeThread(p_internal->thread_id);
|
DWORD nSuspendCount = ::ResumeThread(p_internal->hThread);
|
||||||
if ( nSuspendCount == (DWORD)-1 )
|
if ( nSuspendCount == (DWORD)-1 )
|
||||||
{
|
{
|
||||||
wxLogSysError(_("Can not resume thread %x"), p_internal->thread_id);
|
wxLogSysError(_("Can not resume thread %x"), p_internal->hThread);
|
||||||
|
|
||||||
return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
|
return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p_internal->state = STATE_RUNNING;
|
||||||
|
|
||||||
return wxTHREAD_NO_ERROR;
|
return wxTHREAD_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,14 +422,15 @@ void *wxThread::Join()
|
|||||||
if (p_internal->state == STATE_IDLE)
|
if (p_internal->state == STATE_IDLE)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (wxThread::IsMain())
|
// FIXME this dead locks... wxThread class design must be changed
|
||||||
s_critsectGui->Leave();
|
#if 0
|
||||||
WaitForSingleObject(p_internal->thread_id, INFINITE);
|
WaitForSingleObject(p_internal->hThread, INFINITE);
|
||||||
if (wxThread::IsMain())
|
#else
|
||||||
s_critsectGui->Enter();
|
::TerminateThread(p_internal->hThread, 0);
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
GetExitCodeThread(p_internal->thread_id, &exit_code);
|
GetExitCodeThread(p_internal->hThread, &exit_code);
|
||||||
CloseHandle(p_internal->thread_id);
|
CloseHandle(p_internal->hThread);
|
||||||
|
|
||||||
p_internal->state = STATE_IDLE;
|
p_internal->state = STATE_IDLE;
|
||||||
|
|
||||||
@@ -427,7 +454,7 @@ bool wxThread::IsAlive() const
|
|||||||
|
|
||||||
bool wxThread::IsMain()
|
bool wxThread::IsMain()
|
||||||
{
|
{
|
||||||
return (GetCurrentThread() == s_idMainThread);
|
return ::GetCurrentThreadId() == s_idMainThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxThread::wxThread()
|
wxThread::wxThread()
|
||||||
@@ -468,9 +495,12 @@ IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
|
|||||||
|
|
||||||
bool wxThreadModule::OnInit()
|
bool wxThreadModule::OnInit()
|
||||||
{
|
{
|
||||||
|
s_critsectWaitingForGui = new wxCriticalSection();
|
||||||
|
|
||||||
s_critsectGui = new wxCriticalSection();
|
s_critsectGui = new wxCriticalSection();
|
||||||
s_critsectGui->Enter();
|
s_critsectGui->Enter();
|
||||||
s_idMainThread = GetCurrentThread();
|
|
||||||
|
s_idMainThread = ::GetCurrentThreadId();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -483,18 +513,103 @@ void wxThreadModule::OnExit()
|
|||||||
delete s_critsectGui;
|
delete s_critsectGui;
|
||||||
s_critsectGui = NULL;
|
s_critsectGui = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxDELETE(s_critsectWaitingForGui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
// under Windows, these functions are implemented usign a critical section and
|
// under Windows, these functions are implemented usign a critical section and
|
||||||
// not a mutex, so the names are a bit confusing
|
// not a mutex, so the names are a bit confusing
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void WXDLLEXPORT wxMutexGuiEnter()
|
void WXDLLEXPORT wxMutexGuiEnter()
|
||||||
{
|
{
|
||||||
//s_critsectGui->Enter();
|
// this would dead lock everything...
|
||||||
|
wxASSERT_MSG( !wxThread::IsMain(),
|
||||||
|
"main thread doesn't want to block in wxMutexGuiEnter()!" );
|
||||||
|
|
||||||
|
// the order in which we enter the critical sections here is crucial!!
|
||||||
|
|
||||||
|
// set the flag telling to the main thread that we want to do some GUI
|
||||||
|
{
|
||||||
|
wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
|
||||||
|
|
||||||
|
s_nWaitingForGui++;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxWakeUpMainThread();
|
||||||
|
|
||||||
|
// now we may block here because the main thread will soon let us in
|
||||||
|
// (during the next iteration of OnIdle())
|
||||||
|
s_critsectGui->Enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WXDLLEXPORT wxMutexGuiLeave()
|
void WXDLLEXPORT wxMutexGuiLeave()
|
||||||
{
|
{
|
||||||
//s_critsectGui->Leave();
|
wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
|
||||||
|
|
||||||
|
if ( wxThread::IsMain() )
|
||||||
|
{
|
||||||
|
s_bGuiOwnedByMainThread = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// decrement the number of waiters now
|
||||||
|
wxASSERT_MSG( s_nWaitingForGui > 0,
|
||||||
|
"calling wxMutexGuiLeave() without entering it first?" );
|
||||||
|
|
||||||
|
s_nWaitingForGui--;
|
||||||
|
|
||||||
|
wxWakeUpMainThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
s_critsectGui->Leave();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
|
||||||
|
{
|
||||||
|
wxASSERT_MSG( wxThread::IsMain(),
|
||||||
|
"only main thread may call wxMutexGuiLeaveOrEnter()!" );
|
||||||
|
|
||||||
|
wxCriticalSectionLocker enter(*s_critsectWaitingForGui);
|
||||||
|
|
||||||
|
if ( s_nWaitingForGui == 0 )
|
||||||
|
{
|
||||||
|
// no threads are waiting for GUI - so we may acquire the lock without
|
||||||
|
// any danger (but only if we don't already have it)
|
||||||
|
if ( !wxGuiOwnedByMainThread() )
|
||||||
|
{
|
||||||
|
s_critsectGui->Enter();
|
||||||
|
|
||||||
|
s_bGuiOwnedByMainThread = TRUE;
|
||||||
|
}
|
||||||
|
//else: already have it, nothing to do
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// some threads are waiting, release the GUI lock if we have it
|
||||||
|
if ( wxGuiOwnedByMainThread() )
|
||||||
|
{
|
||||||
|
wxMutexGuiLeave();
|
||||||
|
}
|
||||||
|
//else: some other worker thread is doing GUI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WXDLLEXPORT wxGuiOwnedByMainThread()
|
||||||
|
{
|
||||||
|
return s_bGuiOwnedByMainThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wake up the main thread if it's in ::GetMessage()
|
||||||
|
void WXDLLEXPORT wxWakeUpMainThread()
|
||||||
|
{
|
||||||
|
// sending any message would do - hopefully WM_NULL is harmless enough
|
||||||
|
if ( !::PostThreadMessage(s_idMainThread, WM_NULL, 0, 0) )
|
||||||
|
{
|
||||||
|
// should never happen
|
||||||
|
wxLogLastError("PostThreadMessage(WM_NULL)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // wxUSE_THREADS
|
#endif // wxUSE_THREADS
|
||||||
|
Reference in New Issue
Block a user