]> git.saurik.com Git - wxWidgets.git/commitdiff
joinable and detached POSIX threads (not fully tested yet)
authorVadim Zeitlin <vadim@wxwidgets.org>
Mon, 29 Nov 1999 23:05:23 +0000 (23:05 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Mon, 29 Nov 1999 23:05:23 +0000 (23:05 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@4769 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/latex/wx/thread.tex
docs/latex/wx/tthreads.tex
include/wx/msw/textctrl.h
include/wx/thread.h
samples/console/console.cpp
src/msw/thread.cpp
src/unix/threadpsx.cpp

index b487d8178bbf19380a624352631c3a5fcc73c99c..77c2814a59f38835f6f56ec961eb16995e52aefe 100644 (file)
@@ -8,6 +8,25 @@ much easier to share common data between several threads, it also makes much
 easier to shoot oneself in the foot, so careful use of synchronization objects
 such as \helpref{mutexes}{wxmutex} and/or \helpref{critical sections}{wxcriticalsection} is recommended.
 
 easier to shoot oneself in the foot, so careful use of synchronization objects
 such as \helpref{mutexes}{wxmutex} and/or \helpref{critical sections}{wxcriticalsection} is recommended.
 
+There are two types of threads in wxWindows: {\it detached} and {\it joinable}
+ones, just as in POSIX thread API (but unlike Win32 threads where all threads
+are joinable). The difference between the two is that only joinbale threads
+can return a return code - it is returned by Wait() function. The detached
+threads (default) can not be waited for.
+
+You shouldn't hurry to create all the threads joinable, however, because this
+has a disadvantage as well: you {\bf must} Wait() for a joinable thread of the
+system resources used by it will never be freed and you also must delete the
+corresponding wxThread object yourself, while detached threads are of the
+"fire-and-forget" kind: you only have to start a detached thread and it will
+terminate and destroy itself.
+
+This means, of course, that all detached threads {\bf must} be created on the
+heap because the thread will call {\tt delete this;} upon termination. The
+joinable threads may be created on stack (don't create global thread objects
+because they allocate memory in their constructor which is a badthing to do),
+although usually they will be created on the heap as well.
+
 \wxheading{Derived from}
 
 None.
 \wxheading{Derived from}
 
 None.
@@ -84,6 +103,10 @@ created. Moreover, it must be called if \helpref{Create}{wxthreadcreate} or
 occupied by the thread object (it will be done in the destructor for joinable
 threads).
 
 occupied by the thread object (it will be done in the destructor for joinable
 threads).
 
+Delete() may be called for thread in any state: running, paused or even not yet created. Moreover,
+it must be called if \helpref{Create}{wxthreadcreate} or \helpref{Run}{wxthreadrun} fail to free
+the memory occupied by the thread object.
+
 For detached threads Delete() will also delete the C++ thread object, but it
 will not do this for joinable ones.
 
 For detached threads Delete() will also delete the C++ thread object, but it
 will not do this for joinable ones.
 
index c1ef2fdfff0e686f5a3daf8f16909ed9a4ffb8ee..2e1d6cb80deef9ad3db99f7fba4d217e0b554ca9 100644 (file)
@@ -8,7 +8,10 @@ wxWindows provides a complete set of classes encapsulating objects necessary in
 multithreaded (MT) programs: the \helpref{thread}{wxthread} class itself and different
 synchronization objects: \helpref{mutexes}{wxmutex} and 
 \helpref{critical sections}{wxcriticalsection} with 
 multithreaded (MT) programs: the \helpref{thread}{wxthread} class itself and different
 synchronization objects: \helpref{mutexes}{wxmutex} and 
 \helpref{critical sections}{wxcriticalsection} with 
-\helpref{conditions}{wxcondition}.
+\helpref{conditions}{wxcondition}. The thread API in wxWindows resembles to
+POSIX1.c threads API (a.k.a. pthreads), although several functions have
+different names and some features inspired by Win32 thread API are there as
+well.
 
 These classes will hopefully make writing MT programs easier and they also
 provide some extra error checking (compared to the native (be it Win32 or Posix)
 
 These classes will hopefully make writing MT programs easier and they also
 provide some extra error checking (compared to the native (be it Win32 or Posix)
@@ -21,7 +24,7 @@ new thread for each new client), but in others it might be a very poor choice
 (example: launching a separate thread when doing a long computation to show a
 progress dialog). Other implementation choices are available: for the progress
 dialog example it is far better to do the calculations in the 
 (example: launching a separate thread when doing a long computation to show a
 progress dialog). Other implementation choices are available: for the progress
 dialog example it is far better to do the calculations in the 
-\helpref{idle handler}{wxidleevent} or call \helpref{wxYield()}{wxyield}
+\helpref{idle handler}{wxidleevent} or call \helpref{wxYield()}{wxyield} 
 periodically to update the screen.
 
 If you do decide to use threads in your application, it is strongly recommended
 periodically to update the screen.
 
 If you do decide to use threads in your application, it is strongly recommended
index 184655a8d5ea6a8f224c26f6fbbf86d04c8e9e64..732a38ebe88f69112483c6c32f83be16a72e543a 100644 (file)
     #pragma interface "textctrl.h"
 #endif
 
     #pragma interface "textctrl.h"
 #endif
 
-// can we use RICHEDIT class for wxTextCtrl implementation?
-#if defined(__WIN95__) && !defined(__TWIN32__) && !defined(__WXWINE__)
-    #define wxUSE_RICHEDIT 1
-#else
-    #define wxUSE_RICHEDIT 0
-#endif
-
 class WXDLLEXPORT wxTextCtrl : public wxTextCtrlBase
 {
 public:
 class WXDLLEXPORT wxTextCtrl : public wxTextCtrlBase
 {
 public:
index 3f1415adc7830ec7563d2bd61b7104a36f084dbc..016e2ff2b5ab9a7433109c31cd86b23e1bdae0f3 100644 (file)
 
 enum wxMutexError
 {
 
 enum wxMutexError
 {
-  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
+    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
 };
 
 enum wxThreadError
 {
 };
 
 enum wxThreadError
 {
-  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_KILLED,            // Thread we waited for had to be killed
-  wxTHREAD_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_KILLED,            // Thread we waited for had to be killed
+    wxTHREAD_MISC_ERROR         // Some other error
 };
 
 enum wxThreadKind
 };
 
 enum wxThreadKind
@@ -106,7 +106,7 @@ protected:
     wxMutex& operator=(const wxMutex&);
 
     int m_locked;
     wxMutex& operator=(const wxMutex&);
 
     int m_locked;
-    wxMutexInternal *p_internal;
+    wxMutexInternal *m_internal;
 };
 
 // a helper class which locks the mutex in the ctor and unlocks it in the dtor:
 };
 
 // a helper class which locks the mutex in the ctor and unlocks it in the dtor:
@@ -213,28 +213,32 @@ private:
 };
 
 // ----------------------------------------------------------------------------
 };
 
 // ----------------------------------------------------------------------------
-// Condition handler.
+// Condition variable: allows to block the thread execution until something
+// happens (== condition is signaled)
 // ----------------------------------------------------------------------------
 
 class wxConditionInternal;
 class WXDLLEXPORT wxCondition
 {
 public:
 // ----------------------------------------------------------------------------
 
 class wxConditionInternal;
 class WXDLLEXPORT wxCondition
 {
 public:
-  // constructor & destructor
-  wxCondition();
-  ~wxCondition();
-
-  // Waits indefinitely.
-  void Wait(wxMutex& mutex);
-  // Waits until a signal is raised or the timeout is elapsed.
-  bool Wait(wxMutex& mutex, unsigned long sec, unsigned long nsec);
-  // Raises a signal: only one "Waiter" is released.
-  void Signal();
-  // Broadcasts to all "Waiters".
-  void Broadcast();
+    // constructor & destructor
+    wxCondition();
+    ~wxCondition();
+
+    // wait until the condition is signaled
+        // waits indefinitely.
+    void Wait();
+        // waits until a signal is raised or the timeout elapses
+    bool Wait(unsigned long sec, unsigned long nsec);
+
+    // signal the condition
+        // wakes up one (and only one) of the waiting threads
+    void Signal();
+        // wakes up all threads waiting onthis condition
+    void Broadcast();
 
 private:
 
 private:
-  wxConditionInternal *p_internal;
+    wxConditionInternal *m_internal;
 };
 
 // ----------------------------------------------------------------------------
 };
 
 // ----------------------------------------------------------------------------
@@ -381,7 +385,7 @@ private:
     friend class wxThreadInternal;
 
     // the (platform-dependent) thread class implementation
     friend class wxThreadInternal;
 
     // the (platform-dependent) thread class implementation
-    wxThreadInternal *p_internal;
+    wxThreadInternal *m_internal;
 
     // protects access to any methods of wxThreadInternal object
     wxCriticalSection m_critsect;
 
     // protects access to any methods of wxThreadInternal object
     wxCriticalSection m_critsect;
index 6e6af43806f186a4141f7d316e1f2847af284c97..f72458ddd57d23a9a89efa2703131f36700d399e 100644 (file)
 
 //#define TEST_ARRAYS
 //#define TEST_LOG
 
 //#define TEST_ARRAYS
 //#define TEST_LOG
-//#define TEST_THREADS
+//#define TEST_STRINGS
+#define TEST_THREADS
 //#define TEST_TIME
 //#define TEST_TIME
-#define TEST_LONGLONG
+//#define TEST_LONGLONG
 
 // ============================================================================
 // implementation
 
 // ============================================================================
 // implementation
@@ -52,7 +53,7 @@ static void TestSpeed()
 {
     static const long max = 100000000;
     long n;
 {
     static const long max = 100000000;
     long n;
-    
+
     {
         wxStopWatch sw;
 
     {
         wxStopWatch sw;
 
@@ -151,7 +152,7 @@ wxThread::ExitCode MyJoinableThread::Entry()
 class MyDetachedThread : public wxThread
 {
 public:
 class MyDetachedThread : public wxThread
 {
 public:
-    MyDetachedThread(char ch) { m_ch = ch; Create(); }
+    MyDetachedThread(size_t n, char ch) { m_n = n; m_ch = ch; Create(); }
 
     // thread execution starts here
     virtual ExitCode Entry();
 
     // thread execution starts here
     virtual ExitCode Entry();
@@ -160,7 +161,8 @@ public:
     virtual void OnExit();
 
 private:
     virtual void OnExit();
 
 private:
-    char m_ch;
+    size_t m_n; // number of characters to write
+    char m_ch;  // character to write
 };
 
 wxThread::ExitCode MyDetachedThread::Entry()
 };
 
 wxThread::ExitCode MyDetachedThread::Entry()
@@ -173,8 +175,7 @@ wxThread::ExitCode MyDetachedThread::Entry()
             gs_counter++;
     }
 
             gs_counter++;
     }
 
-    static const size_t nIter = 10;
-    for ( size_t n = 0; n < nIter; n++ )
+    for ( size_t n = 0; n < m_n; n++ )
     {
         if ( TestDestroy() )
             break;
     {
         if ( TestDestroy() )
             break;
@@ -190,11 +191,86 @@ wxThread::ExitCode MyDetachedThread::Entry()
 
 void MyDetachedThread::OnExit()
 {
 
 void MyDetachedThread::OnExit()
 {
+    wxLogTrace("thread", "Thread %ld is in OnExit", GetId());
+
     wxCriticalSectionLocker lock(gs_critsect);
     if ( !--gs_counter )
         gs_cond.Signal();
 }
 
     wxCriticalSectionLocker lock(gs_critsect);
     if ( !--gs_counter )
         gs_cond.Signal();
 }
 
+void TestDetachedThreads()
+{
+    puts("*** Testing detached threads ***");
+
+    static const size_t nThreads = 3;
+    MyDetachedThread *threads[nThreads];
+    size_t n;
+    for ( n = 0; n < nThreads; n++ )
+    {
+        threads[n] = new MyDetachedThread(10, 'A' + n);
+    }
+
+    threads[0]->SetPriority(WXTHREAD_MIN_PRIORITY);
+    threads[1]->SetPriority(WXTHREAD_MAX_PRIORITY);
+
+    for ( n = 0; n < nThreads; n++ )
+    {
+        threads[n]->Run();
+    }
+
+    // wait until all threads terminate
+    gs_cond.Wait();
+
+    puts("");
+}
+
+void TestJoinableThreads()
+{
+    puts("*** Testing a joinable thread (a loooong calculation...) ***");
+
+    // calc 10! in the background
+    MyJoinableThread thread(10);
+    thread.Run();
+
+    printf("\nThread terminated with exit code %lu.\n",
+           (unsigned long)thread.Wait());
+}
+
+void TestThreadSuspend()
+{
+    MyDetachedThread *thread = new MyDetachedThread(30, 'X');
+
+    thread->Run();
+
+    // this is for this demo only, in a real life program we'd use another
+    // condition variable which would be signaled from wxThread::Entry() to
+    // tell us that the thread really started running - but here just wait a
+    // bit and hope that it will be enough (the problem is, of course, that
+    // the thread might still not run when we call Pause() which will result
+    // in an error)
+    wxThread::Sleep(300);
+
+    for ( size_t n = 0; n < 3; n++ )
+    {
+        thread->Pause();
+
+        puts("\nThread suspended");
+        if ( n > 0 )
+        {
+            // don't sleep but resume immediately the first time
+            wxThread::Sleep(300);
+        }
+        puts("Going to resume the thread");
+
+        thread->Resume();
+    }
+
+    // wait until the thread terminates
+    gs_cond.Wait();
+
+    puts("");
+}
+
 #endif // TEST_THREADS
 
 // ----------------------------------------------------------------------------
 #endif // TEST_THREADS
 
 // ----------------------------------------------------------------------------
@@ -216,6 +292,64 @@ void PrintArray(const char* name, const wxArrayString& array)
 
 #endif // TEST_ARRAYS
 
 
 #endif // TEST_ARRAYS
 
+// ----------------------------------------------------------------------------
+// strings
+// ----------------------------------------------------------------------------
+
+#ifdef TEST_STRINGS
+
+#include "wx/timer.h"
+
+void TestString()
+{
+    wxStopWatch sw;
+
+    wxString a, b, c;
+
+    a.reserve (128);
+    b.reserve (128);
+    c.reserve (128);
+
+    for (int i = 0; i < 1000000; ++i)
+    {
+        a = "Hello";
+        b = " world";
+        c = "! How'ya doin'?";
+        a += b;
+        a += c;
+        c = "Hello world! What's up?";
+        if (c != a)
+            c = "Doh!";
+    }
+
+    printf ("TestString elapsed time: %ld\n", sw.Time());
+}
+
+void TestPChar()
+{
+    wxStopWatch sw;
+
+    char a [128];
+    char b [128];
+    char c [128];
+
+    for (int i = 0; i < 1000000; ++i)
+    {
+        strcpy (a, "Hello");
+        strcpy (b, " world");
+        strcpy (c, "! How'ya doin'?");
+        strcat (a, b);
+        strcat (a, c);
+        strcpy (c, "Hello world! What's up?");
+        if (strcmp (c, a) == 0)
+            strcpy (c, "Doh!");
+    }
+
+    printf ("TestPChar elapsed time: %ld\n", sw.Time());
+}
+
+#endif // TEST_STRINGS
+
 // ----------------------------------------------------------------------------
 // entry point
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // entry point
 // ----------------------------------------------------------------------------
@@ -227,6 +361,11 @@ int main(int argc, char **argv)
         fprintf(stderr, "Failed to initialize the wxWindows library, aborting.");
     }
 
         fprintf(stderr, "Failed to initialize the wxWindows library, aborting.");
     }
 
+#ifdef TEST_STRINGS
+    TestPChar();
+    TestString();
+#endif // TEST_STRINGS
+
 #ifdef TEST_ARRAYS
     wxArrayString a1;
     a1.Add("tiger");
 #ifdef TEST_ARRAYS
     wxArrayString a1;
     a1.Add("tiger");
@@ -279,38 +418,15 @@ int main(int argc, char **argv)
 #endif // TEST_LOG
 
 #ifdef TEST_THREADS
 #endif // TEST_LOG
 
 #ifdef TEST_THREADS
-    puts("Testing detached threads...");
+    if ( argc > 1 && argv[1][0] == 't' )
+        wxLog::AddTraceMask("thread");
 
 
-    static const size_t nThreads = 3;
-    MyDetachedThread *threads[nThreads];
-    size_t n;
-    for ( n = 0; n < nThreads; n++ )
+    TestThreadSuspend();
+    if ( 0 )
     {
     {
-        threads[n] = new MyDetachedThread('A' + n);
+    TestDetachedThreads();
+    TestJoinableThreads();
     }
     }
-
-    threads[0]->SetPriority(WXTHREAD_MIN_PRIORITY);
-    threads[1]->SetPriority(WXTHREAD_MAX_PRIORITY);
-
-    for ( n = 0; n < nThreads; n++ )
-    {
-        threads[n]->Run();
-    }
-
-    // wait until all threads terminate
-    wxMutex mutex;
-    mutex.Lock();
-    gs_cond.Wait(mutex);
-    mutex.Unlock();
-
-    puts("\n\nTesting a joinable thread used for a loooong calculation...");
-
-    // calc 10! in the background
-    MyJoinableThread thread(10);
-    thread.Run();
-
-    printf("\nThread terminated with exit code %lu.\n",
-           (unsigned long)thread.Wait());
 #endif // TEST_THREADS
 
 #ifdef TEST_LONGLONG
 #endif // TEST_THREADS
 
 #ifdef TEST_LONGLONG
index a3929b693f4d5373e5e38fc8e390f10ae6f0a753..3ae5d7b979e867d983f9620a520cbbc8751e77e5 100644 (file)
@@ -104,9 +104,9 @@ public:
 
 wxMutex::wxMutex()
 {
 
 wxMutex::wxMutex()
 {
-    p_internal = new wxMutexInternal;
-    p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
-    if ( !p_internal->p_mutex )
+    m_internal = new wxMutexInternal;
+    m_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
+    if ( !m_internal->p_mutex )
     {
         wxLogSysError(_("Can not create mutex."));
     }
     {
         wxLogSysError(_("Can not create mutex."));
     }
@@ -118,14 +118,14 @@ wxMutex::~wxMutex()
 {
     if (m_locked > 0)
         wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked);
 {
     if (m_locked > 0)
         wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked);
-    CloseHandle(p_internal->p_mutex);
+    CloseHandle(m_internal->p_mutex);
 }
 
 wxMutexError wxMutex::Lock()
 {
     DWORD ret;
 
 }
 
 wxMutexError wxMutex::Lock()
 {
     DWORD ret;
 
-    ret = WaitForSingleObject(p_internal->p_mutex, INFINITE);
+    ret = WaitForSingleObject(m_internal->p_mutex, INFINITE);
     switch ( ret )
     {
         case WAIT_ABANDONED:
     switch ( ret )
     {
         case WAIT_ABANDONED:
@@ -152,7 +152,7 @@ wxMutexError wxMutex::TryLock()
 {
     DWORD ret;
 
 {
     DWORD ret;
 
-    ret = WaitForSingleObject(p_internal->p_mutex, 0);
+    ret = WaitForSingleObject(m_internal->p_mutex, 0);
     if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED)
         return wxMUTEX_BUSY;
 
     if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED)
         return wxMUTEX_BUSY;
 
@@ -165,7 +165,7 @@ wxMutexError wxMutex::Unlock()
     if (m_locked > 0)
         m_locked--;
 
     if (m_locked > 0)
         m_locked--;
 
-    BOOL ret = ReleaseMutex(p_internal->p_mutex);
+    BOOL ret = ReleaseMutex(m_internal->p_mutex);
     if ( ret == 0 )
     {
         wxLogSysError(_("Couldn't release a mutex"));
     if ( ret == 0 )
     {
         wxLogSysError(_("Couldn't release a mutex"));
@@ -197,16 +197,14 @@ public:
         waiters = 0;
     }
 
         waiters = 0;
     }
 
-    bool Wait(wxMutex& mutex, DWORD timeout)
+    bool Wait(DWORD timeout)
     {
     {
-        mutex.Unlock();
         waiters++;
 
         // FIXME this should be MsgWaitForMultipleObjects() as well probably
         DWORD rc = ::WaitForSingleObject(event, timeout);
 
         waiters--;
         waiters++;
 
         // FIXME this should be MsgWaitForMultipleObjects() as well probably
         DWORD rc = ::WaitForSingleObject(event, timeout);
 
         waiters--;
-        mutex.Lock();
 
         return rc != WAIT_TIMEOUT;
     }
 
         return rc != WAIT_TIMEOUT;
     }
@@ -228,24 +226,23 @@ public:
 
 wxCondition::wxCondition()
 {
 
 wxCondition::wxCondition()
 {
-    p_internal = new wxConditionInternal;
+    m_internal = new wxConditionInternal;
 }
 
 wxCondition::~wxCondition()
 {
 }
 
 wxCondition::~wxCondition()
 {
-    delete p_internal;
+    delete m_internal;
 }
 
 }
 
-void wxCondition::Wait(wxMutex& mutex)
+void wxCondition::Wait()
 {
 {
-    (void)p_internal->Wait(mutex, INFINITE);
+    (void)m_internal->Wait(INFINITE);
 }
 
 }
 
-bool wxCondition::Wait(wxMutex& mutex,
-                       unsigned long sec,
+bool wxCondition::Wait(unsigned long sec,
                        unsigned long nsec)
 {
                        unsigned long nsec)
 {
-    return p_internal->Wait(mutex, sec*1000 + nsec/1000000);
+    return m_internal->Wait(sec*1000 + nsec/1000000);
 }
 
 void wxCondition::Signal()
 }
 
 void wxCondition::Signal()
@@ -255,7 +252,7 @@ void wxCondition::Signal()
     // someone waits on it. In any case, the system will return it to a non
     // signalled state afterwards. If multiple threads are waiting, only one
     // will be woken up.
     // someone waits on it. In any case, the system will return it to a non
     // signalled state afterwards. If multiple threads are waiting, only one
     // will be woken up.
-    if ( !::SetEvent(p_internal->event) )
+    if ( !::SetEvent(m_internal->event) )
     {
         wxLogLastError("SetEvent");
     }
     {
         wxLogLastError("SetEvent");
     }
@@ -266,7 +263,7 @@ void wxCondition::Broadcast()
     // this works because all these threads are already waiting and so each
     // SetEvent() inside Signal() is really a PulseEvent() because the event
     // state is immediately returned to non-signaled
     // this works because all these threads are already waiting and so each
     // SetEvent() inside Signal() is really a PulseEvent() because the event
     // state is immediately returned to non-signaled
-    for ( int i = 0; i < p_internal->waiters; i++ )
+    for ( int i = 0; i < m_internal->waiters; i++ )
     {
         Signal();
     }
     {
         Signal();
     }
@@ -378,8 +375,8 @@ DWORD wxThreadInternal::WinThreadStart(wxThread *thread)
 
     // enter m_critsect before changing the thread state
     thread->m_critsect.Enter();
 
     // enter m_critsect before changing the thread state
     thread->m_critsect.Enter();
-    bool wasCancelled = thread->p_internal->GetState() == STATE_CANCELED;
-    thread->p_internal->SetState(STATE_EXITED);
+    bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
+    thread->m_internal->SetState(STATE_EXITED);
     thread->m_critsect.Leave();
 
     thread->OnExit();
     thread->m_critsect.Leave();
 
     thread->OnExit();
@@ -535,14 +532,14 @@ void wxThread::Sleep(unsigned long milliseconds)
 
 wxThread::wxThread(wxThreadKind kind)
 {
 
 wxThread::wxThread(wxThreadKind kind)
 {
-    p_internal = new wxThreadInternal();
+    m_internal = new wxThreadInternal();
 
     m_isDetached = kind == wxTHREAD_DETACHED;
 }
 
 wxThread::~wxThread()
 {
 
     m_isDetached = kind == wxTHREAD_DETACHED;
 }
 
 wxThread::~wxThread()
 {
-    delete p_internal;
+    delete m_internal;
 }
 
 // create/start thread
 }
 
 // create/start thread
@@ -552,7 +549,7 @@ wxThreadError wxThread::Create()
 {
     wxCriticalSectionLocker lock(m_critsect);
 
 {
     wxCriticalSectionLocker lock(m_critsect);
 
-    if ( !p_internal->Create(this) )
+    if ( !m_internal->Create(this) )
         return wxTHREAD_NO_RESOURCE;
 
     return wxTHREAD_NO_ERROR;
         return wxTHREAD_NO_RESOURCE;
 
     return wxTHREAD_NO_ERROR;
@@ -562,7 +559,7 @@ wxThreadError wxThread::Run()
 {
     wxCriticalSectionLocker lock(m_critsect);
 
 {
     wxCriticalSectionLocker lock(m_critsect);
 
-    if ( p_internal->GetState() != STATE_NEW )
+    if ( m_internal->GetState() != STATE_NEW )
     {
         // actually, it may be almost any state at all, not only STATE_RUNNING
         return wxTHREAD_RUNNING;
     {
         // actually, it may be almost any state at all, not only STATE_RUNNING
         return wxTHREAD_RUNNING;
@@ -579,14 +576,14 @@ wxThreadError wxThread::Pause()
 {
     wxCriticalSectionLocker lock(m_critsect);
 
 {
     wxCriticalSectionLocker lock(m_critsect);
 
-    return p_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
+    return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
 }
 
 wxThreadError wxThread::Resume()
 {
     wxCriticalSectionLocker lock(m_critsect);
 
 }
 
 wxThreadError wxThread::Resume()
 {
     wxCriticalSectionLocker lock(m_critsect);
 
-    return p_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
+    return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
 }
 
 // stopping thread
 }
 
 // stopping thread
@@ -603,7 +600,7 @@ wxThread::ExitCode wxThread::Wait()
 
     (void)Delete(&rc);
 
 
     (void)Delete(&rc);
 
-    p_internal->Free();
+    m_internal->Free();
 
     return rc;
 }
 
     return rc;
 }
@@ -616,7 +613,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc)
     if ( IsPaused() )
         Resume();
 
     if ( IsPaused() )
         Resume();
 
-    HANDLE hThread = p_internal->GetHandle();
+    HANDLE hThread = m_internal->GetHandle();
 
     if ( IsRunning() )
     {
 
     if ( IsRunning() )
     {
@@ -634,7 +631,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc)
         {
             wxCriticalSectionLocker lock(m_critsect);
 
         {
             wxCriticalSectionLocker lock(m_critsect);
 
-            p_internal->Cancel();
+            m_internal->Cancel();
         }
 
 #if wxUSE_GUI
         }
 
 #if wxUSE_GUI
@@ -742,14 +739,14 @@ wxThreadError wxThread::Kill()
     if ( !IsRunning() )
         return wxTHREAD_NOT_RUNNING;
 
     if ( !IsRunning() )
         return wxTHREAD_NOT_RUNNING;
 
-    if ( !::TerminateThread(p_internal->GetHandle(), (DWORD)-1) )
+    if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
     {
         wxLogSysError(_("Couldn't terminate thread"));
 
         return wxTHREAD_MISC_ERROR;
     }
 
     {
         wxLogSysError(_("Couldn't terminate thread"));
 
         return wxTHREAD_MISC_ERROR;
     }
 
-    p_internal->Free();
+    m_internal->Free();
 
     if ( IsDetached() )
     {
 
     if ( IsDetached() )
     {
@@ -761,7 +758,7 @@ wxThreadError wxThread::Kill()
 
 void wxThread::Exit(ExitCode status)
 {
 
 void wxThread::Exit(ExitCode status)
 {
-    p_internal->Free();
+    m_internal->Free();
 
     if ( IsDetached() )
     {
 
     if ( IsDetached() )
     {
@@ -784,50 +781,50 @@ void wxThread::SetPriority(unsigned int prio)
 {
     wxCriticalSectionLocker lock(m_critsect);
 
 {
     wxCriticalSectionLocker lock(m_critsect);
 
-    p_internal->SetPriority(prio);
+    m_internal->SetPriority(prio);
 }
 
 unsigned int wxThread::GetPriority() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
 }
 
 unsigned int wxThread::GetPriority() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
-    return p_internal->GetPriority();
+    return m_internal->GetPriority();
 }
 
 unsigned long wxThread::GetId() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
 }
 
 unsigned long wxThread::GetId() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
-    return (unsigned long)p_internal->GetId();
+    return (unsigned long)m_internal->GetId();
 }
 
 bool wxThread::IsRunning() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
 }
 
 bool wxThread::IsRunning() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
-    return p_internal->GetState() == STATE_RUNNING;
+    return m_internal->GetState() == STATE_RUNNING;
 }
 
 bool wxThread::IsAlive() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
 }
 
 bool wxThread::IsAlive() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
-    return (p_internal->GetState() == STATE_RUNNING) ||
-           (p_internal->GetState() == STATE_PAUSED);
+    return (m_internal->GetState() == STATE_RUNNING) ||
+           (m_internal->GetState() == STATE_PAUSED);
 }
 
 bool wxThread::IsPaused() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
 }
 
 bool wxThread::IsPaused() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
-    return p_internal->GetState() == STATE_PAUSED;
+    return m_internal->GetState() == STATE_PAUSED;
 }
 
 bool wxThread::TestDestroy()
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
 }
 
 bool wxThread::TestDestroy()
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
 
-    return p_internal->GetState() == STATE_CANCELED;
+    return m_internal->GetState() == STATE_CANCELED;
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
index a54694647ccf92fd869abe677b123438d33295f0..1ab453633f345e0f4bf6f2d1c9e7f781ce470601 100644 (file)
@@ -60,6 +60,48 @@ enum wxThreadState
     STATE_EXITED        // thread doesn't exist any more
 };
 
     STATE_EXITED        // thread doesn't exist any more
 };
 
+// the exit value of a thread which has been cancelled
+static const wxThread::ExitCode EXITCODE_CANCELLED = (wxThread::ExitCode)-1;
+
+// our trace mask
+#define TRACE_THREADS   _T("thread")
+
+// ----------------------------------------------------------------------------
+// private functions
+// ----------------------------------------------------------------------------
+
+static void ScheduleThreadForDeletion();
+static void DeleteThread(wxThread *This);
+
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+// same as wxMutexLocker but for "native" mutex
+class MutexLock
+{
+public:
+    MutexLock(pthread_mutex_t& mutex)
+    {
+        m_mutex = &mutex;
+        if ( pthread_mutex_lock(m_mutex) != 0 )
+        {
+            wxLogDebug(_T("pthread_mutex_lock() failed"));
+        }
+    }
+
+    ~MutexLock()
+    {
+        if ( pthread_mutex_unlock(m_mutex) != 0 )
+        {
+            wxLogDebug(_T("pthread_mutex_unlock() failed"));
+        }
+    }
+
+private:
+    pthread_mutex_t *m_mutex;
+};
+
 // ----------------------------------------------------------------------------
 // types
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // types
 // ----------------------------------------------------------------------------
@@ -81,8 +123,21 @@ static pthread_t gs_tidMain;
 // the key for the pointer to the associated wxThread object
 static pthread_key_t gs_keySelf;
 
 // the key for the pointer to the associated wxThread object
 static pthread_key_t gs_keySelf;
 
-// this mutex must be acquired before any call to a GUI function
-static wxMutex *gs_mutexGui;
+// the number of threads which are being deleted - the program won't exit
+// until there are any left
+static size_t gs_nThreadsBeingDeleted = 0;
+
+// a mutex to protect gs_nThreadsBeingDeleted
+static pthread_mutex_t gs_mutexDeleteThread = PTHREAD_MUTEX_INITIALIZER;
+
+// and a condition variable which will be signaled when all
+// gs_nThreadsBeingDeleted will have been deleted
+static wxCondition *gs_condAllDeleted = (wxCondition *)NULL;
+
+#if wxUSE_GUI
+    // this mutex must be acquired before any call to a GUI function
+    static wxMutex *gs_mutexGui;
+#endif // wxUSE_GUI
 
 // ============================================================================
 // implementation
 
 // ============================================================================
 // implementation
@@ -95,14 +150,14 @@ static wxMutex *gs_mutexGui;
 class wxMutexInternal
 {
 public:
 class wxMutexInternal
 {
 public:
-    pthread_mutex_t p_mutex;
+    pthread_mutex_t m_mutex;
 };
 
 wxMutex::wxMutex()
 {
 };
 
 wxMutex::wxMutex()
 {
-    p_internal = new wxMutexInternal;
-    
-    pthread_mutex_init(&(p_internal->p_mutex),
+    m_internal = new wxMutexInternal;
+
+    pthread_mutex_init(&(m_internal->m_mutex),
                        (pthread_mutexattr_t*) NULL );
     m_locked = 0;
 }
                        (pthread_mutexattr_t*) NULL );
     m_locked = 0;
 }
@@ -112,13 +167,13 @@ wxMutex::~wxMutex()
     if (m_locked > 0)
         wxLogDebug(wxT("Freeing a locked mutex (%d locks)"), m_locked);
 
     if (m_locked > 0)
         wxLogDebug(wxT("Freeing a locked mutex (%d locks)"), m_locked);
 
-    pthread_mutex_destroy( &(p_internal->p_mutex) );
-    delete p_internal;
+    pthread_mutex_destroy( &(m_internal->m_mutex) );
+    delete m_internal;
 }
 
 wxMutexError wxMutex::Lock()
 {
 }
 
 wxMutexError wxMutex::Lock()
 {
-    int err = pthread_mutex_lock( &(p_internal->p_mutex) );
+    int err = pthread_mutex_lock( &(m_internal->m_mutex) );
     if (err == EDEADLK)
     {
         wxLogDebug(wxT("Locking this mutex would lead to deadlock!"));
     if (err == EDEADLK)
     {
         wxLogDebug(wxT("Locking this mutex would lead to deadlock!"));
@@ -138,7 +193,7 @@ wxMutexError wxMutex::TryLock()
         return wxMUTEX_BUSY;
     }
 
         return wxMUTEX_BUSY;
     }
 
-    int err = pthread_mutex_trylock( &(p_internal->p_mutex) );
+    int err = pthread_mutex_trylock( &(m_internal->m_mutex) );
     switch (err)
     {
         case EBUSY: return wxMUTEX_BUSY;
     switch (err)
     {
         case EBUSY: return wxMUTEX_BUSY;
@@ -162,7 +217,7 @@ wxMutexError wxMutex::Unlock()
         return wxMUTEX_UNLOCKED;
     }
 
         return wxMUTEX_UNLOCKED;
     }
 
-    pthread_mutex_unlock( &(p_internal->p_mutex) );
+    pthread_mutex_unlock( &(m_internal->m_mutex) );
 
     return wxMUTEX_NO_ERROR;
 }
 
     return wxMUTEX_NO_ERROR;
 }
@@ -171,48 +226,151 @@ wxMutexError wxMutex::Unlock()
 // wxCondition (Posix implementation)
 //--------------------------------------------------------------------
 
 // wxCondition (Posix implementation)
 //--------------------------------------------------------------------
 
+// notice that we must use a mutex with POSIX condition variables to ensure
+// that the worker thread doesn't signal condition before the waiting thread
+// starts to wait for it
 class wxConditionInternal
 {
 public:
 class wxConditionInternal
 {
 public:
-  pthread_cond_t p_condition;
+    wxConditionInternal();
+    ~wxConditionInternal();
+
+    void Wait();
+    bool WaitWithTimeout(const timespec* ts);
+
+    void Signal();
+    void Broadcast();
+
+private:
+    pthread_mutex_t m_mutex;
+    pthread_cond_t m_condition;
 };
 
 };
 
+wxConditionInternal::wxConditionInternal()
+{
+    if ( pthread_cond_init(&m_condition, (pthread_condattr_t *)NULL) != 0 )
+    {
+        // this is supposed to never happen
+        wxFAIL_MSG( _T("pthread_cond_init() failed") );
+    }
+
+    if ( pthread_mutex_init(&m_mutex, (pthread_mutexattr_t*)NULL) != 0 )
+    {
+        // neither this
+        wxFAIL_MSG( _T("wxCondition: pthread_mutex_init() failed") );
+    }
+
+    // initially the mutex is locked, so no thread can Signal() or Broadcast()
+    // until another thread starts to Wait()
+    if ( pthread_mutex_lock(&m_mutex) != 0 )
+    {
+        wxFAIL_MSG( _T("wxCondition: pthread_mutex_lock() failed") );
+    }
+}
+
+wxConditionInternal::~wxConditionInternal()
+{
+    if ( pthread_cond_destroy( &m_condition ) != 0 )
+    {
+        wxLogDebug(_T("Failed to destroy condition variable (some "
+                      "threads are probably still waiting on it?)"));
+    }
+
+    if ( pthread_mutex_unlock( &m_mutex ) != 0 )
+    {
+        wxLogDebug(_T("wxCondition: failed to unlock the mutex"));
+    }
+
+    if ( pthread_mutex_destroy( &m_mutex ) != 0 )
+    {
+        wxLogDebug(_T("Failed to destroy mutex (it is probably locked)"));
+    }
+}
+
+void wxConditionInternal::Wait()
+{
+    if ( pthread_cond_wait( &m_condition, &m_mutex ) != 0 )
+    {
+        // not supposed to ever happen
+        wxFAIL_MSG( _T("pthread_cond_wait() failed") );
+    }
+}
+
+bool wxConditionInternal::WaitWithTimeout(const timespec* ts)
+{
+    switch ( pthread_cond_timedwait( &m_condition, &m_mutex, ts ) )
+    {
+        case 0:
+            // condition signaled
+            return TRUE;
+
+        default:
+            wxLogDebug(_T("pthread_cond_timedwait() failed"));
+
+            // fall through
+
+        case ETIMEDOUT:
+        case EINTR:
+            // wait interrupted or timeout elapsed
+            return FALSE;
+    }
+}
+
+void wxConditionInternal::Signal()
+{
+    MutexLock lock(m_mutex);
+
+    if ( pthread_cond_signal( &m_condition ) != 0 )
+    {
+        // shouldn't ever happen
+        wxFAIL_MSG(_T("pthread_cond_signal() failed"));
+    }
+}
+
+void wxConditionInternal::Broadcast()
+{
+    MutexLock lock(m_mutex);
+
+    if ( pthread_cond_broadcast( &m_condition ) != 0 )
+    {
+        // shouldn't ever happen
+        wxFAIL_MSG(_T("pthread_cond_broadcast() failed"));
+    }
+}
+
 wxCondition::wxCondition()
 {
 wxCondition::wxCondition()
 {
-    p_internal = new wxConditionInternal;
-    pthread_cond_init( &(p_internal->p_condition),
-                       (pthread_condattr_t *) NULL );
+    m_internal = new wxConditionInternal;
 }
 
 wxCondition::~wxCondition()
 {
 }
 
 wxCondition::~wxCondition()
 {
-    pthread_cond_destroy( &(p_internal->p_condition) );
-
-    delete p_internal;
+    delete m_internal;
 }
 
 }
 
-void wxCondition::Wait(wxMutex& mutex)
+void wxCondition::Wait()
 {
 {
-    pthread_cond_wait( &(p_internal->p_condition), &(mutex.p_internal->p_mutex) );
+    m_internal->Wait();
 }
 
 }
 
-bool wxCondition::Wait(wxMutex& mutex, unsigned long sec, unsigned long nsec)
+bool wxCondition::Wait(unsigned long sec, unsigned long nsec)
 {
 {
-    struct timespec tspec;
+    timespec tspec;
 
 
-    tspec.tv_sec = time(0L)+sec;
+    tspec.tv_sec = time(0L) + sec;  // FIXME is time(0) correct here?
     tspec.tv_nsec = nsec;
     tspec.tv_nsec = nsec;
-    return (pthread_cond_timedwait(&(p_internal->p_condition), &(mutex.p_internal->p_mutex), &tspec) != ETIMEDOUT);
+
+    return m_internal->WaitWithTimeout(&tspec);
 }
 
 void wxCondition::Signal()
 {
 }
 
 void wxCondition::Signal()
 {
-    pthread_cond_signal( &(p_internal->p_condition) );
+    m_internal->Signal();
 }
 
 void wxCondition::Broadcast()
 {
 }
 
 void wxCondition::Broadcast()
 {
-    pthread_cond_broadcast( &(p_internal->p_condition) );
+    m_internal->Broadcast();
 }
 
 //--------------------------------------------------------------------
 }
 
 //--------------------------------------------------------------------
@@ -258,43 +416,63 @@ public:
         // "cancelled" flag
     void SetCancelFlag() { m_cancelled = TRUE; }
     bool WasCancelled() const { return m_cancelled; }
         // "cancelled" flag
     void SetCancelFlag() { m_cancelled = TRUE; }
     bool WasCancelled() const { return m_cancelled; }
+        // exit code
+    void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }
+    wxThread::ExitCode GetExitCode() const { return m_exitcode; }
+
+        // tell the thread that it is a detached one
+    void Detach() { m_shouldBeJoined = m_shouldBroadcast = FALSE; }
+        // but even detached threads need to notifyus about their termination
+        // sometimes - tell the thread that it should do it
+    void Notify() { m_shouldBroadcast = TRUE; }
 
 private:
     pthread_t     m_threadId;   // id of the thread
     wxThreadState m_state;      // see wxThreadState enum
     int           m_prio;       // in wxWindows units: from 0 to 100
 
 
 private:
     pthread_t     m_threadId;   // id of the thread
     wxThreadState m_state;      // see wxThreadState enum
     int           m_prio;       // in wxWindows units: from 0 to 100
 
-    // set when the thread should terminate
+    // this flag is set when the thread should terminate
     bool m_cancelled;
 
     bool m_cancelled;
 
-    // this (mutex, cond) pair is used to synchronize the main thread and this
-    // thread in several situations:
-    //  1. The thread function blocks until condition is signaled by Run() when
-    //     it's initially created - this allows thread creation in "suspended"
-    //     state
-    //  2. The Delete() function blocks until the condition is signaled when the
-    //     thread exits.
-    // GL: On Linux, this may fail because we can have a deadlock in either
-    //     SignalExit() or Wait(): so we add m_end_mutex for the finalization.
-    wxMutex     m_mutex, m_end_mutex;
-    wxCondition m_cond;
-
-    // another (mutex, cond) pair for Pause()/Resume() usage
-    //
-    // VZ: it's possible that we might reuse the mutex and condition from above
-    //     for this too, but as I'm not at all sure that it won't create subtle
-    //     problems with race conditions between, say, Pause() and Delete() I
-    //     prefer this may be a bit less efficient but much safer solution
-    wxMutex     m_mutexSuspend;
+    // the thread exit code - only used for joinable (!detached) threads and
+    // is only valid after the thread termination
+    wxThread::ExitCode m_exitcode;
+
+    // many threads may call Wait(), but only one of them should call
+    // pthread_join(), so we have to keep track of this
+    wxCriticalSection m_csJoinFlag;
+    bool m_shouldBeJoined;
+    bool m_shouldBroadcast;
+
+    // VZ: it's possible that we might do with less than three different
+    //     condition objects - for example, m_condRun and m_condEnd a priori
+    //     won't be used in the same time. But for now I prefer this may be a
+    //     bit less efficient but safer solution of having distinct condition
+    //     variables for each purpose.
+
+    // this condition is signaled by Run() and the threads Entry() is not
+    // called before it is done
+    wxCondition m_condRun;
+
+    // this one is signaled when the thread should resume after having been
+    // Pause()d
     wxCondition m_condSuspend;
     wxCondition m_condSuspend;
+
+    // finally this one is signalled when the thread exits
+    wxCondition m_condEnd;
 };
 
 };
 
