X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5cba3a254852a4dad83d903d0a750a8335a7c36c..071de7a097edd90a8f39d4624b26b5744560e677:/interface/wx/thread.h?ds=sidebyside diff --git a/interface/wx/thread.h b/interface/wx/thread.h index a4801d4a2a..3dcb9f3f2a 100644 --- a/interface/wx/thread.h +++ b/interface/wx/thread.h @@ -279,65 +279,127 @@ public: Example: @code + wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent); + class MyFrame : public wxFrame, public wxThreadHelper { public: - MyFrame() : wxThreadHelper(wxTHREAD_JOINABLE) {} - - ... - - virtual ExitCode Entry() - { - // here we do our long task, periodically calling TestDestroy(): - while (!TestDestroy()) - { - // ...do another bit of work here... - - // post an update message to the frame - } - - // TestDestroy() returned true (which means the main thread - // asked us to terminate as soon as possible) or we ended the - // long task... - return (ExitCode)0; - } - + MyFrame(...) { ... } ~MyFrame() { - // important: before terminating, we _must_ wait for our - // joinable thread to end, if it's running! - if (GetThread()->IsRunning()) - GetThread()->Wait(); + // it's better to do any thread cleanup in the OnClose() + // event handler, rather than in the destructor. + // This is because the event loop for a top-level window is not + // active anymore when its destructor is called and if the thread + // sends events when ending, they won't be processed unless + // you ended the thread from OnClose. + // See @ref overview_windowdeletion for more info. } ... void DoStartALongTask(); + void OnThreadUpdate(wxThreadEvent& evt); + void OnClose(wxCloseEvent& evt); ... - } + + protected: + virtual wxThread::ExitCode Entry(); + + // the output data of the Entry() routine: + char m_data[1024]; + wxCriticalSection m_dataCS; // protects field above + + DECLARE_EVENT_TABLE() + }; + + wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent) + BEGIN_EVENT_TABLE(MyFrame, wxFrame) + EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_UPDATE, MyFrame::OnThreadUpdate) + EVT_CLOSE(MyFrame::OnClose) + END_EVENT_TABLE() void MyFrame::DoStartALongTask() { // we want to start a long task, but we don't want our GUI to block // while it's executed, so we use a thread to do it. - if (Create() != wxTHREAD_NO_ERROR) + if (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR) { wxLogError("Could not create the worker thread!"); return; } // go! - if (Run() != wxTHREAD_NO_ERROR) + if (GetThread()->Run() != wxTHREAD_NO_ERROR) { wxLogError("Could not run the worker thread!"); return; } } + + wxThread::ExitCode MyFrame::Entry() + { + // IMPORTANT: + // this function gets executed in the secondary thread context! + + int offset = 0; + + // here we do our long task, periodically calling TestDestroy(): + while (!GetThread()->TestDestroy()) + { + // since this Entry() is implemented in MyFrame context we don't + // need any pointer to access the m_data, m_processedData, m_dataCS + // variables... very nice! + + // this is an example of the generic structure of a download thread: + char buffer[1024]; + download_chunk(buffer, 1024); // this takes time... + + { + // ensure noone reads m_data while we write it + wxCriticalSectionLocker lock(m_dataCS); + memcpy(m_data+offset, buffer, 1024); + offset += 1024; + } + + + // VERY IMPORTANT: do not call any GUI function inside this + // function; rather use wxQueueEvent(): + wxQueueEvent(this, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_UPDATE)); + // we used pointer 'this' assuming it's safe; see OnClose() + } + + // TestDestroy() returned true (which means the main thread asked us + // to terminate as soon as possible) or we ended the long task... + return (wxThread::ExitCode)0; + } + + void MyFrame::OnClose(wxCloseEvent&) + { + // important: before terminating, we _must_ wait for our joinable + // thread to end, if it's running; in fact it uses variables of this + // instance and posts events to *this event handler + + if (GetThread() && // DoStartALongTask() may have not been called + GetThread()->IsRunning()) + GetThread()->Wait(); + + Destroy(); + } + + void MyFrame::OnThreadUpdate(wxThreadEvent& evt) + { + // ...do something... e.g. m_pGauge->Pulse(); + + // read some parts of m_data just for fun: + wxCriticalSectionLocker lock(m_dataCS); + wxPrintf("%c", m_data[100]); + } @endcode @library{wxbase} @category{threading} - @see wxThread + @see wxThread, wxThreadEvent */ class wxThreadHelper { @@ -365,6 +427,25 @@ public: This function is pure virtual and must be implemented by any derived class. The thread execution will start here. + You'll typically want your Entry() to look like: + @code + wxThread::ExitCode Entry() + { + while (!GetThread()->TestDestroy()) + { + // ... do some work ... + + if (IsWorkCompleted) + break; + + if (HappenedStoppingError) + return (wxThread::ExitCode)1; // failure + } + + return (wxThread::ExitCode)0; // success + } + @endcode + The returned value is the thread exit code which is only useful for joinable threads and is the value returned by @c "GetThread()->Wait()". @@ -374,26 +455,36 @@ public: virtual ExitCode Entry() = 0; /** - Creates a new thread. + @deprecated + Use CreateThread() instead. + */ + wxThreadError Create(unsigned int stackSize = 0); + + /** + Creates a new thread of the given @a kind. The thread object is created in the suspended state, and you should call @ref wxThread::Run "GetThread()->Run()" to start running it. You may optionally specify the stack size to be allocated to it (ignored - on platforms that don't support setting it explicitly, eg. Unix). - - Note that the type of the thread which is created is defined in the - constructor. + on platforms that don't support setting it explicitly, e.g. Unix). @return One of the ::wxThreadError enum values. */ - wxThreadError Create(unsigned int stackSize = 0); + wxThreadError CreateThread(wxThreadKind kind = wxTHREAD_JOINABLE, + unsigned int stackSize = 0); /** This is a public function that returns the wxThread object associated with the thread. */ wxThread* GetThread() const; + + /** + Returns the last type of thread given to the CreateThread() function + or to the constructor. + */ + wxThreadKind GetThreadKind() const; }; /** @@ -444,11 +535,17 @@ public: ~wxCriticalSection(); /** - Enter the critical section (same as locking a mutex). - + Enter the critical section (same as locking a mutex): if another thread + has already entered it, this call will block until the other thread + calls Leave(). There is no error return for this function. - After entering the critical section protecting some global - data the thread running in critical section may safely use/modify it. + + After entering the critical section protecting a data variable, + the thread running inside the critical section may safely use/modify it. + + Note that entering the same critical section twice or more from the same + thread doesn't result in a deadlock; in this case in fact this function will + immediately return. */ void Enter(); @@ -540,56 +637,68 @@ enum @code // declare a new type of event, to be used by our MyThread class: - extern const wxEventType wxEVT_COMMAND_MYTHREAD_COMPLETED; + wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent); + wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent); + class MyFrame; class MyThread : public wxThread { public: - MyThread(wxEvtHandler *handler) : wxThread(wxTHREAD_DETACHED) - { m_pHandler = handler; } - - ExitCode Entry() - { - while (!TestDestroy()) - { - // ... do a bit of work... - } + MyThread(MyFrame *handler) + : wxThread(wxTHREAD_DETACHED) + { m_pHandler = handler } + ~MyThread(); - // signal the event handler that this thread is going to be destroyed - // NOTE: here we assume that using the m_pHandler pointer is safe, - // (in this case it's assured by the MyFrame destructor) - wxQueueEvent(m_pHandler, new wxCommandEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED)); - - return (ExitCode)0; // success - } - - wxEvtHandler *m_pHandler; + protected: + virtual ExitCode Entry(); + MyFrame *m_pHandler; }; class MyFrame : public wxFrame { public: ... - ~MyFrame(); + ~MyFrame() + { + // it's better to do any thread cleanup in the OnClose() + // event handler, rather than in the destructor. + // This is because the event loop for a top-level window is not + // active anymore when its destructor is called and if the thread + // sends events when ending, they won't be processed unless + // you ended the thread from OnClose. + // See @ref overview_windowdeletion for more info. + } ... void DoStartThread(); void DoPauseThread(); - // a resume routine would be mostly identic to DoPauseThread() + // a resume routine would be nearly identic to DoPauseThread() void DoResumeThread() { ... } - void OnThreadExit(wxCommandEvent&); + void OnThreadUpdate(wxThreadEvent&); + void OnThreadCompletion(wxThreadEvent&); + void OnClose(wxCloseEvent&); protected: MyThread *m_pThread; + wxCriticalSection m_pThreadCS; // protects the m_pThread pointer - // this is _required_ for writing safe code! - wxCriticalSection m_critSection; + DECLARE_EVENT_TABLE() }; + BEGIN_EVENT_TABLE(MyFrame, wxFrame) + EVT_CLOSE(MyFrame::OnClose) + EVT_MENU(Minimal_Start, MyFrame::DoStartThread) + EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_UPDATE, MyFrame::OnThreadUpdate) + EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_COMPLETED, MyFrame::OnThreadCompletion) + END_EVENT_TABLE() + + wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent) + wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent) + void MyFrame::DoStartThread() { - m_pThread = new wxThread(); + m_pThread = new MyThread(this); if ( m_pThread->Create() != wxTHREAD_NO_ERROR ) { @@ -613,55 +722,96 @@ enum } } - void MyFrame::OnThreadExit(wxCommandEvent&) + wxThread::ExitCode MyThread::Entry() { - // the thread just ended; make sure not to leave dangling pointers around - m_pThread = NULL; + while (!TestDestroy()) + { + // ... do a bit of work... + + wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_UPDATE)); + } + + // signal the event handler that this thread is going to be destroyed + // NOTE: here we assume that using the m_pHandler pointer is safe, + // (in this case this is assured by the MyFrame destructor) + wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED)); + + return (wxThread::ExitCode)0; // success + } + + MyThread::~MyThread() + { + wxCriticalSectionLocker enter(m_pHandler->m_pThreadCS); + + // the thread is being destroyed; make sure not to leave dangling pointers around + m_pHandler->m_pThread = NULL; + } + + void MyFrame::OnThreadCompletion(wxThreadEvent&) + { + wxMessageOutputDebug().Printf("MYFRAME: MyThread exited!\n"); + } + + void MyFrame::OnThreadUpdate(wxThreadEvent&) + { + wxMessageOutputDebug().Printf("MYFRAME: MyThread update...\n"); } void MyFrame::DoPauseThread() { // anytime we access the m_pThread pointer we must ensure that it won't - // be modified in the meanwhile; inside a critical section we are sure - // that we are the only thread running, so that's what we need. - wxCriticalSectionLocker enter(m_critSection); + // be modified in the meanwhile; since only a single thread may be + // inside a given critical section at a given time, the following code + // is safe: + wxCriticalSectionLocker enter(m_pThreadCS); if (m_pThread) // does the thread still exist? { // without a critical section, once reached this point it may happen // that the OS scheduler gives control to the MyThread::Entry() function, // which in turn may return (because it completes its work) making - // invalid the m_pThread pointer; the critical section above - // makes this code safe. + // invalid the m_pThread pointer if (m_pThread->Pause() != wxTHREAD_NO_ERROR ) wxLogError("Can't pause the thread!"); } } - MyFrame::~MyFrame() + void MyFrame::OnClose(wxCloseEvent&) { - wxCriticalSectionLocker enter(m_critSection); + { + wxCriticalSectionLocker enter(m_pThreadCS); - if (m_pThread) // does the thread still exist? + if (m_pThread) // does the thread still exist? + { + m_out.Printf("MYFRAME: deleting thread"); + + if (m_pThread->Delete() != wxTHREAD_NO_ERROR ) + wxLogError("Can't delete the thread!"); + } + } // exit from the critical section to give the thread + // the possibility to enter its destructor + // (which is guarded with m_pThreadCS critical section!) + + while (1) { - if (m_pThread->Delete() != wxTHREAD_NO_ERROR ) - wxLogError("Can't delete the thread!"); - - // as soon as we exit the critical section and the MyThread::Entry - // function calls TestDestroy(), the thread will exit and thus - // call OnExitThread(); we need to maintain MyFrame object alive - // until then: - wxEventLoopBase* p = wxEventLoopBase::GetActive(); - while (p->Pending() && m_pThread) - p->Dispatch(); - - // the wxEVT_COMMAND_MYTHREAD_COMPLETED event was posted, we can - // safely exit + { // was the ~MyThread() function executed? + wxCriticalSectionLocker enter(m_pThreadCS); + if (!m_pThread) break; + } + + // wait for thread completion + wxThread::This()->Sleep(1); } + + Destroy(); } @endcode + For a more detailed and comprehensive example, see @sample{thread}. + For a simpler way to share data and synchronization objects between + the main and the secondary thread see wxThreadHelper. + Conversely, @b joinable threads do not delete themselves when they are done processing and as such are safe to create on the stack. Joinable threads also provide the ability for one to get value it returned from Entry() @@ -720,9 +870,9 @@ enum as MFC. A workaround for some wxWidgets ports is calling wxMutexGUIEnter() - before any GUI calls and then calling wxMutexGUILeave() afterwords. However, - the recommended way is to simply process the GUI calls in the main thread - through an event that is posted by wxQueueEvent(). + before any GUI calls and then calling wxMutexGUILeave() afterwords. + However, the recommended way is to simply process the GUI calls in the main + thread through an event that is posted by wxQueueEvent(). This does not imply that calls to these classes are thread-safe, however, as most wxWidgets classes are not thread-safe, including wxString. @@ -817,9 +967,10 @@ public: the thread calls TestDestroy() or when it finishes processing. @note - While this could work on a joinable thread you simply should not - call this routine on them as afterwards you may not be able to call - Wait() to free the memory of that thread. + This function works on a joinable thread but in that case makes + the TestDestroy() function of the thread return @true and then + waits for its completion (i.e. it differs from Wait() because + it asks the thread to terminate before waiting). See @ref thread_deletion for a broader explanation of this routine. */ @@ -828,6 +979,10 @@ public: /** Returns the number of system CPUs or -1 if the value is unknown. + For multi-core systems the returned value is typically the total number + of @e cores, since the OS usually abstract a single N-core CPU + as N different cores. + @see SetConcurrency() */ static int GetCPUCount(); @@ -836,7 +991,7 @@ public: Returns the platform specific thread ID of the current thread as a long. This can be used to uniquely identify threads, even if they are not wxThreads. */ - static unsigned long GetCurrentId(); + static wxThreadIdType GetCurrentId(); /** Gets the thread identifier: this is a platform dependent number that uniquely @@ -845,6 +1000,13 @@ public: */ wxThreadIdType GetId() const; + /** + Returns the thread kind as it was given in the ctor. + + @since 2.9.0 + */ + wxThreadKind GetKind() const; + /** Gets the priority of the thread, between zero and 100. @@ -913,17 +1075,6 @@ public: */ wxThreadError Kill(); - /** - Called when the thread exits. - - This function is called in the context of the thread associated with the - wxThread object, not in the context of the main thread. - This function will not be called if the thread was @ref Kill() killed. - - This function should never be called directly. - */ - virtual void OnExit(); - /** Suspends the thread. @@ -944,8 +1095,13 @@ public: wxThreadError Resume(); /** - Starts the thread execution. Should be called after - Create(). + Starts the thread execution. Should be called after Create(). + + Note that once you Run() a @b detached thread, @e any function call you do + on the thread pointer (you must allocate it on the heap) is @e "unsafe"; + i.e. the thread may have terminated at any moment after Run() and your pointer + may be dangling. See @ref thread_types for an example of safe manipulation + of detached threads. This function can only be called from another thread context. */ @@ -1003,12 +1159,13 @@ public: static wxThread* This(); /** - Waits for a joinable thread to terminate and returns the value the thread + Waits for a @b joinable thread to terminate and returns the value the thread returned from Entry() or @c "(ExitCode)-1" on error. Notice that, unlike Delete(), this function doesn't cancel the thread in any way so the caller waits for as long as it takes to the thread to exit. You can only Wait() for @b joinable (not detached) threads. + This function can only be called from another thread context. See @ref thread_deletion for a broader explanation of this routine. @@ -1016,7 +1173,7 @@ public: ExitCode Wait(); /** - Give the rest of the thread time slice to the system allowing the other + Give the rest of the thread's time-slice to the system allowing the other threads to run. Note that using this function is @b strongly discouraged, since in @@ -1042,8 +1199,8 @@ public: With a well-behaving, CPU-efficient thread the operating system is likely to properly care for its reactivation the moment it needs it, whereas with non-deterministic, Yield-using threads all bets are off and the system - scheduler is free to penalize drastically, and this effect gets worse - with increasing system load due to less free CPU resources available. + scheduler is free to penalize them drastically, and this effect + gets worse with increasing system load due to less free CPU resources available. You may refer to various Linux kernel @c sched_yield discussions for more information. @@ -1076,6 +1233,19 @@ protected: OnExit() will be called just before exiting. */ void Exit(ExitCode exitcode = 0); + +private: + + /** + Called when the thread exits. + + This function is called in the context of the thread associated with the + wxThread object, not in the context of the main thread. + This function will not be called if the thread was @ref Kill() killed. + + This function should never be called directly. + */ + virtual void OnExit(); }; @@ -1342,6 +1512,9 @@ public: Locks the mutex object. This is equivalent to LockTimeout() with infinite timeout. + Note that if this mutex is already locked by the caller thread, + this function doesn't block but rather immediately returns. + @return One of: @c wxMUTEX_NO_ERROR, @c wxMUTEX_DEAD_LOCK. */ wxMutexError Lock(); @@ -1374,7 +1547,7 @@ public: // Global functions/macros // ============================================================================ -/** @ingroup group_funcmacro_thread */ +/** @addtogroup group_funcmacro_thread */ //@{ /** @@ -1457,6 +1630,8 @@ public: */ bool wxIsMainThread(); + + /** This function must be called when any thread other than the main GUI thread wants to get access to the GUI library. This function will block the @@ -1475,14 +1650,14 @@ bool wxIsMainThread(); wxMutexGuiEnter(); // Call GUI here: - my_window-DrawSomething(); + my_window->DrawSomething(); wxMutexGuiLeave(); } @endcode This function is only defined on platforms which support preemptive - threads. + threads and only works under some ports (wxMSW currently). @note Under GTK, no creation of top-level windows is allowed in any thread but the main one.