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:
Vadim Zeitlin
1999-01-09 00:28:27 +00:00
parent ad5c34f309
commit bee503b05f
4 changed files with 375 additions and 116 deletions

View File

@@ -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__

View File

@@ -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:
void DeleteThread(size_t index);
// crit section protects access to the array below
wxCriticalSection m_critsect;
wxArrayInt m_aToDelete;
wxTextCtrl *m_txtctrl; 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);
}

View File

@@ -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,23 +743,81 @@ 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 )
{ {
// got WM_QUIT
m_keepGoing = FALSE;
return FALSE; return FALSE;
} }
else if ( rc == -1 )
{
// should never happen, but let's test for it nevertheless
wxLogLastError("GetMessage");
}
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 // Process the message
if (!ProcessMessage((WXMSG *)&s_currentMsg)) if ( !ProcessMessage((WXMSG *)&s_currentMsg) )
{ {
::TranslateMessage(&s_currentMsg); ::TranslateMessage(&s_currentMsg);
wxApp::sm_lastMessageTime = s_currentMsg.time; /* MATTHEW: timeStamp impl. */ wxApp::sm_lastMessageTime = s_currentMsg.time; /* MATTHEW: timeStamp impl. */
::DispatchMessage(&s_currentMsg); ::DispatchMessage(&s_currentMsg);
} }
}
return TRUE; 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;
} }

View File

@@ -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