+// ----------------------------------------------------------------------------
+// thread startup and exit functions
+// ----------------------------------------------------------------------------
+
 void *wxThreadInternal::PthreadStart(void *ptr)
 {
     wxThread *thread = (wxThread *)ptr;
 void *wxThreadInternal::PthreadStart(void *ptr)
 {
     wxThread *thread = (wxThread *)ptr;
-    wxThreadInternal *pthread = thread->p_internal;
-    void *status;
+    wxThreadInternal *pthread = thread->m_internal;
 
 
+    // associate the thread pointer with the newly created thread so that
+    // wxThread::This() will work
     int rc = pthread_setspecific(gs_keySelf, thread);
     if ( rc != 0 )
     {
     int rc = pthread_setspecific(gs_keySelf, thread);
     if ( rc != 0 )
     {
@@ -302,25 +480,44 @@ void *wxThreadInternal::PthreadStart(void *ptr)
 
         return (void *)-1;
     }
 
         return (void *)-1;
     }
+
 #if HAVE_THREAD_CLEANUP_FUNCTIONS
 #if HAVE_THREAD_CLEANUP_FUNCTIONS
-    // Install the cleanup handler.
+    // install the cleanup handler which will be called if the thread is
+    // cancelled
     pthread_cleanup_push(wxThreadInternal::PthreadCleanup, ptr);
     pthread_cleanup_push(wxThreadInternal::PthreadCleanup, ptr);
-#endif
+#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
 
     // wait for the condition to be signaled from Run()
 
     // wait for the condition to be signaled from Run()
-    // mutex state: currently locked by the thread which created us
-    pthread->m_cond.Wait(pthread->m_mutex);
-    // mutex state: locked again on exit of Wait()
+    pthread->m_condRun.Wait();
 
     // call the main entry
 
     // call the main entry
-    status = thread->Entry();
+    pthread->m_exitcode = thread->Entry();
+
+    wxLogTrace(TRACE_THREADS, _T("Thread %ld left its Entry()."),
+               pthread->GetId());
+
+    {
+        wxCriticalSectionLocker lock(thread->m_critsect);
 
 
+        wxLogTrace(TRACE_THREADS, _T("Thread %ld changes state to EXITED."),
+                   pthread->GetId());
+
+        // change the state of the thread to "exited" so that PthreadCleanup
+        // handler won't do anything from now (if it's called before we do
+        // pthread_cleanup_pop below)
+        pthread->SetState(STATE_EXITED);
+    }
+
+    // NB: at least under Linux, pthread_cleanup_push/pop are macros and pop
+    //     contains the matching '}' for the '{' in push, so they must be used
+    //     in the same block!
 #if HAVE_THREAD_CLEANUP_FUNCTIONS
 #if HAVE_THREAD_CLEANUP_FUNCTIONS
+    // remove the cleanup handler without executing it
     pthread_cleanup_pop(FALSE);
     pthread_cleanup_pop(FALSE);
-#endif
+#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
 
     // terminate the thread
 
     // terminate the thread
-    thread->Exit(status);
+    thread->Exit(pthread->m_exitcode);
 
     wxFAIL_MSG(wxT("wxThread::Exit() can't return."));
 
 
     wxFAIL_MSG(wxT("wxThread::Exit() can't return."));
 
@@ -328,26 +525,30 @@ void *wxThreadInternal::PthreadStart(void *ptr)
 }
 
 #if HAVE_THREAD_CLEANUP_FUNCTIONS
 }
 
 #if HAVE_THREAD_CLEANUP_FUNCTIONS
