Multithreaded GUI thread2 sample

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
Andreas Micheler
In need of some credit
In need of some credit
Posts: 4
Joined: Tue May 30, 2006 12:10 am
Contact:

Multithreaded GUI thread2 sample

Post by Andreas Micheler » Tue May 30, 2006 1:02 am

Hi all,

This is a try to use a multithreaded GUI system with wxWidgets.
It's derived from the thread sample and is probably not really finished.
But it shows how to carefully use multithreaded GUIs.
ShowModal is not really thread safe,
as far as I can see by experiment.
So it's better to not use it in multithreaded GUIs.
This is not really a tutorial, since I have not much commented it.
But it could get you started with multithreaded GUIs.
What you can see is the thread sample,
which can create multiple thread sample GUIs,
and each of them can of course create more children.
Each child writes to the TextCtrl of its parent some status info
every 100 ms about (probably longer, because it's not so very fast).

Ok, here's the code:

Code: Select all

/////////////////////////////////////////////////////////////////////////////
// Name:        thread2.cpp
// Purpose:     wxWidgets thread sample 2
// Author:      Guilhem Lavaux, Vadim Zeitlin, Andreas Micheler
// Modified by:
// Created:     06/16/98
// RCS-ID:      $Id: thread2.cpp,v 1.25 2006/05/28  4:01:00 JS Exp $
// Copyright:   (c) 1998-2002 wxWidgets team
// Licence:     wxWindows license
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#if !wxUSE_THREADS
    #error "This sample requires thread support!"
#endif // wxUSE_THREADS

#include <crtdbg.h>

#include "wx/thread.h"
#include "wx/dynarray.h"
#include "wx/numdlg.h"
#include "wx/evtloop.h"

#include "wx/progdlg.h"

#include "../sample.xpm"

// define this to use wxExecute in the exec tests, otherwise just use system
#define USE_EXECUTE

#ifdef USE_EXECUTE
    #define EXEC(cmd) wxExecute((cmd), wxEXEC_SYNC)
#else
    #define EXEC(cmd) system(cmd)
#endif

#ifdef _DEBUG
    #undef THIS_FILE
    static char THIS_FILE[]= __FILE__;
    #define new new(_NORMAL_BLOCK, THIS_FILE, __LINE__)
#endif


class MyApp;
class MyThread;
class MyFrame;

WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread);

//______________________________ MyApp ______________________________

// Define a new application type
class MyApp : public wxApp
{
public:
    MyApp();
    virtual ~MyApp(){};
    virtual bool OnInit();
    virtual int MainLoop();
};

//____________________________ MyThread _____________________________

class MyThread : public wxThread
{
public:
    MyThread(MyThread *parentThread);

    void GuiEnter();
    void GuiLeave();
    void GuiLeaveOrEnter();
    void WakeUpThread();

    // thread execution starts here
    virtual void *Entry();

    // called when the thread exits - whether it terminates normally or is
    // stopped with Delete() (but not when it is Kill()ed!)
    virtual void OnExit();

    // write something to the text control
    void WriteText(const wxString& text);

    static MyThread *This()
    {    if (IsMain())
            return main;
        MyThread *th=(MyThread*)wxThread::This();
        return th;
    }

public:
    bool running, guiOwnedByMainThread;
    int waitingForGui, y;
    size_t   count;
    MyFrame *m_frame;
    MyThread *m_parent;
    wxCriticalSection gui, csWaitingForGui;
    wxArrayThread m_threads;
    wxMutex m_critsect;
    wxSemaphore m_semAllDone;
    bool m_waitingUntilAllDone;

    static MyThread *main;
};

MyThread *MyThread::main=0;

//_____________________________ MyFrame _____________________________

// Define a new frame type
class MyFrame: public wxFrame
{
public:
    MyThread *m_thread;
    wxWindow *canvas;

    // ctor
    MyFrame(MyThread *thread, wxFrame *frame, const wxString& title, int x, int y, int w, int h);
    virtual ~MyFrame();

    // operations
    void WriteText(const wxString& text) { m_txtctrl->WriteText(text); }

    // accessors for MyWorkerThread (called in its context!)
    bool Cancelled();

protected:
    // callbacks
    void OnClose(wxCloseEvent &event);
    void OnQuit(wxCommandEvent& event);
    void OnClear(wxCommandEvent& event);

