The thread is joinable.
I had a look into wxMutex but tbh I found the example code a bit confusing, could this be a typo?
Code: Select all
s_mutexProtectingTheGlobalList->Lock();
Should it not be:
Code: Select all
s_mutexProtectingTheGlobalData->Lock();
https://docs.wxwidgets.org/3.0/classwx_mutex.html
Anyway, I couldn't get wxMutex to compile in my code, but wasn't entirely sure where to put it.
----------
As requested, I created a compilable sample that shows the problem.
There are various ways of causing the exception:
Method 1
1) Click 'Start Thread'.
2) When the counter is under way(eg reaches 3), then quickly double click 'Start Thread'.
Method 2
1) Click 'Start Thread'.
2) When the counter is under way(eg reaches 3), then quickly double click 'Stop Thread'.
Method 3
1) Click 'Start Thread'.
2) When the counter is under way(eg reaches 3), then rapidly click 'Start Thread' like its 1982 and youre playing space invaders.
Method 4
1) Click 'Start Thread'.
2) When the counter is under way(eg reaches 3), then rapidly click 'Stop Thread' like its 1982 and youre playing space invaders.
----
What doesn't cause the issue?
1) Rapidly clicking 'Stop Thread' when no thread is running
Code: Select all
#include <wx/wx.h>
#include <wx/arrstr.h>
#include <wx/thread.h>
#include <wx/listctrl.h>
#include <string>
#include <vector>
wxDECLARE_EVENT(wxEVT_INC_COUNT, wxThreadEvent);
wxDECLARE_EVENT(wxEVT_RESET_COUNT, wxThreadEvent);
wxDEFINE_EVENT(wxEVT_INC_COUNT, wxThreadEvent);
wxDEFINE_EVENT(wxEVT_RESET_COUNT, wxThreadEvent);
class MyCounter {
public:
MyCounter() :
m_data(),
count(0)
{
}
const std::string& at(const long& pos_) const
{
//---------------- | GET LOG DATA AT POS | ----------------
if (!this->m_data.empty())
{
return this->m_data.at(pos_);
}
}
void inc()
{
m_data.push_back(std::to_string(count++));
}
void reset()
{
this->m_data.clear();
this->m_data.shrink_to_fit();
count = 0;
}
const size_t size() const
{
//---------------- | GET SIZE OF DIAGNOSTIC LOG | ----------------
return this->m_data.size();
}
private:
int count;
std::vector<std::string> m_data;
};
class ListCtrl_CountView : public wxListCtrl {
public:
ListCtrl_CountView::ListCtrl_CountView(
wxWindow* parent_,
const wxWindowID& id_,
const wxPoint& pos_,
const wxSize& size_,
long style_,
MyCounter* mc_) :
wxListCtrl(parent_, id_, pos_, size_, style_),
m_counter(mc_)
{
//---------------- | Add first column | ----------------
wxListItem col0;
col0.SetId(0);
col0.SetText(_("Count..."));
col0.SetWidth(575);
InsertColumn(0, col0);
}
const int GetSelection() const
{
//---------------- | RETURN INDEX OF SELECTED ITEM ELSE IF NO SELECTION RETURN: wxNOT_FOUND | ----------------
long itemIndex = -1;
while ((itemIndex = this->GetNextItem(itemIndex, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != wxNOT_FOUND)
{
if (itemIndex >= 0) { return itemIndex; }
}
return itemIndex;
}
wxString OnGetItemText(long row_, long col_) const
{
//---------------- | Overload virtual method of wxListCtrl to provide text data for virtual list | ----------------
//---------------- | Get: item data | ----------------
//---------------- | Use item_ and col_ to return the correct data for that particular cell | ----------------
if (col_ == 0 && m_counter->size() > 0)
{
wxString str = m_counter->at(row_);
return str;
}
return "";
}
private:
MyCounter* m_counter;
};
class ThreadTimedDataRefresh : public wxThread
{
public:
ThreadTimedDataRefresh(wxEvtHandler* sink_, int interval_) :
wxThread(wxTHREAD_JOINABLE),
m_sink(sink_),
m_refresh_interval(interval_)
{
}
protected:
wxEvtHandler* m_sink;
int m_refresh_interval;
ExitCode Entry() override
{
while (!TestDestroy())
{
// interval between each count
this->Sleep(this->m_refresh_interval);
// increment count
wxThreadEvent evt(wxEVT_INC_COUNT);
wxQueueEvent(m_sink, evt.Clone());
}
wxQueueEvent(this->m_sink, new wxThreadEvent(wxEVT_RESET_COUNT));
return static_cast<wxThread::ExitCode>(nullptr);
return static_cast<wxThread::ExitCode>(nullptr);
}
};
class MAIN_FRAME : public wxFrame
{
public:
MAIN_FRAME() :
wxFrame(NULL, wxID_ANY, "Threaded Counter", wxDefaultPosition, wxSize(800, 600)),
m_count_view(),
m_count_thread(),
m_counter()
{
m_counter = new MyCounter();
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
wxButton* button = new wxButton(mainPanel, wxID_ANY, "Start Thread");
mainSizer->Add(button, wxSizerFlags().Expand().Border());
button->Bind(wxEVT_BUTTON, &MAIN_FRAME::OnCountStart, this);
button = new wxButton(mainPanel, wxID_ANY, "Stop Thread");
mainSizer->Add(button, wxSizerFlags().Expand().Border());
button->Bind(wxEVT_BUTTON, &MAIN_FRAME::OnCountStop, this);
m_count_view = new ListCtrl_CountView(mainPanel, wxID_ANY, wxDefaultPosition, wxSize(600, 500), wxLC_REPORT | wxLC_VIRTUAL | wxLC_NO_SORT_HEADER | wxLC_SINGLE_SEL | wxFULL_REPAINT_ON_RESIZE, m_counter);
m_count_view->SetFont(wxFont(7, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, wxT("Tahoma")));
mainSizer->Add(m_count_view, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
mainPanel->SetSizer(mainSizer);
Bind(wxEVT_INC_COUNT, &MAIN_FRAME::OnIncCount, this);
Bind(wxEVT_RESET_COUNT, &MAIN_FRAME::OnResetCount, this);
}
~MAIN_FRAME()
{
StopThreadedCount();
}
private:
ListCtrl_CountView* m_count_view;
wxThread* m_count_thread;
MyCounter* m_counter;
// button start count clicked
void MAIN_FRAME::OnCountStart(wxCommandEvent&)
{
StartThreadedCount();
}
// button stop count clicked
void MAIN_FRAME::OnCountStop(wxCommandEvent&)
{
StopThreadedCount();
}
bool MAIN_FRAME::StartThreadedCount()
{
int refresh_interval = 1000;
StopThreadedCount();
m_count_thread = new ThreadTimedDataRefresh(this, refresh_interval);
if (m_count_thread->Run() != wxTHREAD_NO_ERROR)
{
delete m_count_thread;
m_count_thread = nullptr;
wxLogError(_("Could not create the thread needed to load the data."));
return false;
}
return true;
}
void MAIN_FRAME::StopThreadedCount()
{
if (m_count_thread)
{
m_count_thread->Delete();
delete m_count_thread;
m_count_thread = nullptr;
}
}
// handler for wxEVT_INC_COUNT
void MAIN_FRAME::OnIncCount(wxThreadEvent& evt)
{
m_counter->inc();
this->m_count_view->SetItemCount(m_counter->size());
this->m_count_view->Refresh();
}
// handler for wxEVT_RESET_COUNT
void MAIN_FRAME::OnResetCount(wxThreadEvent&)
{
m_counter->reset();
}
};
class MyApp : public wxApp
{
public:
bool OnInit()
{
(new MAIN_FRAME())->Show();
return true;
}
}; wxIMPLEMENT_APP(MyApp);
wxWidgets(v3.2.2.1) - Vs2022(v143) - Win10(x64) - DialogBlocks(v5.16.5_Unicode)