-// Only called when the thread is explicitely killed.
 
 
+// this handler is called when the thread is cancelled
 void wxThreadInternal::PthreadCleanup(void *ptr)
 {
     wxThread *thread = (wxThread *) ptr;
 
 void wxThreadInternal::PthreadCleanup(void *ptr)
 {
     wxThread *thread = (wxThread *) ptr;
 
-    // The thread is already considered as finished.
-    if (thread->p_internal->GetState() == STATE_EXITED)
-      return;
+    {
+        wxCriticalSectionLocker lock(thread->m_critsect);
+        if ( thread->m_internal->GetState() == STATE_EXITED )
+        {
+            // thread is already considered as finished.
+            return;
+        }
+    }
 
 
-    // first call user-level clean up code
-    thread->OnExit();
+    // exit the thread gracefully
+    thread->Exit(EXITCODE_CANCELLED);
+}
 
 
-    // next wake up the threads waiting for us (OTOH, this function won't retur
-    // until someone waited for us!)
-    thread->p_internal->SetState(STATE_EXITED);
+#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
 
 
-    thread->p_internal->SignalExit();
-}
-#endif
+// ----------------------------------------------------------------------------
+// wxThreadInternal
+// ----------------------------------------------------------------------------
 
 wxThreadInternal::wxThreadInternal()
 {
 
 wxThreadInternal::wxThreadInternal()
 {
@@ -355,80 +556,57 @@ wxThreadInternal::wxThreadInternal()
     m_cancelled = FALSE;
     m_prio = WXTHREAD_DEFAULT_PRIORITY;
     m_threadId = 0;
     m_cancelled = FALSE;
     m_prio = WXTHREAD_DEFAULT_PRIORITY;
     m_threadId = 0;
+    m_exitcode = 0;
 
 
-    // this mutex is locked during almost all thread lifetime - it will only be
-    // unlocked in the very end
-    m_mutex.Lock();
-    // this mutex is used by wxThreadInternal::Wait() and by
-    // wxThreadInternal::SignalExit(). We don't use m_mutex because of a
-    // possible deadlock in either Wait() or SignalExit().
-    m_end_mutex.Lock();
-
-    // this mutex is used in Pause()/Resume() and is also locked all the time
-    // unless the thread is paused
-    m_mutexSuspend.Lock();
+    // defaults for joinable threads
+    m_shouldBeJoined = TRUE;
+    m_shouldBroadcast = TRUE;
 }
 
 wxThreadInternal::~wxThreadInternal()
 {
 }
 
 wxThreadInternal::~wxThreadInternal()
 {
-    // GL: moved to SignalExit
-    // m_mutexSuspend.Unlock();
-
-    // note that m_mutex will be unlocked by the thread which waits for our
-    // termination
-
-    // In the case, we didn't start the thread, all these mutex are locked:
-    // we must unlock them.
-    if (m_mutex.IsLocked())
-      m_mutex.Unlock();
-
-    if (m_end_mutex.IsLocked())
-      m_end_mutex.Unlock();
-
-    if (m_mutexSuspend.IsLocked())
-      m_mutexSuspend.Unlock();
 }
 
 wxThreadError wxThreadInternal::Run()
 {
     wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
 }
 
 wxThreadError wxThreadInternal::Run()
 {
     wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
-                 wxT("thread may only be started once after successful Create()") );
+                 wxT("thread may only be started once after Create()") );
 
 
-    // the mutex was locked on Create(), so we will be able to lock it again
-    // only when the thread really starts executing and enters the wait -
-    // otherwise we might signal the condition before anybody is waiting for it
-    wxMutexLocker lock(m_mutex);
-    m_cond.Signal();
+    m_condRun.Signal();
 
 
-    m_state = STATE_RUNNING;
+    SetState(STATE_RUNNING);
 
     return wxTHREAD_NO_ERROR;
 
     return wxTHREAD_NO_ERROR;