    void OnStartThread(wxCommandEvent& event);
    void OnStartThreads(wxCommandEvent& event);
    void OnStopThread(wxCommandEvent& event);
    void OnPauseThread(wxCommandEvent& event);
    void OnResumeThread(wxCommandEvent& event);

    void OnStartWorker(wxCommandEvent& event);
    void OnWorkerEvent(wxCommandEvent& event);
    void OnUpdateWorker(wxUpdateUIEvent& event);

    void OnExecMain(wxCommandEvent& event);
    void OnExecThread(wxCommandEvent& event);

    void OnShowCPUs(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);

    void OnIdle(wxIdleEvent &event);

private:
    // helper function - creates a new thread (but doesn't run it)
    MyThread *CreateThread();

    // just some place to put our messages in
    wxTextCtrl *m_txtctrl;

    // remember the number of running threads and total number of threads
    size_t m_nRunning, m_nCount;

    // the progress dialog which we show while worker thread is running
    wxProgressDialog *m_dlgProgress;

    // was the worker thread cancelled by user?
    bool m_cancelled;

    // protects m_cancelled
    wxCriticalSection m_critsectWork;

    DECLARE_EVENT_TABLE()
};

// ID for the menu commands
enum
{
    THREAD_QUIT  = wxID_EXIT,
    THREAD_ABOUT = wxID_ABOUT,
    THREAD_TEXT          = 101,
    THREAD_CLEAR,
    THREAD_START_THREAD  = 201,
    THREAD_START_THREADS,
    THREAD_STOP_THREAD,
    THREAD_PAUSE_THREAD,
    THREAD_RESUME_THREAD,
    THREAD_START_WORKER,

    THREAD_EXEC_MAIN,
    THREAD_EXEC_THREAD,

    THREAD_SHOWCPUS,

    WORKER_EVENT    // this one gets sent from the worker thread
};

//______________________________ MyApp ______________________________

// Create a new application object
IMPLEMENT_APP(MyApp)

MyApp *theApp;

MyApp::MyApp()
{
    MyThread::main=new MyThread(0);
}

// `Main program' equivalent, creating windows and returning main app frame
bool MyApp::OnInit()
{
    theApp=this;
    // uncomment this to get some debugging messages from the trace code
    // on the console (or just set WXTRACE env variable to include "thread")
    //wxLog::AddTraceMask("thread");

    // Create the main frame window
    MyFrame *frame = new MyFrame(MyThread::main,
                                          (wxFrame *)NULL,
                                          _T("wxWidgets threads sample 2"),
                                          0, 0, 200, 200);
    MyThread::main->m_frame=frame;
    // Show the frame
    frame->Show(true);

    SetTopWindow(frame);

    return true;
}

int MyApp::MainLoop()
{
    MyThread *th=MyThread::This();

    wxEventLoop *eventLoop = new wxEventLoop,
        *oldEventLoop=wxEventLoop::GetActive();
    wxEventLoop::SetActive(eventLoop);

    th->running=true;
    while (th->running)
    {    th->GuiLeaveOrEnter();
        while (eventLoop->Pending())
            eventLoop->Dispatch();
        while (theApp->ProcessIdle())
            ;
    }
    for (int i=MyThread::main->m_threads.GetCount()-1; i >= 0; i--)
    {    MyThread *child=(MyThread*)MyThread::main->m_threads.Item(i);
        child->running=false;
    }
    while (!MyThread::main->m_threads.IsEmpty())
    {    while (theApp->Pending())
            theApp->Dispatch();
    }
    if (MyThread::main->m_frame)
        delete MyThread::main->m_frame;//->Destroy();

    while (theApp->Pending())
        theApp->Dispatch();
    wxEventLoop::SetActive(oldEventLoop);
    delete eventLoop;
    delete MyThread::main;
    return 0;
}

//____________________________ MyThread _____________________________

MyThread::MyThread(MyThread *parent):
    guiOwnedByMainThread(false),
    waitingForGui(0),
    wxThread(),
    m_semAllDone(),
    m_critsect(),
    gui(),
    y(0)
{
    m_waitingUntilAllDone = false;
    count = 0;
    m_parent=parent;
}

