I'm using an example from WxWidget wiki to understand how to do the Inter-Thread and Inter-Process communication. I don't know why the example works but my code, very similar, doesn't at line "m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)"
When I run the code an exeption is showed:
First-chance exception at 0x00B2DB61 in TeamTranslate.exe: 0xC0000005: Access violation reading location 0x00000078.
If there is a handler for this exception, the program may be safely continued.
and the breakpoint is at line 1749 of xstring file. This is the code:
size_type length() const _NOEXCEPT
{ // return length of sequence
return (this->_Mysize);
}
I hope someone can tell me why this doesn't work!
Thank you in advance.
header:
class QueueMSG
{
public:
QueueMSG(wxEvtHandler* pParent) : m_pParent(pParent) {}
void AddJob(MessagePTR job); // push a job with given priority class onto the FIFO
MessagePTR Pop();
void Report(const Message::tCOMMANDS& cmd, MessagePTR arg); // report back to parent
size_t Stacksize(); // helper function to return no of pending jobs
private:
wxEvtHandler* m_pParent;
std::vector<MessagePTR> m_Jobs; // multimap to reflect prioritization: values with lower keys come first, newer values with same key are appended
wxMutex m_MutexQueue; // protects queue access
wxSemaphore m_QueueCount; // semaphore count reflects number of queued jobs
};
class WorkerThread : public wxThread
{
public:
WorkerThread(QueueMSG* pQueue) : m_pQueue(pQueue) { wxThread::Create(); }
private:
QueueMSG* m_pQueue;
BingTranslate bng;
virtual wxThread::ExitCode Entry();
virtual void OnJob();
}; // class WorkerThread : public wxThread
cpp:
void QueueMSG::AddJob(MessagePTR job) // push a job with given priority class onto the FIFO
{
wxMutexLocker lock(m_MutexQueue); // lock the queue
m_Jobs.push_back(job); // insert the prioritized entry into the multimap
m_QueueCount.Post(); // new job has arrived: increment semaphore counter
} // void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL)
MessagePTR QueueMSG::Pop()
{
std::vector<MessagePTR>::iterator element;
m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)
m_MutexQueue.Lock(); // lock queue
element = m_Jobs.begin(); // get the first entry from queue (higher priority classes come first)
m_Jobs.erase(m_Jobs.begin()); // erase it
m_MutexQueue.Unlock();// unlock queue
return *element; // return job entry
} // tJOB Pop()
void QueueMSG::Report(const Message::tCOMMANDS& cmd, MessagePTR arg) // report back to parent
{
wxThreadEvent evt(wxEVT_THREAD, wxID_ANY);// cmd); // create command event object
evt.SetPayload<MessagePTR>(arg); // associate string with it
wxQueueEvent(m_pParent, evt.Clone());
//m_pParent->AddPendingEvent(evt); // and add it to parent's event queue
} // void Report(const tJOB::tCOMMANDS& cmd, const wxString& arg=wxEmptyString)
size_t QueueMSG::Stacksize()
{
wxMutexLocker lock(m_MutexQueue); // lock queue until the size has been read
return m_Jobs.size();
}
void WorkerThread::OnJob()
{
MessagePTR job = m_pQueue->Pop(); // pop a job from the queue. this will block the worker thread if queue is empty
bng.translateThis(job);
switch (job->m_cmd)
{
case Message::eID_THREAD_EXIT: // thread should exit
//Sleep(1000); // wait a while
throw Message::eID_THREAD_EXIT; // confirm exit command
case Message::eID_THREAD_JOB: // process a standard job
//Sleep(2000);
m_pQueue->Report(Message::eID_THREAD_JOB, job); // report successful completion
break;
case Message::eID_THREAD_JOBERR: // process a job that terminates with an error
m_pQueue->Report(Message::eID_THREAD_JOB, job);
//Sleep(1000);
throw Message::eID_THREAD_EXIT; // report exit of worker thread
break;
case Message::eID_THREAD_NULL: // dummy command
default: break; // default
} // switch(job.m_cmd)
} // virtual void OnJob()
wxThread::ExitCode WorkerThread::Entry()
{
Message::tCOMMANDS iErr;
m_pQueue->Report(Message::eID_THREAD_STARTED, NULL); // tell main thread that worker thread has successfully started
try {
while (true)
OnJob();
} // this is the main loop: process jobs until a job handler throws
catch (Message::tCOMMANDS& i) {
m_pQueue->Report(iErr = i, NULL);
} // catch return value from error condition
return (wxThread::ExitCode)iErr; // and return exit code
} // virtual wxThread::ExitCode Entry()
Message class:
#pragma once
#include <cstring>
#include <stdio.h>
#include <wx/string.h>
#include <vector>
#include <memory>
enum MSGDirection{
in,
out
};
class Message {
public:
enum tCOMMANDS // list of commands that are currently implemented
{
eID_THREAD_EXIT = wxID_EXIT, // thread should exit or wants to exit
eID_THREAD_NULL = wxID_HIGHEST + 1, // dummy command
eID_THREAD_STARTED, // worker thread has started OK
eID_THREAD_JOB, // process normal job
eID_THREAD_JOBERR // process errorneous job after which thread likes to exit
}; // enum tCOMMANDS
Message(MSGDirection dir, wxString from, wxString message, wxString language_org, wxString language_dest) : m_message(message), m_dir(dir), m_from(from), m_language_orig(language_org), m_language_dest(language_dest), m_cmd(eID_THREAD_EXIT){
time_t rawtime;
struct tm* timeinfo;
char timestamp[100];
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(timestamp, 100, "%c", timeinfo);
m_timestamp = timestamp;
}
Message() : m_cmd(eID_THREAD_NULL) {}
~Message(){ }
void setIO(MSGDirection dir);
MSGDirection getIO(){ return m_dir; };
void setFrom(char* dir);
wxString getFrom(){ return m_from; };
void setMSG(wxString dir){
m_message = dir;
};
wxString getMSG(){ return m_message; }
wxString getTranslated(){ return m_translated; }
wxString getTimeStamp(){ return m_timestamp; }
wxString getLanguageOrig(){ return m_language_orig; }
wxString getLanguageDest(){ return m_language_dest; }
void setSrtTranslate(wxString str){ m_translated = str; }
tCOMMANDS m_cmd;
private:
MSGDirection m_dir;
wxString m_language_orig;
wxString m_from;
wxString m_message;
wxString m_timestamp;
wxString m_language_dest;
wxString m_translated;
};
typedef std::shared_ptr<Message> MessagePTR;
typedef std::shared_ptr<std::vector<MessagePTR>> MessageQueuePTR;
In your QueueMSG::Pop()
call, you use an iterator even after it has been invalidated. Even if it wasn't invalidated by your call to erase
(which it will be, see here), you cannot guarantee its validation after you have unlocked the mutex since you can be time-sliced at that point and then another thread can modify the container. Therefore you should instead do:
MessagePtr msg;
m_QueueCount.Wait();
{
wxMutexLocker lock( m_MutexQueue );
auto element = m_Jobs.begin();
msg = *element;
m_Jobs.erase(element);
}
return msg;
In their code - only slightly different than yours - they dereference the iterator to get a pair and return the contained second element via this line:
element=(m_Jobs.begin())->second; // that '->'
which is why they would not suffer from undefined behaviour.
User contributions licensed under CC BY-SA 3.0