-
-    // now the mutex is unlocked back - but just to allow Wait() function to
-    // terminate by relocking it, so the net result is that the worker thread
-    // starts executing and the mutex is still locked
 }
 
 void wxThreadInternal::Wait()
 {
 }
 
 void wxThreadInternal::Wait()
 {
-    wxCHECK_RET( WasCancelled(), wxT("thread should have been cancelled first") );
-
     // if the thread we're waiting for is waiting for the GUI mutex, we will
     // deadlock so make sure we release it temporarily
     if ( wxThread::IsMain() )
         wxMutexGuiLeave();
 
     // if the thread we're waiting for is waiting for the GUI mutex, we will
     // deadlock so make sure we release it temporarily
     if ( wxThread::IsMain() )
         wxMutexGuiLeave();
 
-    // entering Wait() releases the mutex thus allowing SignalExit() to acquire
-    // it and to signal us its termination
-    m_cond.Wait(m_end_mutex);
+    // wait until the thread terminates (we're blocking in _another_ thread,
+    // of course)
+    m_condEnd.Wait();
 
 
-    // mutex is still in the locked state - relocked on exit from Wait(), so
-    // unlock it - we don't need it any more, the thread has already terminated
-    m_end_mutex.Unlock();
+    // to avoid memory leaks we should call pthread_join(), but it must only
+    // be done once
+    wxCriticalSectionLocker lock(m_csJoinFlag);
 
 
-    // After that, we wait for the real end of the other thread.
-    pthread_join(GetId(), NULL);
+    if ( m_shouldBeJoined )
+    {
+        // FIXME shouldn't we set cancellation type to DISABLED here? If we're
+        //       cancelled inside pthread_join(), things will almost certainly
+        //       break - but if we disable the cancellation, we might deadlock
+        if ( pthread_join(GetId(), &m_exitcode) != 0 )
+        {
+            wxLogError(_T("Failed to join a thread, potential memory leak "
+                          "detected - please restart the program"));
+        }
+
+        m_shouldBeJoined = FALSE;
+    }
 
     // reacquire GUI mutex
     if ( wxThread::IsMain() )
 
     // reacquire GUI mutex
     if ( wxThread::IsMain() )
@@ -437,18 +615,15 @@ void wxThreadInternal::Wait()
 
 void wxThreadInternal::SignalExit()
 {
 
 void wxThreadInternal::SignalExit()
 {
-    // GL: Unlock mutexSuspend here.
-    m_mutexSuspend.Unlock();
-
-    // as mutex is currently locked, this will block until some other thread
-    // (normally the same which created this one) unlocks it by entering Wait()
-    m_end_mutex.Lock();
+    wxLogTrace(TRACE_THREADS, _T("Thread %ld about to exit."), GetId());
 
 
-    // wake up all the threads waiting for our termination
-    m_cond.Broadcast();
+    SetState(STATE_EXITED);
 
 
-    // after this call mutex will be finally unlocked
-    m_end_mutex.Unlock();
+    // wake up all the threads waiting for our termination - if there are any
+    if ( m_shouldBroadcast )
+    {
+        m_condEnd.Broadcast();
+    }
 }
 
 void wxThreadInternal::Pause()
 }
 
 void wxThreadInternal::Pause()
@@ -458,14 +633,10 @@ void wxThreadInternal::Pause()
     wxCHECK_RET( m_state == STATE_PAUSED,
                  wxT("thread must first be paused with wxThread::Pause().") );
 
     wxCHECK_RET( m_state == STATE_PAUSED,
                  wxT("thread must first be paused with wxThread::Pause().") );
 
-    // don't pause the thread which is being terminated - this would lead to
-    // deadlock if the thread is paused after Delete() had called Resume() but
-    // before it had time to call Wait()
-    if ( WasCancelled() )
-        return;
+    wxLogTrace(TRACE_THREADS, _T("Thread %ld goes to sleep."), GetId());
 
     // wait until the condition is signaled from Resume()
 
     // wait until the condition is signaled from Resume()
-    m_condSuspend.Wait(m_mutexSuspend);
+    m_condSuspend.Wait();
 }
 
 void wxThreadInternal::Resume()
 }
 
 void wxThreadInternal::Resume()