void MyThread::GuiEnter()
{
    // this would dead lock everything...
    wxASSERT_MSG( This()!=this,
            wxT("This() thread itself doesn't want to block in GuiEnter()!") );

    // the order in which we enter the critical sections here is crucial!!
    // set the flag telling to the main thread that we want to do some GUI
    {    wxCriticalSectionLocker enter(csWaitingForGui);
        waitingForGui++;
    }
    WakeUpThread();

    // now we may block here because the main thread will soon let us in
    // (during the next iteration of OnIdle())
    gui.Enter();
}

void MyThread::GuiLeave()
{
    wxCriticalSectionLocker enter(csWaitingForGui);

    if ( This()==this )
    {    guiOwnedByMainThread = false;
    }else
    {    // decrement the number of threads waiting for GUI access now
        wxASSERT_MSG( waitingForGui > 0,
             wxT("calling GuiLeave() without entering it first?") );
        waitingForGui--;
        WakeUpThread();
    }
    gui.Leave();
}

void MyThread::GuiLeaveOrEnter()
{
    wxASSERT_MSG( This()==this,
        wxT("only This() thread itself may call GuiLeaveOrEnter()!") );

    wxCriticalSectionLocker enter(csWaitingForGui);

    if ( waitingForGui == 0 )
    {    // no threads are waiting for GUI - so we may acquire the lock without
        // any danger (but only if we don't already have it)
        if (!guiOwnedByMainThread)
        {    gui.Enter();
            guiOwnedByMainThread = true;
        }
        //else: already have it, nothing to do
    }
    else
    {    // some threads are waiting, release the GUI lock if we have it
        if (guiOwnedByMainThread)
        {    GuiLeave();
        }
        //else: some other worker thread is doing GUI
    }
    return;
}

// wake up the main thread if it's in ::GetMessage()
void MyThread::WakeUpThread()
{
    // sending any message would do - hopefully WM_NULL is harmless enough
//    if (!::PostThreadMessage(GetId(), WM_NULL, 0, 0))
    {    // should never happen
//        wxLogLastError(wxT("PostThreadMessage(WM_NULL)"));
    }
}

void MyThread::OnExit()
{
    wxMutexLocker locker(m_parent->m_critsect);
    m_parent->m_threads.Remove(this);
}

void *MyThread::Entry()
{
    running=true;
    
    m_frame = new MyFrame(this, (wxFrame *)NULL, _T("wxWidgets threads sample"),
            m_parent->m_frame->GetPosition().x+200, m_parent->y, 200, 200);
    m_parent->y=(m_parent->y+200) % 1000;
    m_frame->Show();

    srand(time(0));

    wxString text;

    text.Printf(wxT("Thread 0x%lx started (priority = %u).\n"),
     GetId(), GetPriority());
    WriteText(text);
    // wxLogMessage(text); -- test wxLog thread safeness

    for ( count = 0; count < 1000; count++ )
    {
        // check if we were asked to exit
        if (TestDestroy())
            break;

        text.Printf(wxT("[%u] Thread 0x%lx here.\n"), count, GetId());
        WriteText(text);

        // wxSleep() can't be called from non-GUI thread!
        Sleep(100);

        while (theApp->Pending())
            theApp->Dispatch();
        while (theApp->ProcessIdle())
            ;
        if (!running)
            break;
    }

    text.Printf(wxT("Thread 0x%lx finished.\n"), GetId());
    WriteText(text);
    // wxLogMessage(text); -- test wxLog thread safeness

    for (int i=m_threads.GetCount()-1; i >= 0; i--)
    {    MyThread *child=(MyThread*)m_threads.Item(i);
        child->running=false;
    }
    while (!m_threads.IsEmpty())
    {    while (theApp->Pending())
            theApp->Dispatch();
    }
    if (m_frame)
        delete m_frame;//->Destroy();

    while (theApp->Pending())
        theApp->Dispatch();
    return NULL;
}

int random(int max)
{
    return rand() % max;
}

void MyThread::WriteText(const wxString& text)
{
    // before doing any GUI calls we must ensure that this thread is the only
    // one doing it!
    m_parent->GuiEnter();

    if (m_parent->m_frame)
        m_parent->m_frame->WriteText(text);

    m_parent->GuiLeave();
}

//_________________________ MyWorkerThread __________________________

class MyWorkerThread : public wxThread
{
public:
    MyWorkerThread(MyFrame *frame);

    // thread execution starts here
    virtual void *Entry();

