Exception with semaphore wxwidgets

0

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;
c++
visual-studio-2013
wxwidgets
asked on Stack Overflow Apr 7, 2015 by Daniel 976034 • edited Apr 7, 2015 by Daniel 976034

1 Answer

0

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.

answered on Stack Overflow Apr 7, 2015 by Rollen • edited Apr 7, 2015 by Rollen

User contributions licensed under CC BY-SA 3.0