@@ -473,15 +644,16 @@ void wxThreadInternal::Resume()
     wxCHECK_RET( m_state == STATE_PAUSED,
                  wxT("can't resume thread which is not suspended.") );
 
     wxCHECK_RET( m_state == STATE_PAUSED,
                  wxT("can't resume thread which is not suspended.") );
 
-    // we will be able to lock this mutex only when Pause() starts waiting
-    wxMutexLocker lock(m_mutexSuspend);
+    wxLogTrace(TRACE_THREADS, _T("Waking up thread %ld"), GetId());
+
+    // wake up Pause()
     m_condSuspend.Signal();
 
     SetState(STATE_RUNNING);
 }
 
 // -----------------------------------------------------------------------------
     m_condSuspend.Signal();
 
     SetState(STATE_RUNNING);
 }
 
 // -----------------------------------------------------------------------------
-// static functions
+// wxThread static functions
 // -----------------------------------------------------------------------------
 
 wxThread *wxThread::This()
 // -----------------------------------------------------------------------------
 
 wxThread *wxThread::This()
@@ -513,38 +685,42 @@ wxThread::wxThread(wxThreadKind kind)
     // add this thread to the global list of all threads
     gs_allThreads.Add(this);
 
     // add this thread to the global list of all threads
     gs_allThreads.Add(this);
 