    // called when the thread exits - whether it terminates normally or is
    // stopped with Delete() (but not when it is Kill()ed!)
    virtual void OnExit();

public:
    MyFrame *m_frame;
    size_t   count;
};

MyWorkerThread::MyWorkerThread(MyFrame *frame)
        : wxThread()
{
    m_frame = frame;
    count = 0;
}

void MyWorkerThread::OnExit()
{
}

void *MyWorkerThread::Entry()
{
    for ( count = 0; !m_frame->Cancelled() && (count < 100); count++ )
    {
        // check if we were asked to exit
        if ( TestDestroy() )
            break;

        // create any type of command event here
        wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
        event.SetInt( count );

        // send in a thread-safe way
        wxPostEvent( m_frame, event );

        // wxSleep() can't be called from non-main thread!
        wxThread::Sleep(200);
    }

    wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, WORKER_EVENT );
    event.SetInt(-1); // that's all
    wxPostEvent( m_frame, event );

    return NULL;
}

//__________________________ MyExecThread ___________________________

class MyExecThread : public wxThread
{
public:
    MyExecThread(const wxChar *command) : wxThread(wxTHREAD_JOINABLE),
                                          m_command(command)
    {
        Create();
    }

    virtual ExitCode Entry()
    {
        return (ExitCode)EXEC(m_command);
    }

private:
    wxString m_command;
};

//_____________________________ MyFrame _____________________________

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MENU(THREAD_QUIT, MyFrame::OnQuit)
    EVT_MENU(THREAD_CLEAR, MyFrame::OnClear)
    EVT_MENU(THREAD_START_THREAD, MyFrame::OnStartThread)
    EVT_MENU(THREAD_START_THREADS, MyFrame::OnStartThreads)
    EVT_MENU(THREAD_STOP_THREAD, MyFrame::OnStopThread)
    EVT_MENU(THREAD_PAUSE_THREAD, MyFrame::OnPauseThread)
    EVT_MENU(THREAD_RESUME_THREAD, MyFrame::OnResumeThread)

    EVT_MENU(THREAD_EXEC_MAIN, MyFrame::OnExecMain)
    EVT_MENU(THREAD_EXEC_THREAD, MyFrame::OnExecThread)

    EVT_MENU(THREAD_SHOWCPUS, MyFrame::OnShowCPUs)
    EVT_MENU(THREAD_ABOUT, MyFrame::OnAbout)

    EVT_UPDATE_UI(THREAD_START_WORKER, MyFrame::OnUpdateWorker)
    EVT_MENU(THREAD_START_WORKER, MyFrame::OnStartWorker)
    EVT_MENU(WORKER_EVENT, MyFrame::OnWorkerEvent)

    EVT_CLOSE(MyFrame::OnClose)
    EVT_IDLE(MyFrame::OnIdle)
END_EVENT_TABLE()

// My frame constructor
MyFrame::MyFrame(MyThread *thread, wxFrame *frame, const wxString& title,
                 int x, int y, int w, int h):
    m_thread(thread),
    wxFrame(frame, wxID_ANY, title, wxPoint(x, y), wxSize(w, h))

