X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4ccf05663316e28d964c7d76dc5defe774b59427..6496345c33824373fdb8cf7de04a43197fa0341c:/interface/wx/thread.h?ds=sidebyside diff --git a/interface/wx/thread.h b/interface/wx/thread.h index ae197ca0f2..20d0f92a60 100644 --- a/interface/wx/thread.h +++ b/interface/wx/thread.h @@ -188,23 +188,6 @@ public: wxCondError WaitTimeout(unsigned long milliseconds); }; -// There are 2 types of mutexes: normal mutexes and recursive ones. The attempt -// to lock a normal mutex by a thread which already owns it results in -// undefined behaviour (it always works under Windows, it will almost always -// result in a deadlock under Unix). Locking a recursive mutex in such -// situation always succeeds and it must be unlocked as many times as it has -// been locked. -// -// However recursive mutexes have several important drawbacks: first, in the -// POSIX implementation, they're less efficient. Second, and more importantly, -// they CAN NOT BE USED WITH CONDITION VARIABLES under Unix! Using them with -// wxCondition will work under Windows and some Unices (notably Linux) but will -// deadlock under other Unix versions (e.g. Solaris). As it might be difficult -// to ensure that a recursive mutex is not used with wxCondition, it is a good -// idea to avoid using recursive mutexes at all. Also, the last problem with -// them is that some (older) Unix versions don't support this at all -- which -// results in a configure warning when building and a deadlock when using them. - /** @class wxCriticalSectionLocker @@ -269,7 +252,8 @@ public: @class wxThreadHelper The wxThreadHelper class is a mix-in class that manages a single background - thread. By deriving from wxThreadHelper, a class can implement the thread + thread, either detached or joinable (see wxThread for the differences). + By deriving from wxThreadHelper, a class can implement the thread code in its own wxThreadHelper::Entry() method and easily share data and synchronization objects between the main thread and the worker thread. @@ -282,16 +266,136 @@ public: Ordinarily, a wxThread derived object would be created with the calculation code implemented in wxThread::Entry. To access the inputs to the calculation, - the frame object would often to pass a pointer to itself to the thread object. + the frame object would often need to pass a pointer to itself to the thread object. Similarly, the frame object would hold a pointer to the thread object. + Shared data and synchronization objects could be stored in either object though the object without the data would have to access the data through a pointer. - However, with wxThreadHelper, the frame object and the thread object are + However with wxThreadHelper the frame object and the thread object are treated as the same object. Shared data and synchronization variables are stored in the single object, eliminating a layer of indirection and the associated pointers. + Example: + @code + extern const wxEventType wxEVT_COMMAND_MYTHREAD_UPDATE; + + class MyFrame : public wxFrame, public wxThreadHelper + { + 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 DoStartALongTask(); + void OnThreadUpdate(wxCommandEvent& 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() + }; + + DEFINE_EVENT_TYPE(wxEVT_COMMAND_MYTHREAD_UPDATE) + 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 (CreateThread(wxTHREAD_JOINABLE) != wxTHREAD_NO_ERROR) + { + wxLogError("Could not create the worker thread!"); + return; + } + + // go! + 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 wxCommandEvent(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(wxCommandEvent&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} @@ -301,12 +405,19 @@ class wxThreadHelper { public: /** - This constructor simply initializes a member variable. + This constructor simply initializes internal member variables and tells + wxThreadHelper which type the thread internally managed should be. */ wxThreadHelper(wxThreadKind kind = wxTHREAD_JOINABLE); /** - The destructor frees the resources associated with the thread. + The destructor frees the resources associated with the thread, forcing + it to terminate (it uses wxThread::Kill function). + + Because of the wxThread::Kill unsafety, you should always wait + (with wxThread::Wait) for joinable threads to end or call wxThread::Delete + on detached threads, instead of relying on this destructor for stopping + the thread. */ virtual ~wxThreadHelper(); @@ -316,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()". @@ -325,23 +455,30 @@ public: virtual ExitCode Entry() = 0; /** - Creates a new thread. + 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. + 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). + 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. + 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; }; /** @@ -464,10 +601,11 @@ enum While it makes it much easier to share common data between several threads, it also makes it much easier to shoot oneself in the foot, so careful use of - synchronization objects such as mutexes() or critical sections (see wxCriticalSection) - is recommended. In addition, don't create global thread objects because they - allocate memory in their constructor, which will cause problems for the memory - checking system. + synchronization objects such as mutexes (see wxMutex) or critical sections + (see wxCriticalSection) is recommended. + In addition, don't create global thread objects because they allocate memory + in their constructor, which will cause problems for the memory checking system. + @section thread_types Types of wxThreads @@ -475,45 +613,229 @@ enum modeled after the the POSIX thread API. This is different from the Win32 API where all threads are joinable. - By default wxThreads in wxWidgets use the detached behavior. Detached threads - delete themselves once they have completed, either by themselves when they - complete processing or through a call to Delete(), and thus - must be created on the heap (through the new operator, for example). - Conversely, joinable threads do not delete themselves when they are done + By default wxThreads in wxWidgets use the @b detached behavior. + Detached threads delete themselves once they have completed, either by themselves + when they complete processing or through a call to Delete(), and thus + @b must be created on the heap (through the new operator, for example). + + Typically you'll want to store the instances of the detached wxThreads you + allocate, so that you can call functions on them. + Because of their nature however you'll need to always use a critical section + when accessing them: + + @code + // declare a new type of event, to be used by our MyThread class: + extern const wxEventType wxEVT_COMMAND_MYTHREAD_COMPLETED; + extern const wxEventType wxEVT_COMMAND_MYTHREAD_UPDATE; + class MyFrame; + + class MyThread : public wxThread + { + public: + MyThread(MyFrame *handler) + : wxThread(wxTHREAD_DETACHED) + { m_pHandler = handler } + ~MyThread(); + + protected: + virtual ExitCode Entry(); + MyFrame *m_pHandler; + }; + + class MyFrame : public wxFrame + { + public: + ... + ~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 nearly identic to DoPauseThread() + void DoResumeThread() { ... } + + void OnThreadCompletion(wxCommandEvent&); + void OnClose(wxCloseEvent&); + + protected: + MyThread *m_pThread; + wxCriticalSection m_pThreadCS; // protects the m_pThread pointer + + 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() + + DEFINE_EVENT_TYPE(wxEVT_COMMAND_MYTHREAD_COMPLETED) + DEFINE_EVENT_TYPE(wxEVT_COMMAND_MYTHREAD_UPDATE) + + void MyFrame::DoStartThread() + { + m_pThread = new MyThread(this); + + if ( m_pThread->Create() != wxTHREAD_NO_ERROR ) + { + wxLogError("Can't create the thread!"); + delete m_pThread; + m_pThread = NULL; + } + else + { + if (m_pThread->Run() != wxTHREAD_NO_ERROR ) + { + wxLogError("Can't create the thread!"); + delete m_pThread; + m_pThread = NULL; + } + + // after the call to wxThread::Run(), the m_pThread pointer is "unsafe": + // at any moment the thread may cease to exist (because it completes its work). + // To avoid dangling pointers OnThreadExit() will set m_pThread + // to NULL when the thread dies. + } + } + + wxThread::ExitCode MyThread::Entry() + { + while (!TestDestroy()) + { + // ... do a bit of work... + + wxQueueEvent(m_pHandler, new wxCommandEvent(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 wxCommandEvent(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(wxCommandEvent&) + { + wxMessageOutputDebug().Printf("MYFRAME: MyThread exited!\n"); + } + + void MyFrame::OnThreadUpdate(wxCommandEvent&) + { + 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; 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 + + if (m_pThread->Pause() != wxTHREAD_NO_ERROR ) + wxLogError("Can't pause the thread!"); + } + } + + void MyFrame::OnClose(wxCloseEvent&) + { + { + wxCriticalSectionLocker enter(m_pThreadCS); + + 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) + { + { // 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() through Wait(). - You shouldn't hurry to create all the threads joinable, however, because this has a disadvantage as well: you @b must Wait() for a joinable thread or the system resources used by it will never be freed, and you also must delete the corresponding wxThread object yourself if you did not create it on the stack. - In contrast, detached threads are of the "fire-and-forget" kind: you only have to - start a detached thread and it will terminate and destroy itself. + In contrast, detached threads are of the "fire-and-forget" kind: you only have + to start a detached thread and it will terminate and destroy itself. @section thread_deletion wxThread Deletion Regardless of whether it has terminated or not, you should call Wait() on a - joinable thread to release its memory, as outlined in @ref thread_types. + @b joinable thread to release its memory, as outlined in @ref thread_types. If you created a joinable thread on the heap, remember to delete it manually with the @c delete operator or similar means as only detached threads handle this type of memory management. - Since detached threads delete themselves when they are finished processing, + Since @b detached threads delete themselves when they are finished processing, you should take care when calling a routine on one. If you are certain the thread is still running and would like to end it, you may call Delete() to gracefully end it (which implies that the thread will be deleted after - that call to Delete()). It should be implied that you should never attempt - to delete a detached thread with the delete operator or similar means. - As mentioned, Wait() or Delete() attempts to gracefully terminate a - joinable and detached thread, respectively. It does this by waiting until - the thread in question calls TestDestroy() or ends processing (returns + that call to Delete()). It should be implied that you should @b never attempt + to delete a detached thread with the @c delete operator or similar means. + + As mentioned, Wait() or Delete() functions attempt to gracefully terminate a + joinable and a detached thread, respectively. They do this by waiting until + the thread in question calls TestDestroy() or ends processing (i.e. returns from wxThread::Entry). - Obviously, if the thread does call TestDestroy() and does not end the calling - thread will come to halt. This is why it is important to call TestDestroy() in - the Entry() routine of your threads as often as possible. + Obviously, if the thread does call TestDestroy() and does not end, the + thread which called Wait() or Delete() will come to halt. + This is why it's important to call TestDestroy() in the Entry() routine of + your threads as often as possible and immediately exit when it returns @true. + As a last resort you can end the thread immediately through Kill(). It is strongly recommended that you do not do this, however, as it does not free the resources associated with the object (although the wxThread object of @@ -523,10 +845,10 @@ enum @section thread_secondary wxWidgets Calls in Secondary Threads - All threads other than the "main application thread" (the one - wxApp::OnInit() or your main function runs in, for example) are considered - "secondary threads". These include all threads created by Create() or the - corresponding constructors. + All threads other than the "main application thread" (the one running + wxApp::OnInit() or the one your main function runs in, for example) are + considered "secondary threads". These include all threads created by Create() + or the corresponding constructors. GUI calls, such as those to a wxWindow or wxBitmap are explicitly not safe at all in secondary threads and could end your application prematurely. @@ -537,7 +859,7 @@ enum 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 either wxQueueEvent(). + 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. @@ -547,15 +869,15 @@ enum A common problem users experience with wxThread is that in their main thread they will check the thread every now and then to see if it has ended through IsRunning(), only to find that their application has run into problems - because the thread is using the default behavior and has already deleted - itself. Naturally, they instead attempt to use joinable threads in place - of the previous behavior. However, polling a wxThread for when it has ended - is in general a bad idea - in fact calling a routine on any running wxThread - should be avoided if possible. Instead, find a way to notify yourself when - the thread has ended. + because the thread is using the default behavior (i.e. it's @b detached) and + has already deleted itself. + Naturally, they instead attempt to use joinable threads in place of the previous + behavior. However, polling a wxThread for when it has ended is in general a + bad idea - in fact calling a routine on any running wxThread should be avoided + if possible. Instead, find a way to notify yourself when the thread has ended. Usually you only need to notify the main thread, in which case you can - post an event to it via wxPostEvent() or wxEvtHandler::AddPendingEvent(). + post an event to it via wxQueueEvent(). In the case of secondary threads you can call a routine of another class when the thread is about to complete processing and/or set the value of a variable, possibly using mutexes (see wxMutex) and/or other synchronization @@ -564,11 +886,17 @@ enum @library{wxbase} @category{threading} - @see wxMutex, wxCondition, wxCriticalSection + @see wxThreadHelper, wxMutex, wxCondition, wxCriticalSection, + @ref overview_thread */ class wxThread { public: + /** + The return type for the thread functions. + */ + typedef void* ExitCode; + /** This constructor creates a new detached (default) or joinable C++ thread object. It does not create or start execution of the real thread - @@ -622,13 +950,14 @@ public: wxThreadError Create(unsigned int stackSize = 0); /** - Calling Delete() gracefully terminates a detached thread, either when - the thread calls TestDestroy() or finished processing. + Calling Delete() gracefully terminates a @b detached thread, either when + 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 one 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. */ @@ -654,6 +983,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. @@ -722,17 +1058,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. @@ -753,8 +1078,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. */ @@ -812,12 +1142,13 @@ public: static wxThread* This(); /** - Waits for a joinable thread to terminate and returns the value the thread - returned from Entry() or @c (ExitCode)-1 on error. Notice that, unlike - Delete() doesn't cancel the thread in any way so the caller waits for as - long as it takes to the thread to exit. + 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. - You can only Wait() for 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. @@ -825,7 +1156,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 @@ -851,8 +1182,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. @@ -885,6 +1216,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(); }; @@ -1084,7 +1428,7 @@ enum wxMutexError For example, when several threads use the data stored in the linked list, modifications to the list should only be allowed to one thread at a time because during a new node addition the list integrity is temporarily broken - (this is also called @e program invariant). + (this is also called @e program @e invariant). @code // this variable has an "s_" prefix because it is static: seeing an "s_" in @@ -1125,9 +1469,9 @@ enum wxMutexError Notice how wxMutexLocker was used in the second function to ensure that the mutex is unlocked in any case: whether the function returns true or false - (because the destructor of the local object lock is always called). Using - this class instead of directly using wxMutex is, in general safer and is - even more so if your program uses C++ exceptions. + (because the destructor of the local object @e lock is always called). + Using this class instead of directly using wxMutex is, in general, safer + and is even more so if your program uses C++ exceptions. @library{wxbase} @category{threading}