-    p_internal = new wxThreadInternal();
+    m_internal = new wxThreadInternal();
 
     m_isDetached = kind == wxTHREAD_DETACHED;
 }
 
 wxThreadError wxThread::Create()
 {
 
     m_isDetached = kind == wxTHREAD_DETACHED;
 }
 
 wxThreadError wxThread::Create()
 {
-    if (p_internal->GetState() != STATE_NEW)
+    if ( m_internal->GetState() != STATE_NEW )
+    {
+        // don't recreate thread
         return wxTHREAD_RUNNING;
         return wxTHREAD_RUNNING;
+    }
 
     // set up the thread attribute: right now, we only set thread priority
     pthread_attr_t attr;
     pthread_attr_init(&attr);
 
 #ifdef HAVE_THREAD_PRIORITY_FUNCTIONS
 
     // set up the thread attribute: right now, we only set thread priority
     pthread_attr_t attr;
     pthread_attr_init(&attr);
 
 #ifdef HAVE_THREAD_PRIORITY_FUNCTIONS
-    int prio;
-    if ( pthread_attr_getschedpolicy(&attr, &prio) != 0 )
+    int policy;
+    if ( pthread_attr_getschedpolicy(&attr, &policy) != 0 )
     {
         wxLogError(_("Cannot retrieve thread scheduling policy."));
     }
 
     {
         wxLogError(_("Cannot retrieve thread scheduling policy."));
     }
 
-    int min_prio = sched_get_priority_min(prio),
-        max_prio = sched_get_priority_max(prio);
+    int min_prio = sched_get_priority_min(policy),
+        max_prio = sched_get_priority_max(policy),
+        prio = m_internal->GetPriority();
 
     if ( min_prio == -1 || max_prio == -1 )
     {
         wxLogError(_("Cannot get priority range for scheduling policy %d."),
 
     if ( min_prio == -1 || max_prio == -1 )
     {
         wxLogError(_("Cannot get priority range for scheduling policy %d."),
-                   prio);
+                   policy);
     }
     else if ( max_prio == min_prio )
     {
     }
     else if ( max_prio == min_prio )
     {
-        if ( p_internal->GetPriority() != WXTHREAD_DEFAULT_PRIORITY )
+        if ( prio != WXTHREAD_DEFAULT_PRIORITY )
         {
             // notify the programmer that this doesn't work here
             wxLogWarning(_("Thread priority setting is ignored."));
         {
             // notify the programmer that this doesn't work here
             wxLogWarning(_("Thread priority setting is ignored."));
@@ -556,26 +732,61 @@ wxThreadError wxThread::Create()
     else
     {
         struct sched_param sp;
     else
     {
         struct sched_param sp;
-        pthread_attr_getschedparam(&attr, &sp);
-        sp.sched_priority = min_prio +
-                           (p_internal->GetPriority()*(max_prio-min_prio))/100;
-        pthread_attr_setschedparam(&attr, &sp);
+        if ( pthread_attr_getschedparam(&attr, &sp) != 0 )
+        {
+            wxFAIL_MSG(_T("pthread_attr_getschedparam() failed"));
+        }
+
+        sp.sched_priority = min_prio + (prio*(max_prio - min_prio))/100;
+
+        if ( pthread_attr_setschedparam(&attr, &sp) != 0 )
+        {
+            wxFAIL_MSG(_T("pthread_attr_setschedparam(priority) failed"));
+        }
     }
 #endif // HAVE_THREAD_PRIORITY_FUNCTIONS
 
 #ifdef HAVE_PTHREAD_ATTR_SETSCOPE
     // this will make the threads created by this process really concurrent
     }
 #endif // HAVE_THREAD_PRIORITY_FUNCTIONS
 
 #ifdef HAVE_PTHREAD_ATTR_SETSCOPE
     // this will make the threads created by this process really concurrent
-    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+    if ( pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) != 0 )
+    {
+        wxFAIL_MSG(_T("pthread_attr_setscope(PTHREAD_SCOPE_SYSTEM) failed"));
+    }
 #endif // HAVE_PTHREAD_ATTR_SETSCOPE
 
 #endif // HAVE_PTHREAD_ATTR_SETSCOPE
 
+    // VZ: assume that this one is always available (it's rather fundamental),
+    //     if this function is ever missing we should try to use
+    //     pthread_detach() instead (after thread creation)
+    if ( m_isDetached )
+    {
+        if ( pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0 )
+        {
+            wxFAIL_MSG(_T("pthread_attr_setdetachstate(DETACHED) failed"));
+        }
+
+        // never try to join detached threads
+        m_internal->Detach();
+    }
+    //else: threads are created joinable by default, it's ok
+
     // create the new OS thread object
     // create the new OS thread object
-    int rc = pthread_create(p_internal->GetIdPtr(), &attr,
-                            wxThreadInternal::PthreadStart, (void *)this);
-    pthread_attr_destroy(&attr);
+    int rc = pthread_create
+             (
+                m_internal->GetIdPtr(),
+                &attr,
+                wxThreadInternal::PthreadStart,
+                (void *)this
+             );
+
+    if ( pthread_attr_destroy(&attr) != 0 )
+    {
+        wxFAIL_MSG(_T("pthread_attr_destroy() failed"));
+    }
 
     if ( rc != 0 )
     {
 
     if ( rc != 0 )
     {
-        p_internal->SetState(STATE_EXITED);
+        m_internal->SetState(STATE_EXITED);
+
         return wxTHREAD_NO_RESOURCE;
     }
 
         return wxTHREAD_NO_RESOURCE;
     }
 
@@ -584,10 +795,12 @@ wxThreadError wxThread::Create()
 
 wxThreadError wxThread::Run()
 {
 
 wxThreadError wxThread::Run()
 {
-    wxCHECK_MSG( p_internal->GetId(), wxTHREAD_MISC_ERROR,
+    wxCriticalSectionLocker lock(m_critsect);
+
+    wxCHECK_MSG( m_internal->GetId(), wxTHREAD_MISC_ERROR,
                  wxT("must call wxThread::Create() first") );
 
                  wxT("must call wxThread::Create() first") );
 
-    return p_internal->Run();
+    return m_internal->Run();
 }
 
 // -----------------------------------------------------------------------------
 }
 
 // -----------------------------------------------------------------------------
@@ -602,11 +815,11 @@ void wxThread::SetPriority(unsigned int prio)
 
     wxCriticalSectionLocker lock(m_critsect);
 
 
     wxCriticalSectionLocker lock(m_critsect);
 
-    switch ( p_internal->GetState() )
+    switch ( m_internal->GetState() )
     {
         case STATE_NEW:
             // thread not yet started, priority will be set when it is
     {
         case STATE_NEW:
             // thread not yet started, priority will be set when it is
-            p_internal->SetPriority(prio);
+            m_internal->SetPriority(prio);
             break;
 
         case STATE_RUNNING:
             break;
 
         case STATE_RUNNING:
@@ -616,7 +829,7 @@ void wxThread::SetPriority(unsigned int prio)
                 struct sched_param sparam;
                 sparam.sched_priority = prio;
 
                 struct sched_param sparam;
                 sparam.sched_priority = prio;
 
-                if ( pthread_setschedparam(p_internal->GetId(),
+                if ( pthread_setschedparam(m_internal->GetId(),
                                            SCHED_OTHER, &sparam) != 0 )
                 {
                     wxLogError(_("Failed to set thread priority %d."), prio);
                                            SCHED_OTHER, &sparam) != 0 )
                 {
                     wxLogError(_("Failed to set thread priority %d."), prio);
@@ -635,12 +848,12 @@ unsigned int wxThread::GetPriority() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
 
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
 
-    return p_internal->GetPriority();
+    return m_internal->GetPriority();
 }
 
 unsigned long wxThread::GetId() const
 {
 }
 
 unsigned long wxThread::GetId() const
 {
-    return (unsigned long)p_internal->GetId();
+    return (unsigned long)m_internal->GetId();
 }
 
 // -----------------------------------------------------------------------------
 }
 
 // -----------------------------------------------------------------------------
@@ -651,35 +864,51 @@ wxThreadError wxThread::Pause()
 {
     wxCriticalSectionLocker lock(m_critsect);
 
 {
     wxCriticalSectionLocker lock(m_critsect);
 
-    if ( p_internal->GetState() != STATE_RUNNING )
+    if ( m_internal->GetState() != STATE_RUNNING )
     {
         wxLogDebug(wxT("Can't pause thread which is not running."));
 
         return wxTHREAD_NOT_RUNNING;
     }
 
     {
         wxLogDebug(wxT("Can't pause thread which is not running."));
 
         return wxTHREAD_NOT_RUNNING;
     }
 
-    p_internal->SetState(STATE_PAUSED);
+    // just set a flag, the thread will be really paused only during the next
+    // call to TestDestroy()
+    m_internal->SetState(STATE_PAUSED);
 
     return wxTHREAD_NO_ERROR;
 }
 
 wxThreadError wxThread::Resume()
 {
 
     return wxTHREAD_NO_ERROR;
 }
 
 wxThreadError wxThread::Resume()
 {
-    wxCriticalSectionLocker lock(m_critsect);
+    m_critsect.Enter();
 
 
-    if ( p_internal->GetState() == STATE_PAUSED )
-    {
-        m_critsect.Leave();
-        p_internal->Resume();
-        m_critsect.Enter();
+    wxThreadState state = m_internal->GetState();
 
 
-        return wxTHREAD_NO_ERROR;
-    }
-    else
+    // the thread might be not actually paused yet - if there were no call to
+    // TestDestroy() since the last call to Pause(), so avoid that
+    // TestDestroy() deadlocks trying to enter m_critsect by leaving it before
+    // calling Resume()
+    m_critsect.Leave();
+
+    switch ( state )
     {
     {
-        wxLogDebug(wxT("Attempt to resume a thread which is not paused."));
+        case STATE_PAUSED:
+            wxLogTrace(TRACE_THREADS, _T("Thread %ld is suspended, resuming."),
+                       GetId());
+
+            m_internal->Resume();
+
+            return wxTHREAD_NO_ERROR;
+
+        case STATE_EXITED:
+            wxLogTrace(TRACE_THREADS, _T("Thread %ld exited, won't resume."),
+                       GetId());
+            return wxTHREAD_NO_ERROR;
 
 
-        return wxTHREAD_MISC_ERROR;
+        default:
+            wxLogDebug(_T("Attempt to resume a thread which is not paused."));
+
+            return wxTHREAD_MISC_ERROR;
     }
 }
 
     }
 }
 
@@ -687,23 +916,34 @@ wxThreadError wxThread::Resume()
 // exiting thread
 // -----------------------------------------------------------------------------
 
 // exiting thread
 // -----------------------------------------------------------------------------
 
-wxThread::ExitCode Wait()
+wxThread::ExitCode wxThread::Wait()
 {
 {
-    wxFAIL_MSG("TODO");
+    wxCHECK_MSG( This() != this, (ExitCode)-1,
+                 _T("a thread can't wait for itself") );
+
+    wxCHECK_MSG( !m_isDetached, (ExitCode)-1,
+                 _T("can't wait for detached thread") );
+
+    m_internal->Wait();
 
 
-    return 0;
+    return m_internal->GetExitCode();
 }
 
 wxThreadError wxThread::Delete(ExitCode *rc)
 {
 }
 
 wxThreadError wxThread::Delete(ExitCode *rc)
 {
-    if (IsPaused())
-      Resume();
-
     m_critsect.Enter();
     m_critsect.Enter();
-    wxThreadState state = p_internal->GetState();
+    wxThreadState state = m_internal->GetState();
 
     // ask the thread to stop
 
     // ask the thread to stop
-    p_internal->SetCancelFlag();
+    m_internal->SetCancelFlag();
+
+    if ( m_isDetached )
+    {
+        // detached threads won't broadcast about their termination by default
+        // because usually nobody waits for them - but here we do, so ask the
+        // thread to notify us
+        m_internal->Notify();
+    }
 
     m_critsect.Leave();
 
 
     m_critsect.Leave();
 
@@ -715,14 +955,24 @@ wxThreadError wxThread::Delete(ExitCode *rc)
             break;
 
         case STATE_PAUSED:
             break;
 
         case STATE_PAUSED:
-            // resume the thread first
-            Resume();
+            // resume the thread first (don't call our Resume() because this
+            // would dead lock when it tries to enter m_critsect)
+            m_internal->Resume();
 
             // fall through
 
         default:
             // wait until the thread stops
 
             // fall through
 
         default:
             // wait until the thread stops
-            p_internal->Wait();
+            m_internal->Wait();
+
+            if ( rc )
+            {
+                wxASSERT_MSG( !m_isDetached,
+                              _T("no return code for detached threads") );
+
+                // if it's a joinable thread, it's not deleted yet
+                *rc = m_internal->GetExitCode();
+            }
     }
 
     return wxTHREAD_NO_ERROR;
     }
 
     return wxTHREAD_NO_ERROR;
@@ -730,23 +980,47 @@ wxThreadError wxThread::Delete(ExitCode *rc)
 
 wxThreadError wxThread::Kill()
 {
 
 wxThreadError wxThread::Kill()
 {
-    switch ( p_internal->GetState() )
+    wxCHECK_MSG( This() != this, wxTHREAD_MISC_ERROR,
+                 _T("a thread can't kill itself") );
+
+    switch ( m_internal->GetState() )
     {
         case STATE_NEW:
         case STATE_EXITED:
             return wxTHREAD_NOT_RUNNING;
 
     {
         case STATE_NEW:
         case STATE_EXITED:
             return wxTHREAD_NOT_RUNNING;
 
+        case STATE_PAUSED:
+            // resume the thread first
+            Resume();
+
+            // fall through
+
         default:
         default:
-#ifdef HAVE_PTHREAD_CANCEL 
-            if ( pthread_cancel(p_internal->GetId()) != 0 )
+#ifdef HAVE_PTHREAD_CANCEL
+            if ( pthread_cancel(m_internal->GetId()) != 0 )
 #endif
             {
                 wxLogError(_("Failed to terminate a thread."));
 
                 return wxTHREAD_MISC_ERROR;
             }
 #endif
             {
                 wxLogError(_("Failed to terminate a thread."));
 
                 return wxTHREAD_MISC_ERROR;
             }
-            //GL: As we must auto-destroy, the destruction must happen here (2).
-            delete this;
+
+            if ( m_isDetached )
+            {
+                // if we use cleanup function, this will be done from
+                // PthreadCleanup()
+#if !HAVE_THREAD_CLEANUP_FUNCTIONS
+                ScheduleThreadForDeletion();
+
+                OnExit();
+
+                DeleteThread(this);
+#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
+            }
+            else
+            {
+                m_internal->SetExitCode(EXITCODE_CANCELLED);
+            }
 
             return wxTHREAD_NO_ERROR;
     }
 
             return wxTHREAD_NO_ERROR;
     }
@@ -754,54 +1028,87 @@ wxThreadError wxThread::Kill()
 
 void wxThread::Exit(ExitCode status)
 {
 
 void wxThread::Exit(ExitCode status)
 {
-    // first call user-level clean up code
+    // from the moment we call OnExit(), the main program may terminate at any
+    // moment, so mark this thread as being already in process of being
+    // deleted or wxThreadModule::OnExit() will try to delete it again
+    ScheduleThreadForDeletion();
+
+    // don't enter m_critsect before calling OnExit() because the user code
+    // might deadlock if, for example, it signals a condition in OnExit() (a
+    // common case) while the main thread calls any of functions entering
+    // m_critsect on us (almost all of them do)
     OnExit();
 
     OnExit();
 
+    // now do enter it because SignalExit() will change our state
+    m_critsect.Enter();
+
     // next wake up the threads waiting for us (OTOH, this function won't return
     // until someone waited for us!)
     // next wake up the threads waiting for us (OTOH, this function won't return
     // until someone waited for us!)
-    p_internal->SignalExit();
+    m_internal->SignalExit();
 
 
-    p_internal->SetState(STATE_EXITED);
+    // leave the critical section before entering the dtor which tries to
+    // enter it
+    m_critsect.Leave();
 
 
-    // delete both C++ thread object and terminate the OS thread object
-    // GL: This is very ugly and buggy ...
-//    delete this;
+    // delete C++ thread object if this is a detached thread - user is
+    // responsible for doing this for joinable ones
+    if ( m_isDetached )
+    {
+        // FIXME I'm feeling bad about it - what if another thread function is
+        //       called (in another thread context) now? It will try to access
+        //       half destroyed object which will probably result in something
+        //       very bad - but we can't protect this by a crit section unless
+        //       we make it a global object, but this would mean that we can
+        //       only call one thread function at a time :-(
+        DeleteThread(this);
+    }
+
+    // terminate the thread (pthread_exit() never returns)
     pthread_exit(status);
     pthread_exit(status);
+
+    wxFAIL_MSG(_T("pthread_exit() failed"));
 }
 
 // also test whether we were paused
 bool wxThread::TestDestroy()
 {
 }
 
 // also test whether we were paused
 bool wxThread::TestDestroy()
 {
-    wxCriticalSectionLocker lock(m_critsect);
+    m_critsect.Enter();
 
 
-    if ( p_internal->GetState() == STATE_PAUSED )
+    if ( m_internal->GetState() == STATE_PAUSED )
     {
     {
-        // leave the crit section or the other threads will stop too if they try
-        // to call any of (seemingly harmless) IsXXX() functions while we sleep
+        // leave the crit section or the other threads will stop too if they
+        // try to call any of (seemingly harmless) IsXXX() functions while we
+        // sleep
         m_critsect.Leave();
 
         m_critsect.Leave();
 
-        p_internal->Pause();
-
-        // enter it back before it's finally left in lock object dtor
-        m_critsect.Enter();
+        m_internal->Pause();
+    }
+    else
+    {
+        // thread wasn't requested to pause, nothing to do
+        m_critsect.Leave();
     }
 
     }
 
-    return p_internal->WasCancelled();
+    return m_internal->WasCancelled();
 }
 
 wxThread::~wxThread()
 {
 }
 
 wxThread::~wxThread()
 {
+#ifdef __WXDEBUG__
     m_critsect.Enter();
     m_critsect.Enter();
-    if ( p_internal->GetState() != STATE_EXITED &&
-         p_internal->GetState() != STATE_NEW )
+
+    // check that the thread either exited or couldn't be created
+    if ( m_internal->GetState() != STATE_EXITED &&
+         m_internal->GetState() != STATE_NEW )
     {
     {
-        wxLogDebug(wxT("The thread is being destroyed although it is still "
-                     "running! The application may crash."));
+        wxLogDebug(_T("The thread is being destroyed although it is still "
+                      "running! The application may crash."));
     }
 
     m_critsect.Leave();
     }
 
     m_critsect.Leave();
+#endif // __WXDEBUG__
 
 
-    delete p_internal;
+    delete m_internal;
 
     // remove this thread from the global array
     gs_allThreads.Remove(this);
 
     // remove this thread from the global array
     gs_allThreads.Remove(this);
@@ -815,14 +1122,14 @@ bool wxThread::IsRunning() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
 
 {
     wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
 
-    return p_internal->GetState() == STATE_RUNNING;
+    return m_internal->GetState() == STATE_RUNNING;
 }
 
 bool wxThread::IsAlive() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
 
 }
 
 bool wxThread::IsAlive() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
 
-    switch ( p_internal->GetState() )
+    switch ( m_internal->GetState() )
     {
         case STATE_RUNNING:
         case STATE_PAUSED:
     {
         case STATE_RUNNING:
         case STATE_PAUSED:
@@ -837,7 +1144,7 @@ bool wxThread::IsPaused() const
 {
     wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
 
 {
     wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
 
-    return (p_internal->GetState() == STATE_PAUSED);
+    return (m_internal->GetState() == STATE_PAUSED);
 }
 
 //--------------------------------------------------------------------
 }
 
 //--------------------------------------------------------------------
@@ -867,11 +1174,13 @@ bool wxThreadModule::OnInit()
         return FALSE;
     }
 
         return FALSE;
     }
 
-    gs_mutexGui = new wxMutex();
-
     gs_tidMain = pthread_self();
 
     gs_tidMain = pthread_self();
 
+#if wxUSE_GUI
+    gs_mutexGui = new wxMutex();
+
     gs_mutexGui->Lock();
     gs_mutexGui->Lock();
+#endif // wxUSE_GUI
 
     return TRUE;
 }
 
     return TRUE;
 }
@@ -880,6 +1189,22 @@ void wxThreadModule::OnExit()
 {
     wxASSERT_MSG( wxThread::IsMain(), wxT("only main thread can be here") );
 
 {
     wxASSERT_MSG( wxThread::IsMain(), wxT("only main thread can be here") );
 
+    // are there any threads left which are being deleted right now?
+    size_t nThreadsBeingDeleted;
+    {
+        MutexLock lock(gs_mutexDeleteThread);
+        nThreadsBeingDeleted = gs_nThreadsBeingDeleted;
+    }
+
+    if ( nThreadsBeingDeleted > 0 )
+    {
+        wxLogTrace(TRACE_THREADS, _T("Waiting for %u threads to disappear"),
+                   nThreadsBeingDeleted);
+
+        // have to wait until all of them disappear
+        gs_condAllDeleted->Wait();
+    }
+
     // terminate any threads left
     size_t count = gs_allThreads.GetCount();
     if ( count != 0u )
     // terminate any threads left
     size_t count = gs_allThreads.GetCount();
     if ( count != 0u )
@@ -892,10 +1217,12 @@ void wxThreadModule::OnExit()
         gs_allThreads[0]->Delete();
     }
 
         gs_allThreads[0]->Delete();
     }
 
+#if wxUSE_GUI
     // destroy GUI mutex
     gs_mutexGui->Unlock();
 
     delete gs_mutexGui;
     // destroy GUI mutex
     gs_mutexGui->Unlock();
 
     delete gs_mutexGui;
+#endif // wxUSE_GUI
 
     // and free TLD slot
     (void)pthread_key_delete(gs_keySelf);
 
     // and free TLD slot
     (void)pthread_key_delete(gs_keySelf);
@@ -905,14 +1232,59 @@ void wxThreadModule::OnExit()
 // global functions
 // ----------------------------------------------------------------------------
 
 // global functions
 // ----------------------------------------------------------------------------
 
+static void ScheduleThreadForDeletion()
+{
+    MutexLock lock(gs_mutexDeleteThread);
+
+    if ( gs_nThreadsBeingDeleted == 0 )
+    {
+        gs_condAllDeleted = new wxCondition;
+    }
+
+    gs_nThreadsBeingDeleted++;
+
+    wxLogTrace(TRACE_THREADS, _T("%u threads waiting to be deleted"),
+               gs_nThreadsBeingDeleted);
+}
+
+static void DeleteThread(wxThread *This)
+{
+    // gs_mutexDeleteThread should be unlocked before signalling the condition
+    // or wxThreadModule::OnExit() would deadlock
+    {
+        MutexLock lock(gs_mutexDeleteThread);
+
+        wxLogTrace(TRACE_THREADS, _T("Thread %ld auto deletes."), This->GetId());
+
+        delete This;
+
+        wxCHECK_RET( gs_nThreadsBeingDeleted > 0,
+                     _T("no threads scheduled for deletion, yet we delete "
+                        "one?") );
+    }
+
+    if ( !--gs_nThreadsBeingDeleted )
+    {
+        // no more threads left, signal it
+        gs_condAllDeleted->Signal();
+
+        delete gs_condAllDeleted;
+        gs_condAllDeleted = (wxCondition *)NULL;
+    }
+}
+
 void wxMutexGuiEnter()
 {
 void wxMutexGuiEnter()
 {
-  gs_mutexGui->Lock();
+#if wxUSE_GUI
+    gs_mutexGui->Lock();
+#endif // wxUSE_GUI
 }
 
 void wxMutexGuiLeave()
 {
 }
 
 void wxMutexGuiLeave()
 {
-  gs_mutexGui->Unlock();
+#if wxUSE_GUI
+    gs_mutexGui->Unlock();
+#endif // wxUSE_GUI
 }
 
 #endif
 }
 
 #endif