{
    SetIcon(wxIcon(sample_xpm));

    m_nRunning = m_nCount = 0;

    m_dlgProgress = (wxProgressDialog *)NULL;

#if wxUSE_STATUSBAR
    CreateStatusBar(2);
#endif // wxUSE_STATUSBAR

    m_txtctrl = new wxTextCtrl(this, wxID_ANY, _T(""), wxPoint(0, 0), wxSize(400, 300),
                               wxTE_MULTILINE | wxTE_READONLY);

//     canvas=new wxWindow(this, wxID_ANY, wxPoint(0, 300), wxSize(400, 300));

    // Make a menubar
    wxMenuBar *menuBar = new wxMenuBar;

    wxMenu *menuFile = new wxMenu;
    menuFile->Append(THREAD_CLEAR, _T("&Clear log\tCtrl-L"));
    menuFile->AppendSeparator();
    menuFile->Append(THREAD_QUIT, _T("E&xit\tAlt-X"));
    menuBar->Append(menuFile, _T("&File"));

    wxMenu *menuThread = new wxMenu;
    menuThread->Append(THREAD_START_THREAD, _T("&Start a new thread\tCtrl-N"));
    menuThread->Append(THREAD_START_THREADS, _T("Start &many threads at once"));
    menuThread->Append(THREAD_STOP_THREAD, _T("S&top a running thread\tCtrl-S"));
    menuThread->AppendSeparator();
    menuThread->Append(THREAD_PAUSE_THREAD, _T("&Pause a running thread\tCtrl-P"));
    menuThread->Append(THREAD_RESUME_THREAD, _T("&Resume suspended thread\tCtrl-R"));
    menuThread->AppendSeparator();
    menuThread->Append(THREAD_START_WORKER, _T("Start &worker thread\tCtrl-W"));
    menuBar->Append(menuThread, _T("&Thread"));

    wxMenu *menuExec = new wxMenu;
    menuExec->Append(THREAD_EXEC_MAIN, _T("&Launch a program from main thread\tF5"));
    menuExec->Append(THREAD_EXEC_THREAD, _T("L&aunch a program from a thread\tCtrl-F5"));
    menuBar->Append(menuExec, _T("&Execute"));

    wxMenu *menuHelp = new wxMenu;
    menuHelp->Append(THREAD_SHOWCPUS, _T("&Show CPU count"));
    menuHelp->AppendSeparator();
    menuHelp->Append(THREAD_ABOUT, _T("&About..."));
    menuBar->Append(menuHelp, _T("&Help"));

    SetMenuBar(menuBar);
}

MyFrame::~MyFrame()
{
/*    // NB: although the OS will terminate all the threads anyhow when the main
    //     one exits, it's good practice to do it ourselves -- even if it's not
    //     completely trivial in this example

    // tell all the threads to terminate: note that they can't terminate while
    // we're deleting them because they will block in their OnExit() -- this is
    // important as otherwise we might access invalid array elements
    wxThread *thread;

    MyThread *th=MyThread::This();
    th->m_critsect.Lock();

    // check if we have any threads running first
    const wxArrayThread& threads = th->m_threads;
    size_t count = threads.GetCount();

    if ( count )
    {
      // set the flag for MyThread::OnExit()
      th->m_waitingUntilAllDone = true;

      // stop all threads
      while ( ! threads.IsEmpty() )
      {
            thread = threads.Last();
            th->m_critsect.Unlock();
            thread->Delete();
            th->m_critsect.Lock();
      }
    }
    th->m_critsect.Unlock();
    if ( count )
    {    // now wait for them to really terminate
        th->m_semAllDone.Wait();
    }
    //else: no threads to terminate, no condition to wait for
*/
}

void MyFrame::OnClose(wxCloseEvent &event)
{
    m_thread->running=false;
}

MyThread *MyFrame::CreateThread()
{
    MyThread *thread = new MyThread(MyThread::This());

    if ( thread->Create() != wxTHREAD_NO_ERROR )
    {
        wxLogError(wxT("Can't create thread!"));
    }

    wxMutexLocker enter(MyThread::This()->m_critsect);
    MyThread::This()->m_threads.Add(thread);

    return thread;
}

void MyFrame::OnStartThreads(wxCommandEvent& WXUNUSED(event) )
{
    static long s_num = 10;

    s_num = wxGetNumberFromUser(_T("How many threads to start: "), _T(""),
                                _T("wxThread sample"), s_num, 1, 100, this);
    if ( s_num == -1 )
    {
        s_num = 10;

        return;
    }

    size_t count = (size_t)s_num, n;

    wxArrayThread threads;

    // first create them all...
    for ( n = 0; n < count; n++ )
    {
        wxThread *thr = CreateThread();

        // we want to show the effect of SetPriority(): the first thread will
        // have the lowest priority, the second - the highest, all the rest
        // the normal one
        if ( n == 0 )
            thr->SetPriority(WXTHREAD_MIN_PRIORITY);
        else if ( n == 1 )
            thr->SetPriority(WXTHREAD_MAX_PRIORITY);
        else
            thr->SetPriority(WXTHREAD_DEFAULT_PRIORITY);

        threads.Add(thr);
    }

#if wxUSE_STATUSBAR
    wxString msg;
    msg.Printf(wxT("%d new threads created."), count);
    SetStatusText(msg, 1);
#endif // wxUSE_STATUSBAR

    // ...and then start them
    for ( n = 0; n < count; n++ )
    {
        threads[n]->Run();
    }
}

