#pragma interface "thread.h"
#endif
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
#include "wx/object.h"
#include "wx/setup.h"
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
typedef enum {
- MUTEX_NO_ERROR=0,
- MUTEX_DEAD_LOCK, // Mutex has been already locked by THE CALLING thread
- MUTEX_BUSY, // Mutex has been already locked by ONE thread
- MUTEX_UNLOCKED
+ wxMUTEX_NO_ERROR = 0,
+ wxMUTEX_DEAD_LOCK, // Mutex has been already locked by THE CALLING thread
+ wxMUTEX_BUSY, // Mutex has been already locked by ONE thread
+ wxMUTEX_UNLOCKED,
+ wxMUTEX_MISC_ERROR
} wxMutexError;
typedef enum {
- THREAD_NO_ERROR=0, // No error
- THREAD_NO_RESOURCE, // No resource left to create a new thread
- THREAD_RUNNING, // The thread is already running
- THREAD_NOT_RUNNING, // The thread isn't running
- THREAD_MISC_ERROR // Some other error
+ wxTHREAD_NO_ERROR = 0, // No error
+ wxTHREAD_NO_RESOURCE, // No resource left to create a new thread
+ wxTHREAD_RUNNING, // The thread is already running
+ wxTHREAD_NOT_RUNNING, // The thread isn't running
+ wxTHREAD_MISC_ERROR // Some other error
} wxThreadError;
// defines the interval of priority.
-#define WXTHREAD_MIN_PRIORITY 0
+#define WXTHREAD_MIN_PRIORITY 0
#define WXTHREAD_DEFAULT_PRIORITY 50
-#define WXTHREAD_MAX_PRIORITY 100
+#define WXTHREAD_MAX_PRIORITY 100
-// ---------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
// Mutex handler
+// ----------------------------------------------------------------------------
class WXDLLEXPORT wxMutexInternal;
class WXDLLEXPORT wxMutex {
public:
// Returns true if the mutex is locked.
bool IsLocked() const { return (m_locked > 0); }
+
protected:
friend class wxCondition;
wxMutexInternal *p_internal;
};
-// ---------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
// Condition handler.
+// ----------------------------------------------------------------------------
class wxConditionInternal;
class WXDLLEXPORT wxCondition {
public:
void Signal();
// Broadcasts to all "Waiters".
void Broadcast();
+
private:
wxConditionInternal *p_internal;
};
-// ---------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
// Thread management class
+// ----------------------------------------------------------------------------
class wxThreadInternal;
class WXDLLEXPORT wxThread {
public:
bool IsAlive() const;
// Returns true if the thread is running (not paused, not killed).
bool IsRunning() const;
+ // Returns true if the thread is suspended
+ bool IsPaused() const { return IsAlive() && !IsRunning(); }
+
// Returns true if the thread is the main thread (aka the GUI thread).
static bool IsMain();
void TestDestroy();
// Exits from the current thread.
void Exit(void *status = NULL);
+
private:
// Entry point for the thread.
virtual void *Entry() = 0;
wxThreadInternal *p_internal;
};
-// ---------------------------------------------------------------------------
-// Global variables
+// ----------------------------------------------------------------------------
+// Global functions and variables
+// ----------------------------------------------------------------------------
// GUI mutex handling.
void WXDLLEXPORT wxMutexGuiEnter();
void WXDLLEXPORT wxMutexGuiLeave();
-#endif
+#endif // __THREADH__
// Define a new application type
class MyApp: public wxApp
{
- public:
+public:
bool OnInit(void);
};
-WX_DEFINE_ARRAY(wxThread *,wxArrayThread);
+WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
// Define a new frame type
class MyFrame: public wxFrame
{
- public:
+public:
+ // ctor
MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h);
- public:
+ // operations
+ void WriteText(const wxString& text) { m_txtctrl->WriteText(text); }
+
+ // callbacks
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
+
void OnStartThread(wxCommandEvent& event);
void OnStopThread(wxCommandEvent& event);
void OnPauseThread(wxCommandEvent& event);
+ void OnResumeThread(wxCommandEvent& event);
+
void OnSize(wxSizeEvent &event);
bool OnClose(void) { return TRUE; }
+
+public:
+ wxArrayThread m_threads;
- public:
- wxArrayThread m_threads;
- wxTextCtrl *m_txtctrl;
-
- DECLARE_EVENT_TABLE()
+private:
+ wxTextCtrl *m_txtctrl;
+
+ DECLARE_EVENT_TABLE()
};
class MyThread: public wxThread
{
- public:
+public:
MyThread(MyFrame *frame);
-
+
void *Entry();
- public:
+
+public:
+ size_t m_count;
MyFrame *m_frame;
};
MyThread::MyThread(MyFrame *frame)
- : wxThread()
+ : wxThread()
{
- m_frame = frame;
+ m_count = 0;
+ m_frame = frame;
}
void *MyThread::Entry()
{
- wxString text;
-
- text.Printf("Thread 0x%x: Hello !\n", GetID());
- DeferDestroy(TRUE);
-
- while (1) {
- TestDestroy();
- wxMutexGuiEnter();
- m_frame->m_txtctrl->WriteText(text);
- wxMutexGuiLeave();
- wxSleep(1);
- }
-
- return NULL;
+ wxString text;
+
+ DeferDestroy(TRUE);
+
+ while (1) {
+ TestDestroy();
+ wxMutexGuiEnter();
+
+ text.Printf("[%u] Thread 0x%x here.\n", ++m_count, GetID());
+ m_frame->WriteText(text);
+
+ wxMutexGuiLeave();
+ wxSleep(1);
+ }
+
+ return NULL;
}
// ID for the menu commands
#define TEST_START_THREAD 103
#define TEST_STOP_THREAD 104
#define TEST_PAUSE_THREAD 105
+#define TEST_RESUME_THREAD 106
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
- EVT_MENU(TEST_QUIT, MyFrame::OnQuit)
- EVT_MENU(TEST_ABOUT, MyFrame::OnAbout)
- EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread)
- EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread)
- EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread)
- EVT_SIZE(MyFrame::OnSize)
+ EVT_MENU(TEST_QUIT, MyFrame::OnQuit)
+ EVT_MENU(TEST_ABOUT, MyFrame::OnAbout)
+ EVT_MENU(TEST_START_THREAD, MyFrame::OnStartThread)
+ EVT_MENU(TEST_STOP_THREAD, MyFrame::OnStopThread)
+ EVT_MENU(TEST_PAUSE_THREAD, MyFrame::OnPauseThread)
+ EVT_MENU(TEST_RESUME_THREAD, MyFrame::OnResumeThread)
+
+ EVT_SIZE(MyFrame::OnSize)
END_EVENT_TABLE()
// Create a new application object
// `Main program' equivalent, creating windows and returning main app frame
bool MyApp::OnInit(void)
{
- // Create the main frame window
- MyFrame *frame = new MyFrame((wxFrame *) NULL, (char *) "Minimal wxWindows App", 50, 50, 450, 340);
-
- // Make a menubar
- wxMenu *file_menu = new wxMenu;
-
- file_menu->Append(TEST_ABOUT, "&About");
- file_menu->Append(TEST_QUIT, "E&xit");
- wxMenuBar *menu_bar = new wxMenuBar;
- menu_bar->Append(file_menu, "&File");
-
- wxMenu *thread_menu = new wxMenu;
- thread_menu->Append(TEST_START_THREAD, "Start a new thread");
- thread_menu->Append(TEST_STOP_THREAD, "Stop a running thread");
- thread_menu->Append(TEST_PAUSE_THREAD, "Pause a running thread");
- menu_bar->Append(thread_menu, "Thread");
- frame->SetMenuBar(menu_bar);
-
- // Make a panel with a message
- wxPanel *panel = new wxPanel( frame, -1, wxPoint(0, 0), wxSize(400, 200), wxTAB_TRAVERSAL );
-
- (void)new wxStaticText( panel, -1, "Log window", wxPoint(10,10) );
-
-
- frame->m_txtctrl = new wxTextCtrl(panel, -1, "", wxPoint(10,30), wxSize(390, 190),
- wxTE_MULTILINE);
-
- // Show the frame
- frame->Show(TRUE);
-
- SetTopWindow(frame);
-
- return TRUE;
+ // Create the main frame window
+ MyFrame *frame = new MyFrame((wxFrame *) NULL, "wxWindows thread sample",
+ 50, 50, 450, 340);
+
+ // Make a menubar
+ wxMenu *file_menu = new wxMenu;
+
+ file_menu->Append(TEST_ABOUT, "&About");
+ file_menu->Append(TEST_QUIT, "E&xit");
+ wxMenuBar *menu_bar = new wxMenuBar;
+ menu_bar->Append(file_menu, "&File");
+
+ wxMenu *thread_menu = new wxMenu;
+ thread_menu->Append(TEST_START_THREAD, "Start a new thread");
+ thread_menu->Append(TEST_STOP_THREAD, "Stop a running thread");
+ thread_menu->AppendSeparator();
+ thread_menu->Append(TEST_PAUSE_THREAD, "Pause a running thread");
+ thread_menu->Append(TEST_RESUME_THREAD, "Resume suspended thread");
+ menu_bar->Append(thread_menu, "Thread");
+ frame->SetMenuBar(menu_bar);
+
+ // Show the frame
+ frame->Show(TRUE);
+
+ SetTopWindow(frame);
+
+ return TRUE;
}
// My frame constructor
-MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h):
- wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
-{}
-
-void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
+MyFrame::MyFrame(wxFrame *frame, char *title, int x, int y, int w, int h)
+ : wxFrame(frame, -1, title, wxPoint(x, y), wxSize(w, h))
{
- MyThread *thread = new MyThread(this);
+ wxPanel *panel = new wxPanel(this, -1, wxPoint(0, 0), wxSize(400, 200),
+ wxTAB_TRAVERSAL );
+
+ m_txtctrl = new wxTextCtrl(panel, -1, "", wxPoint(10,30), wxSize(390, 190),
+ wxTE_MULTILINE);
- thread->Create();
+ (void)new wxStaticText(panel, -1, "Log window", wxPoint(10,10));
+}
- m_threads.Add(thread);
+void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
+{
+ MyThread *thread = new MyThread(this);
+
+ thread->Create();
+
+ m_threads.Add(thread);
}
void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
{
- int no_thrd = m_threads.Count()-1;
-
- if (no_thrd < 0)
- return;
+ int no_thrd = m_threads.Count()-1;
+
+ if (no_thrd < 0)
+ return;
+
+ delete (m_threads[no_thrd]);
+ m_threads.Remove(no_thrd);
+}
- delete (m_threads[no_thrd]);
- m_threads.Remove(no_thrd);
+void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
+{
+ // resume first suspended thread
+ size_t n = 0;
+ while ( n < m_threads.Count() && m_threads[n]->IsPaused() )
+ n--;
+
+ if ( n < 0 )
+ wxLogError("No thread to pause!");
+ else
+ m_threads[n]->Resume();
}
void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
{
+ // pause last running thread
+ int n = m_threads.Count() - 1;
+ while ( n >= 0 && !m_threads[n]->IsRunning() )
+ n--;
+
+ if ( n < 0 )
+ wxLogError("No thread to pause!");
+ else
+ m_threads[n]->Pause();
}
void MyFrame::OnSize(wxSizeEvent& event )
{
- wxFrame::OnSize(event);
-
- wxSize size( GetClientSize() );
-
- m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 );
+ wxFrame::OnSize(event);
+
+ wxSize size( GetClientSize() );
+
+ m_txtctrl->SetSize( 10, 30, size.x-20, size.y-40 );
}
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
{
- unsigned int i;
- for (i=0;i<m_threads.Count();i++)
- delete (m_threads[i]);
- Close(TRUE);
+ unsigned int i;
+ for (i=0;i<m_threads.Count();i++)
+ delete (m_threads[i]);
+ Close(TRUE);
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
{
- wxMessageDialog dialog(this, "wxThread sample (based on minimal)\nJulian Smart and Guilhem Lavaux",
- "About wxThread sample", wxYES_NO|wxCANCEL);
-
- dialog.ShowModal();
+ wxMessageDialog dialog(this, "wxThread sample (based on minimal)\nJulian Smart and Guilhem Lavaux",
+ "About wxThread sample", wxYES_NO|wxCANCEL);
+
+ dialog.ShowModal();
}
{
p_internal = new wxMutexInternal;
p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
+ if ( !p_internal->p_mutex )
+ {
+ wxLogSysError(_("Can not create mutex."));
+ }
+
m_locked = 0;
}
wxMutex::~wxMutex()
{
if (m_locked > 0)
- wxDebugMsg("wxMutex warning: freeing a locked mutex (%d locks)\n", m_locked);
+ wxLogDebug("Warning: freeing a locked mutex (%d locks).", m_locked);
CloseHandle(p_internal->p_mutex);
}
ret = WaitForSingleObject(p_internal->p_mutex, INFINITE);
if (ret == WAIT_ABANDONED)
- return MUTEX_BUSY;
+ return wxMUTEX_BUSY;
m_locked++;
- return MUTEX_NO_ERROR;
+ return wxMUTEX_NO_ERROR;
}
wxMutexError wxMutex::TryLock()
ret = WaitForSingleObject(p_internal->p_mutex, 0);
if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED)
- return MUTEX_BUSY;
+ return wxMUTEX_BUSY;
m_locked++;
- return MUTEX_NO_ERROR;
+ return wxMUTEX_NO_ERROR;
}
wxMutexError wxMutex::Unlock()
{
- BOOL ret;
-
if (m_locked > 0)
m_locked--;
- // Why does this have 3 args? The redundant ones removed by JACS
-// ret = ReleaseMutex(p_internal->p_mutex, 1, NULL);
- ret = ReleaseMutex(p_internal->p_mutex);
- return MUTEX_NO_ERROR;
+ BOOL ret = ReleaseMutex(p_internal->p_mutex);
+ if ( ret != 0 )
+ {
+ wxLogSysError(_("Couldn't release a mutex"));
+ return wxMUTEX_MISC_ERROR;
+ }
+
+ return wxMUTEX_NO_ERROR;
}
class wxConditionInternal {
{
p_internal = new wxConditionInternal;
p_internal->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if ( !p_internal->event )
+ {
+ wxLogSysError(_("Can not create event object."));
+ }
+
p_internal->waiters = 0;
}
int i;
for (i=0;i<p_internal->waiters;i++)
- SetEvent(p_internal->event);
+ {
+ if ( SetEvent(p_internal->event) == 0 )
+ {
+ wxLogSysError(_("Couldn't change the state of event object."));
+ }
+ }
}
class wxThreadInternal {
wxThreadError wxThread::Create()
{
- int win_prio, prio = p_internal->prio;
+ int prio = p_internal->prio;
p_internal->thread_id = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)wxThreadInternal::WinThreadStart,
(void *)this, CREATE_SUSPENDED, &p_internal->tid);
- if (p_internal->thread_id == NULL) {
- printf("Error = %d\n", GetLastError());
- return THREAD_NO_RESOURCE;
+
+ if ( p_internal->thread_id == NULL )
+ {
+ wxLogSysError(_("Can't create thread"));
+ return wxTHREAD_NO_RESOURCE;
}
+ int win_prio;
if (prio <= 20)
win_prio = THREAD_PRIORITY_LOWEST;
else if (prio <= 40)
win_prio = THREAD_PRIORITY_ABOVE_NORMAL;
else if (prio <= 100)
win_prio = THREAD_PRIORITY_HIGHEST;
+ else
+ {
+ wxFAIL_MSG("invalid value of thread priority parameter");
+ win_prio = THREAD_PRIORITY_NORMAL;
+ }
SetThreadPriority(p_internal->thread_id, win_prio);
ResumeThread(p_internal->thread_id);
p_internal->state = STATE_RUNNING;
- return THREAD_NO_ERROR;
+ return wxTHREAD_NO_ERROR;
}
wxThreadError wxThread::Destroy()
{
if (p_internal->state != STATE_RUNNING)
- return THREAD_NOT_RUNNING;
+ return wxTHREAD_NOT_RUNNING;
if (p_internal->defer == FALSE)
TerminateThread(p_internal->thread_id, 0);
else
p_internal->state = STATE_CANCELED;
- return THREAD_NO_ERROR;
+ return wxTHREAD_NO_ERROR;
+}
+
+wxThreadError wxThread::Pause()
+{
+ DWORD nSuspendCount = ::SuspendThread(p_internal->thread_id);
+ if ( nSuspendCount == (DWORD)-1 )
+ {
+ wxLogSysError(_("Can not suspend thread %x"), p_internal->thread_id);
+
+ return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
+ }
+
+ return wxTHREAD_NO_ERROR;
+}
+
+wxThreadError wxThread::Resume()
+{
+ DWORD nSuspendCount = ::ResumeThread(p_internal->thread_id);
+ if ( nSuspendCount == (DWORD)-1 )
+ {
+ wxLogSysError(_("Can not resume thread %x"), p_internal->thread_id);
+
+ return wxTHREAD_MISC_ERROR; // no idea what might provoke this error...
+ }
+
+ return wxTHREAD_NO_ERROR;
}
void wxThread::Exit(void *status)
IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
+void WXDLLEXPORT wxMutexGuiEnter()
+{
+ wxFAIL_MSG("not implemented");
+}
+
+void WXDLLEXPORT wxMutexGuiLeave()
+{
+ wxFAIL_MSG("not implemented");
+}