void MyFrame::OnStartThread(wxCommandEvent& WXUNUSED(event) )
{
    MyThread *thread = CreateThread();

    if ( thread->Run() != wxTHREAD_NO_ERROR )
    {
        wxLogError(wxT("Can't start thread!"));
    }

#if wxUSE_STATUSBAR
    SetStatusText(_T("New thread started."), 1);
#endif // wxUSE_STATUSBAR
}

void MyFrame::OnStopThread(wxCommandEvent& WXUNUSED(event) )
{
     m_thread->m_critsect.Lock();

    // stop the last thread
    if ( m_thread->m_threads.IsEmpty() )
    {
        wxLogError(wxT("No thread to stop!"));

        m_thread->m_critsect.Unlock();
    }
    else
    {
        wxThread *thread = m_thread->m_threads.Last();

        // it's important to leave critical section before calling Delete()
        // because delete will (implicitly) call OnExit() which also tries
        // to enter the same crit section - would dead lock.
        m_thread->m_critsect.Unlock();

        thread->Delete();

#if wxUSE_STATUSBAR
        SetStatusText(_T("Thread stopped."), 1);
#endif // wxUSE_STATUSBAR
    }
}

void MyFrame::OnResumeThread(wxCommandEvent& WXUNUSED(event) )
{
     MyThread *th=MyThread::This();
    wxMutexLocker enter(th->m_critsect);

    // resume first suspended thread
    size_t n = 0, count = th->m_threads.Count();
    while ( n < count && !th->m_threads[n]->IsPaused() )
        n++;

    if ( n == count )
    {
        wxLogError(wxT("No thread to resume!"));
    }
    else
    {
        th->m_threads[n]->Resume();

#if wxUSE_STATUSBAR
        SetStatusText(_T("Thread resumed."), 1);
#endif // wxUSE_STATUSBAR
    }
}

void MyFrame::OnPauseThread(wxCommandEvent& WXUNUSED(event) )
{
     MyThread *th=MyThread::This();
    wxMutexLocker enter(th->m_critsect);

    // pause last running thread
    int n = th->m_threads.Count() - 1;
    while ( n >= 0 && !th->m_threads[n]->IsRunning() )
        n--;

    if ( n < 0 )
    {
        wxLogError(wxT("No thread to pause!"));
    }
    else
    {
        th->m_threads[n]->Pause();

#if wxUSE_STATUSBAR
        SetStatusText(_T("Thread paused."), 1);
#endif // wxUSE_STATUSBAR
    }
}

// set the frame title indicating the current number of threads
void MyFrame::OnIdle(wxIdleEvent& event)
{
    if (MyThread::This()!=m_thread)
        return;

    wxMutexLocker enter(m_thread->m_critsect);

    // update the counts of running/total threads
    size_t nRunning = 0,
    nCount = m_thread->m_threads.Count();
    for ( size_t n = 0; n < nCount; n++ )
    {    if ( m_thread->m_threads[n]->IsRunning() )
            nRunning++;
    }

    if ( nCount != m_nCount || nRunning != m_nRunning )
    {    m_nRunning = nRunning;
        m_nCount = nCount;
        wxLogStatus(this, wxT("%u threads total, %u running."), nCount, nRunning);
    }
    //else: avoid flicker - don't print anything
    event.Skip();
}

void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
{
    m_thread->running=false;
}

void MyFrame::OnExecMain(wxCommandEvent& WXUNUSED(event))
{
    wxLogMessage(wxT("The exit code from the main program is %ld"),
                 EXEC(_T("/bin/echo \"main program\"")));
}

void MyFrame::OnExecThread(wxCommandEvent& WXUNUSED(event))
{
    MyExecThread thread(wxT("/bin/echo \"child thread\""));
    thread.Run();

    wxLogMessage(wxT("The exit code from a child thread is %ld"),
                 (long)thread.Wait());
}

void MyFrame::OnShowCPUs(wxCommandEvent& WXUNUSED(event))
{
    wxString msg;

    int nCPUs = wxThread::GetCPUCount();
    switch ( nCPUs )
    {
        case -1:
            msg = _T("Unknown number of CPUs");
            break;

        case 0:
            msg = _T("WARNING: you're running without any CPUs!");
            break;

        case 1:
            msg = _T("This system only has one CPU.");
            break;

        default:
            msg.Printf(wxT("This system has %d CPUs"), nCPUs);
    }

    wxLogMessage(msg);
}

void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
{
    wxMessageDialog dialog(this,
                           _T("wxWidgets multithreaded application sample\n")
                           _T("(c) 1998 Julian Smart, Guilhem Lavaux\n")
                           _T("(c) 1999 Vadim Zeitlin\n")
                           _T("(c) 2000 Robert Roebling"),
                           _T("About wxThread sample"),
                           wxOK | wxICON_INFORMATION);

    dialog.ShowModal();
}

void MyFrame::OnClear(wxCommandEvent& WXUNUSED(event))
{
    m_txtctrl->Clear();
}

void MyFrame::OnUpdateWorker(wxUpdateUIEvent& event)
{
    event.Enable( m_dlgProgress == NULL );
}

void MyFrame::OnStartWorker(wxCommandEvent& WXUNUSED(event))
{
    MyWorkerThread *thread = new MyWorkerThread(this);

    if ( thread->Create() != wxTHREAD_NO_ERROR )
    {
        wxLogError(wxT("Can't create thread!"));
        return;
    }

    m_dlgProgress = new wxProgressDialog
                        (
                         _T("Progress dialog"),
                         _T("Wait until the thread terminates or press [Cancel]"),
                         100,
                         this,
                         wxPD_CAN_ABORT |
                         wxPD_APP_MODAL |
                         wxPD_ELAPSED_TIME |
                         wxPD_ESTIMATED_TIME |
                         wxPD_REMAINING_TIME
                        );

    // thread is not running yet, no need for crit sect
    m_cancelled = false;

    thread->Run();
}

void MyFrame::OnWorkerEvent(wxCommandEvent& event)
{
#if 0
    WriteText( _T("Got message from worker thread: ") );
    WriteText( event.GetString() );
    WriteText( _T("\n") );
#else
    int n = event.GetInt();
    if ( n == -1 )
    {
        m_dlgProgress->Destroy();
        m_dlgProgress = (wxProgressDialog *)NULL;

        // the dialog is aborted because the event came from another thread, so
        // we may need to wake up the main event loop for the dialog to be
        // really closed
        wxWakeUpIdle();
    }
    else
    {
        if ( !m_dlgProgress->Update(n) )
        {
            wxCriticalSectionLocker lock(m_critsectWork);

            m_cancelled = true;
        }
    }
#endif
}

bool MyFrame::Cancelled()
{
    wxCriticalSectionLocker lock(m_critsectWork);

    return m_cancelled;
}

///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
Now follow two diffs of: appcmn.cpp and evtloop.cpp (wxMSW-2.6.1).

common/appcmn.cpp:
399a400,402
> static wxMutex processIdleMutex(wxMUTEX_RECURSIVE);
> wxMutexLocker locker(processIdleMutex);
>

msw/evtloop.cpp:
104a105,107
> static wxMutex processMutex(wxMUTEX_RECURSIVE);
> wxMutexLocker locker(processMutex);
>
314a318,323
> // static wxCriticalSection dispatchCS;
> // wxCriticalSectionLocker locker(dispatchCS);
>
> // static wxMutex dispatchMutex(wxMUTEX_RECURSIVE);
> // wxMutexLocker locker(dispatchMutex);
>
336,338c345,347
< wxASSERT_MSG( wxThread::IsMain(),
< wxT("only the main thread can process Windows messages") );
<
---
> // wxASSERT_MSG( wxThread::IsMain(),
> // wxT("only the main thread can process Windows messages") );
> /*
382a392
> */

If you want the complete files I can mail them directly or place them on my web page, if there's some demand.

Cheers,
Andreas

xin.songtao
Experienced Solver
Experienced Solver
Posts: 86
Joined: Wed Apr 18, 2007 6:10 am
Location: Shanghai China

Post by xin.songtao » Fri Jul 03, 2009 10:14 am

(mark it) && (support you)!
:D

Taamalus
Experienced Solver
Experienced Solver
Posts: 86
Joined: Wed Dec 09, 2009 3:31 pm
Location: Toronto

I know I'm late ...

Post by Taamalus » Sun Jul 25, 2010 12:12 am

I think you may saved me a year or so figguring out where to make threads work in wxWidgets. Many thanks for posting this.

And if you are still around, yes, I want to know everything you found out about this subject.

Cheers
Henry
... time waits for no one
Hank

Post Reply