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.
Related
I want to synchronize two tasks in a command/response communication server. One task sends data to a serial port and another task receives data on the serial port. Received data should either be returned to the sender task or do something else with it.
I unsuccessfully tried using volatile bool flags but have now found that won't work with C++ (See When to use volatile with multi threading?)
So trying to use semaphores to do it but can't quite figure out how. Some (bad) psuedo-code using volatile bool is below. How/where to modify for semaphore give/take?
Actual code/platform is C++ 11 running on ESP32 (ESP-IDF). Resources are very limited so no C++ std:: libraries.
volatile bool responsePending = false;
volatile bool cmdAccepted = false;
char sharedBuffer[100];
// SENDER //
void Task1()
{
char localBuffer[100];
while (1)
{
responsePending = true;
cmdAccepted = false;
sendMessage();
while (responsePending)
sleep();
strcpy(localBuffer, sharedBuffer);
cmdAccepted = true; // signal Task2
}
}
// RECEIVER //
void Task2()
{
char localBuf[100];
int fd = open();
while (1)
{
if (select())
{
read(fd, localBuf);
if (responsePending)
{
strcpy(sharedBuffer, localBuf);
responsePending = false; // signal Task1
while (!cmdAccepted)
sleep();
}
else
{
// Do something else with the received data
}
}
}
}
Create a queue which holds a struct. One tasks waits for the serial, if it got data it will put the message to the struct and the struct to the queue.
Other task waits for the queue, if there are items in the queue it will process the struct.
Example:
struct queueData{
char messageBuffer[100];
};
QueueHandle_t queueHandle;
void taskOne(){
while(){
// Task one checks if it got serial data.
if( gotSerialMsg() ){
// create a struct
queueData data;
// copy the data to the struct
strcpy( getSerialMSG(), data.messageBuffer );
// send struct to queue ( waits indefinietly )
xQueueSend(queueHandle, &data, portMAX_DELAY);
}
vTaskDelay(1); // Must feed other tasks
}
}
void taskTwo(){
while(){
// Check if a structs has an item
if( uxQueueMessagesWaiting(queueHandle) > 0 ){
// create a holding struct
queueData data;
// Receive the whole struct
if (xQueueReceive(queueHandle, &data, 0) == pdTRUE) {
// Struct holds message like: data.messageBuffer
}
}
vTaskDelay(1); // Must feed other tasks
}
}
The good thing in passing structs to queues is that you can always put more data into it. booleans or ints or any other thing.
I'm working on timer queue using concurrent_priority_queue right now..
I implemented basic logic of executing most urgent event in this queue.
Here's my code.
TimerEvent ev{};
while (timer.mLoop)
{
while (timer.mQueue.empty() == false)
{
if (timer.mQueue.try_pop(ev) == false)
continue;
if (ev.Type == EVENT_TYPE::PHYSICS) // Physics event is around 15 ~ 17ms
{
auto now = Clock::now();
std::this_thread::sleep_for(ev.StartTime - now);
timer.mGameServerPtr->PostPhysicsOperation(ev.WorldID);
}
else if (ev.Type == EVENT_TYPE::INVINCIBLE) // This event is 3sec long.
{
auto now = Clock::now();
std::this_thread::sleep_for(ev.StartTime - now); // This is wrong!!
timer.mGameServerPtr->ReleaseInvincibleMode(ev.WorldID);
}
}
std::this_thread::sleep_for(10ms);
}
The problem would be easily solved if there is like front/top method in concurrent_priority_queue.
But there is no such method in class because it isn't thread-safe.
So, I just popped event out of the queue and waited until start time of the event.
In this way, I shouldn't have to insert event into queue again.
But problem is that if I have another type of event like EVENT_TYPE::INVINCIBLE, then I shouldn't just use sleep_for because this event is almost 3 second long. While waiting for 3 second, the PHYSICS event will not executed in time.
I can use sleep_for method for PHYSIC event since it is most shortest one to wait.
But I have to re-insert INVINCIBLE event into queue.
How can I optimize this timer without re-insert event into queue again?
How can I optimize this timer without re-insert event into queue again?
By the looks of it, that'll be hard when using the implementation of concurrent_priority_queue you are currently using. It wouldn't be hard if you just used the standard std::priority_queue and added some locking where needed though.
Example:
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <queue>
using Clock = std::chrono::steady_clock;
using time_point = std::chrono::time_point<Clock>;
struct TimerEvent {
void operator()() { m_event(); }
bool operator<(const TimerEvent& rhs) const {
return rhs.StartTime < StartTime;
}
time_point StartTime;
std::function<void()> m_event; // what to execute when the timer is due
};
class TimerQueue {
public:
~TimerQueue() { shutdown(); }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
// add a new TimerEvent to the queue
template<class... Args>
void emplace(Args&&... args) {
std::scoped_lock lock(m_mutex);
m_queue.emplace(TimerEvent{std::forward<Args>(args)...});
m_cv.notify_all();
}
// Wait until it's time to fire the event that is first in the queue
// which may change while we are waiting, but that'll work too.
bool wait_pop(TimerEvent& ev) {
std::unique_lock lock(m_mutex);
while(!m_shutdown &&
(m_queue.empty() || Clock::now() < m_queue.top().StartTime))
{
if(m_queue.empty()) { // wait "forever"
m_cv.wait(lock);
} else { // wait until first StartTime
auto st = m_queue.top().StartTime;
m_cv.wait_until(lock, st);
}
}
if(m_shutdown) return false; // time to quit
ev = std::move(m_queue.top()); // extract event
m_queue.pop();
return true;
}
private:
std::priority_queue<TimerEvent> m_queue;
mutable std::mutex m_mutex;
std::condition_variable m_cv;
std::atomic<bool> m_shutdown{};
};
If an event that is due before the event we're currently waiting for in wait_pop comes in, the m_cv.wait/m_cv.wait_until will unblock (because of the m_cv.notify_all() in emplace()) and that new element will be the first in queue.
The event loop could simply be:
void event_loop(TimerQueue& tq) {
TimerEvent te;
while(tq.wait_pop(te)) {
te(); // execute event
}
// the queue was shutdown, exit thread
}
And you could put any kind of invocable with the time point when you'd like it to fire in that queue.
#include <thread>
int main() {
TimerQueue tq;
// create a thread to run the event loop
auto ev_th = std::thread(event_loop, std::ref(tq));
// wait a second
std::this_thread::sleep_for(std::chrono::seconds(1));
// add an event in 5 seconds
tq.emplace(Clock::now() + std::chrono::seconds(5), [] {
std::cout << "second\n";
});
// wait a second
std::this_thread::sleep_for(std::chrono::seconds(1));
// add an event in 2 seconds
tq.emplace(Clock::now() + std::chrono::seconds(2), [] {
std::cout << "first\n";
});
// sleep some time
std::this_thread::sleep_for(std::chrono::seconds(3));
// shutdown, only the event printing "first" will have fired
tq.shutdown();
ev_th.join();
}
Demo with logging
I am a hobby programmer learning C++ and multi-threading, and getting started on my first thread-pool attempt.
I use Code::Blocks 20.3, wxWidgets 3.1.4, and MinGW 17.1 on a Windows 10 Pro computer.
I have tried several thread-pool examples, but all blocked the GUI.
I found an example shown in https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication
that uses detached threads in a pool. This should not block the GUI.
I have "restructured" the 1 file example to work in a test project (gui, app, main, thread-pool modules).
I placed the classes in their own file, and moved the "main" part to the Main.cpp in my test project and replaced the gui code with a separate class file.
The standard example works as expected.
In the example, strings are passed to the thread-pool and other strings back to the main thread.
I have been searching for main thread AddToQueue() to pass any function like e.g. aTask() (void, or returning something to the main thread) that executes in the thread-pool. My search was not successful :-(.
=== Simple Task to be executed in a thread ===
std::vector<wxString> wxThreadCom2Frame::aTask(wxString wsSomeString, int x)
{
std::vector<wxString> vTest{};
for(int i = 0; i < x; i++)
{
wxString wsTest{};
wsTest << wsSomeString << " [" << i << "]";
vTest.push_back(wsTest);
}
return vTest;
}
=== Or as alternative, pass the vector by reference
aTask(_T("Just some text"), 5, &vTest); // to be queued with AddJob
===
void wxThreadCom2Frame::aTask(wxString wsSomeString, int x, std::vector<wxString> *vTest)
{
for(int i = 0; i < x; i++)
{
wxString wsTest{};
wsTest << wsSomeString << " [" << i << "]\n";
vTest->push_back(wsTest);
}
}
===
I hope someone can help me understand how to do this.
This is the first step to what I actually like to achieve.
An 'extraction' function returns a structure of 20 tags from a music file (mp3, flac, etc).
The main 'collecting' function will call the 'extraction' function for each file (up to 7000) in a list and place it in the queue of the thread-pool.
The 'collecting' function returns a vector of structures to the main thread for further processing.
Regards, Ruud.
=== ThreadCom.cpp ===
/////////////////////////////////////////////////////////////////////////////
// https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication //
/////////////////////////////////////////////////////////////////////////////
// Standard
#include <stdlib.h>
#include <assert.h>
#include <map>
#include <list>
// wxWidgets
#include <wx/frame.h>
#include <wx/thread.h>
#include <wx/menu.h>
#include <wx/app.h>
class tJOB
{
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 = ID_THREAD_JOB, // process normal job
eID_THREAD_JOBERR = ID_THREAD_JOBERR // process erroneous job after which thread likes to exit
}; // enum tCOMMANDS
tJOB() : m_cmd(eID_THREAD_NULL) {}
tJOB(tCOMMANDS cmd, const wxString& arg) : m_cmd(cmd), m_Arg(arg) {}
tCOMMANDS m_cmd; wxString m_Arg;
}; // class tJOB
class QUEUE
{
public:
enum tPRIORITY { eHIGHEST, eHIGHER, eNORMAL, eBELOW_NORMAL, eLOW, eIDLE }; // priority classes
QUEUE(wxEvtHandler* pParent) : m_pParent(pParent) {}
void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL) // push a job with given priority class onto the FIFO
{
wxMutexLocker lock(m_MutexQueue); // lock the queue
m_Jobs.insert(std::make_pair(priority, 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)
tJOB Pop()
{
tJOB element;
m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)
m_MutexQueue.Lock(); // lock queue
element=(m_Jobs.begin())->second; // 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 Report(const tJOB::tCOMMANDS& cmd, const wxString& sArg=wxEmptyString, int iArg=0) // report back to parent
{
wxCommandEvent evt(wxEVT_THREAD, cmd); // create command event object
evt.SetString(sArg); // associate string with it
evt.SetInt(iArg);
m_pParent->AddPendingEvent(evt); // and add it to parent's event queue
} // void Report(const tJOB::tCOMMANDS& cmd, const wxString& arg=wxEmptyString)
size_t Stacksize() // helper function to return no of pending jobs
{
wxMutexLocker lock(m_MutexQueue); // lock queue until the size has been read
return m_Jobs.size();
}
private:
wxEvtHandler* m_pParent;
std::multimap<tPRIORITY, tJOB> 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(QUEUE* pQueue, int id=0) : m_pQueue(pQueue), m_ID(id) { assert(pQueue); wxThread::Create(); }
private:
QUEUE* m_pQueue;
int m_ID;
virtual wxThread::ExitCode Entry()
{
Sleep(1000); // sleep a while to simulate some time-consuming init procedure
tJOB::tCOMMANDS iErr;
m_pQueue->Report(tJOB::eID_THREAD_STARTED, wxEmptyString, m_ID); // 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(tJOB::tCOMMANDS& i) { m_pQueue->Report(iErr=i, wxEmptyString, m_ID); } // catch return value from error condition
return (wxThread::ExitCode)iErr; // and return exit code
} // virtual wxThread::ExitCode Entry()
virtual void OnJob()
{
tJOB job=m_pQueue->Pop(); // pop a job from the queue. this will block the worker thread if queue is empty
switch(job.m_cmd)
{
case tJOB::eID_THREAD_EXIT: // thread should exit
Sleep(1000); // wait a while
throw tJOB::eID_THREAD_EXIT; // confirm exit command
case tJOB::eID_THREAD_JOB: // process a standard job
Sleep(2000);
m_pQueue->Report(tJOB::eID_THREAD_JOB, wxString::Format(wxT("Job #%s done."), job.m_Arg.c_str()), m_ID); // report successful completion
break;
case tJOB::eID_THREAD_JOBERR: // process a job that terminates with an error
m_pQueue->Report(tJOB::eID_THREAD_JOB, wxString::Format(wxT("Job #%s erroneous."), job.m_Arg.c_str()), m_ID);
Sleep(1000);
throw tJOB::eID_THREAD_EXIT; // report exit of worker thread
break;
case tJOB::eID_THREAD_NULL: // dummy command
default:
break; // default
} // switch(job.m_cmd)
} // virtual void OnJob()
}; // class WorkerThread : public wxThread
=== partial wxThreadCom2Main.cpp ===
void wxThreadCom2Frame::AddToQueue( wxCommandEvent& event )
{
int iJob=rand();
m_pQueue->AddJob(tJOB((tJOB::tCOMMANDS)event.GetId(), wxString::Format(wxT("%u"), iJob)));
SetStatusText(wxString::Format(wxT("Job #%i started."), iJob)); // just set the status text
}
void wxThreadCom2Frame::OnThread(wxCommandEvent& event) // handler for thread notifications
{
switch(event.GetId())
{
case tJOB::eID_THREAD_JOB:
// Get the returned vector and do something with it
SetStatusText(wxString::Format(wxT("[%i]: %s"), event.GetInt(), event.GetString().c_str())); // progress display
break;
case tJOB::eID_THREAD_EXIT:
SetStatusText(wxString::Format(wxT("[%i]: Stopped."), event.GetInt()));
m_Threads.remove(event.GetInt()); // thread has exited: remove thread ID from list
if(m_Threads.empty()) { EnableControls(false); } // disable some menu items if no more threads
break;
case tJOB::eID_THREAD_STARTED:
SetStatusText(wxString::Format(wxT("[%i]: Ready."), event.GetInt()));
EnableControls(true); // at least one thread successfully started: enable controls
break;
default:
event.Skip();
}
}
void wxThreadCom2Frame::EnableControls(bool bEnable) // en/dis-able Stop, Add Job, Add JobErr
{
wxMenu* pMenu=GetMenuBar()->GetMenu(0);
static const int MENUIDS[]={/*ID_START_THREAD, */ID_THREAD_EXIT, ID_THREAD_JOB, ID_THREAD_JOBERR};
for(unsigned int i=0; i<WXSIZEOF(MENUIDS); pMenu->Enable(MENUIDS[i++], bEnable));
}
===
I'm writing an Audio class that holds an std::thread for refilling some buffers asynchronously. Say we call the main thread A and the background (class member) thread B. I'm using an std::mutex to block thread B whenever the sound is not playing, that way it doesn't run in the background when unnecessary and doesn't use excess CPU power. The mutex locked by thread A by default, so thread B is blocked, then when it's time to play the sound thread A unlocks the mutex and thread B runs (by locking then immediately unlocking it) in a loop.
The issue comes up when thread B sees that it's reached the end of the file. It can stop playback and clean up buffers and such, but it can't stop its own loop because thread B can't lock the mutex from thread A.
Here's the relevant code outline:
class Audio {
private:
// ...
std::thread Thread;
std::mutex PauseMutex; // mutex that blocks Thread, locked in constructor
void ThreadFunc(); // assigned to Thread in constructor
public:
// ...
void Play();
void Stop();
}
_
void Audio::ThreadFunc() {
// ... (include initial check of mutex here)
while (!this->EndThread) { // Thread-safe flag, only set when Audio is destructed
// ... Check and refill buffers as necessary, etc ...
if (EOF)
Stop();
// Attempt a lock, blocks thread if sound/music is not playing
this->PauseMutex.lock();
this->PauseMutex.unlock();
}
}
void Audio::Play() {
// ...
PauseMutex.unlock(); // unlock mutex so loop in ThreadFunc can start
}
void Audio::Stop() {
// ...
PauseMutex.lock(); // locks mutex to stop loop in ThreadFunc
// ^^ This is the issue here
}
In the above setup, when the background thread sees that it's reached EOF, it would call the class's Stop() function, which supposedly locks the mutex to stop the background thread. This doesn't work because the mutex would have to be locked by the main thread, not the background thread (in this example, it crashes in ThreadFunc because the background thread attempts a lock in its main loop after already locking in Stop()).
At this point the only thing I could think of would be to somehow have the background thread lock the mutex as if it was the main thread, giving the main thread ownership of the mutex... if that's even possible? Is there a way for a thread to transfer ownership of a mutex to another thread? Or is this a design flaw in the setup I've created? (If the latter, are there any rational workarounds?) Everything else in the class so far works just as designed.
I'm not going to even pretend to understand how your code is trying to do what it is doing. There is one thing, however, that is evident. You're trying to use a mutex for conveying some predicate state change, which is the wrong vehicle to drive on that freeway.
Predicate state change is handled by coupling three things:
Some predicate datum
A mutex to protect the predicate
A condition variable to convey possible change in predicate state.
The Goal
The goal in the below example is to demonstrate how a mutex, a condition variable, and predicate data are used in concert when controlling program flow across multiple threads. It shows examples of using both wait and wait_for condition variable functionality, as well as one way to run a member function as a thread proc.
Following is a simple Player class toggles between four possible states:
Stopped : The player is not playing, nor paused, nor quitting.
Playing : The player is playing
Paused : The player is paused, and will continue from whence it left off once it resumes Playing.
Quit : The player should stop what it is doing and terminate.
The predicate data is fairly obvious. the state member. It must be protected, which means it cannot be changed nor checked unless under the protection of the mutex. I've added to this a counter that simply increments during the course of maintaining the Playing state for some period of time. more specifically:
While Playing, each 200ms the counter increments, then dumps some data to the console.
While Paused, counter is not changed, but retains its last value while Playing. This means when resumed it will continue from where it left off.
When Stopped, the counter is reset to zero and a newline is injected into the console output. This means switching back to Playing will start the counter sequence all over again.
Setting the Quit state has no effect on counter, it will be going away along with everything else.
The Code
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unistd.h>
using namespace std::chrono_literals;
struct Player
{
private:
std::mutex mtx;
std::condition_variable cv;
std::thread thr;
enum State
{
Stopped,
Paused,
Playing,
Quit
};
State state;
int counter;
void signal_state(State st)
{
std::unique_lock<std::mutex> lock(mtx);
if (st != state)
{
state = st;
cv.notify_one();
}
}
// main player monitor
void monitor()
{
std::unique_lock<std::mutex> lock(mtx);
bool bQuit = false;
while (!bQuit)
{
switch (state)
{
case Playing:
std::cout << ++counter << '.';
cv.wait_for(lock, 200ms, [this](){ return state != Playing; });
break;
case Stopped:
cv.wait(lock, [this]() { return state != Stopped; });
std::cout << '\n';
counter = 0;
break;
case Paused:
cv.wait(lock, [this]() { return state != Paused; });
break;
case Quit:
bQuit = true;
break;
}
}
}
public:
Player()
: state(Stopped)
, counter(0)
{
thr = std::thread(std::bind(&Player::monitor, this));
}
~Player()
{
quit();
thr.join();
}
void stop() { signal_state(Stopped); }
void play() { signal_state(Playing); }
void pause() { signal_state(Paused); }
void quit() { signal_state(Quit); }
};
int main()
{
Player player;
player.play();
sleep(3);
player.pause();
sleep(3);
player.play();
sleep(3);
player.stop();
sleep(3);
player.play();
sleep(3);
}
Output
I can't really demonstrate this. You'll have to run it and see how it works, and I invite you to toy with the states in main() as I have above. Do note, however, that once quit is invoked none of the other stated will be monitored. Setting the Quit state will shut down the monitor thread. For what its worth, a run of the above should look something like this:
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
with the first set of numbers dumped in two groups (1..15, then 16..30), as a result of playing, then pausing, then playing again. Then a stop is issued, followed by another play for a period of ~3 seconds. After that, the object self-destructs, and in doing so, sets the Quit state, and waits for the monitor to terminate.
Summary
Hopefully you get something out of this. If you find yourself trying to manage predicate state by manually latching and releasing mutexes, changes are you need a condition-variable design patter to facilitate detecting those changes.
Hope you get something out of it.
class CtLockCS
{
public:
//--------------------------------------------------------------------------
CtLockCS() { ::InitializeCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
~CtLockCS() { ::DeleteCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
bool TryLock() { return ::TryEnterCriticalSection(&m_cs) == TRUE; }
//--------------------------------------------------------------------------
void Lock() { ::EnterCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
void Unlock() { ::LeaveCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
protected:
CRITICAL_SECTION m_cs;
};
///////////////////////////////////////////////////////////////////////////////
// class CtLockMX - using mutex
class CtLockMX
{
public:
//--------------------------------------------------------------------------
CtLockMX(const TCHAR* nameMutex = 0)
{ m_mx = ::CreateMutex(0, FALSE, nameMutex); }
//--------------------------------------------------------------------------
~CtLockMX()
{ if (m_mx) { ::CloseHandle(m_mx); m_mx = NULL; } }
//--------------------------------------------------------------------------
bool TryLock()
{ return m_mx ? (::WaitForSingleObject(m_mx, 0) == WAIT_OBJECT_0) : false; }
//--------------------------------------------------------------------------
void Lock()
{ if (m_mx) { ::WaitForSingleObject(m_mx, INFINITE); } }
//--------------------------------------------------------------------------
void Unlock()
{ if (m_mx) { ::ReleaseMutex(m_mx); } }
//--------------------------------------------------------------------------
protected:
HANDLE m_mx;
};
///////////////////////////////////////////////////////////////////////////////
// class CtLockSM - using semaphore
class CtLockSM
{
public:
//--------------------------------------------------------------------------
CtLockSM(int maxcnt) { m_sm = ::CreateSemaphore(0, maxcnt, maxcnt, 0); }
//--------------------------------------------------------------------------
~CtLockSM() { ::CloseHandle(m_sm); }
//--------------------------------------------------------------------------
bool TryLock() { return m_sm ? (::WaitForSingleObject(m_sm, 0) == WAIT_OBJECT_0) : false; }
//--------------------------------------------------------------------------
void Lock() { if (m_sm) { ::WaitForSingleObject(m_sm, INFINITE); } }
//--------------------------------------------------------------------------
void Unlock()
{
if (m_sm){
LONG prevcnt = 0;
::ReleaseSemaphore(m_sm, 1, &prevcnt);
}
}
//--------------------------------------------------------------------------
protected:
HANDLE m_sm;
};
I have a C++ app with 2 threads. The app displays a gauge on the screen, with an indicator that rotates based on an angle received via UDP socket. My problem is that the indicator should be rotating at a constant rate but it behaves like time slows down at times, and it also fast-forwards to catch up quickly at other times, with some pauses intermittently.
Each frame, the display (main) thread guards a copy of the angle from the UDP thread. The UDP thread also guards writing to the shared variable. I use a Windows CriticalSection object to guard the 'communication' between threads. The UDP packet is received at approximately the same rate as the display update. I am using Windows 7, 64 bit, with a 4-core processor.
I am using a separate python app to broadcast the UDP packet. I use the python function, time.sleep, to keep the broadcast at a constant rate.
Why does the application slow down?
Why does the application seem to fast-forward instead of snapping to the latest angle?
What is the proper fix?
EDIT: I am not 100% sure all angle values are remembered when the app seems to 'fast forward'. The app is snapping to some value (not sure if it is the 'latest') at times.
EDIT 2: per request, some code.
void App::udp_update(DWORD thread_id)
{
Packet p;
_socket.recv(p); // edit: blocks until transmission is received
{
Locker lock(_cs);
_packet = p;
}
}
void App::main_update()
{
float angle_copy = 0.f;
{
Locker lock(_cs);
angle_copy = _packet.angle;
}
draw(angle_copy); // edit: blocks until monitor refreshes
}
Thread.h
class CS
{
private:
friend Locker;
CRITICAL_SECTION _handle;
void _lock();
void _unlock();
// not implemented by design
CS(CS&);
CS& operator=(const CS&);
public:
CS();
~CS();
};
class Locker
{
private:
CS& _cs;
// not implemented by design
Locker();
Locker(const Locker&);
Locker& operator=(const Locker&);
public:
Locker(CS& c)
: _cs(c)
{
_cs._lock();
}
~Locker()
{
_cs._unlock();
}
};
class Win32ThreadPolicy
{
public:
typedef Functor<void,TYPELIST_1(DWORD)> Callback;
private:
Callback _callback;
//SECURITY_DESCRIPTOR _sec_descr;
//SECURITY_ATTRIBUTES _sec_attrib;
HANDLE _handle;
//DWORD _exitValue;
#ifdef USE_BEGIN_API
unsigned int _id;
#else // USE_BEGIN_API
DWORD _id;
#endif // USE_BEGIN_API
/*volatile*/ bool _is_joined;
#ifdef USE_BEGIN_API
static unsigned int WINAPI ThreadProc( void* lpParameter );
#else // USE_BEGIN_API
static DWORD WINAPI ThreadProc( LPVOID lpParameter );
#endif // USE_BEGIN_API
DWORD _run();
void _join();
// not implemented by design
Win32ThreadPolicy();
Win32ThreadPolicy(const Win32ThreadPolicy&);
Win32ThreadPolicy& operator=(const Win32ThreadPolicy&);
public:
Win32ThreadPolicy(Callback& func);
~Win32ThreadPolicy();
void Spawn();
void Join();
};
/// helps to manage parallel operations.
/// attempts to mimic the C++11 std::thread interface, but also passes the thread ID.
class Thread
{
public:
typedef Functor<void,TYPELIST_1(DWORD)> Callback;
typedef Win32ThreadPolicy PlatformPolicy;
private:
PlatformPolicy _platform;
/// not implemented by design
Thread();
Thread(const Thread&);
Thread& operator=(const Thread&);
public:
/// begins parallel execution of the parameter, func.
/// \param func, the function object to be executed.
Thread(Callback& func)
: _platform(func)
{
_platform.Spawn();
}
/// stops parallel execution and joins with main thread.
~Thread()
{
_platform.Join();
}
};
Thread.cpp
#include "Thread.h"
void CS::_lock()
{
::EnterCriticalSection( &_handle );
}
void CS::_unlock()
{
::LeaveCriticalSection( &_handle );
}
CS::CS()
: _handle()
{
::memset( &_handle, 0, sizeof(CRITICAL_SECTION) );
::InitializeCriticalSection( &_handle );
}
CS::~CS()
{
::DeleteCriticalSection( &_handle );
}
Win32ThreadPolicy::Win32ThreadPolicy(Callback& func)
: _handle(NULL)
//, _sec_descr()
//, _sec_attrib()
, _id(0)
, _is_joined(true)
, _callback(func)
{
}
void Win32ThreadPolicy::Spawn()
{
// for an example of managing descriptors, see:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa446595%28v=vs.85%29.aspx
//BOOL success_descr = ::InitializeSecurityDescriptor( &_sec_descr, SECURITY_DESCRIPTOR_REVISION );
//TODO: do we want to start with CREATE_SUSPENDED ?
// TODO: wrap this with exception handling
#ifdef USE_BEGIN_END
// http://msdn.microsoft.com/en-us/library/kdzttdcb%28v=vs.100%29.aspx
_handle = (HANDLE) _beginthreadex( NULL, 0, &Thread::ThreadProc, this, 0, &_id );
#else // USE_BEGIN_END
_handle = ::CreateThread( NULL, 0, &Win32ThreadPolicy::ThreadProc, this, 0, &_id );
#endif // USE_BEGIN_END
}
void Win32ThreadPolicy::_join()
{
// signal that the thread should complete
_is_joined = true;
// maybe ::WFSO is not the best solution.
// "Except that WaitForSingleObject and its big brother WaitForMultipleObjects are dangerous.
// The basic problem is that these calls can cause deadlocks,
// if you ever call them from a thread that has its own message loop and windows."
// http://marc.durdin.net/2012/08/waitforsingleobject-why-you-should-never-use-it/
//
// He advises to use MsgWaitForMultipleObjects instead:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684242%28v=vs.85%29.aspx
DWORD result = ::WaitForSingleObject( _handle, INFINITE );
// _handle must have THREAD_QUERY_INFORMATION security access enabled to use the following:
//DWORD exitCode = 0;
//BOOL success = ::GetExitCodeThread( _handle, &_exitValue );
}
Win32ThreadPolicy::~Win32ThreadPolicy()
{
}
void Win32ThreadPolicy::Join()
{
if( !_is_joined )
{
_join();
}
// this example shows that it is correct to pass the handle returned by CreateThread
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516%28v=vs.85%29.aspx
::CloseHandle( _handle );
_handle = NULL;
}
DWORD Win32ThreadPolicy::_run()
{
// TODO: do we need to make sure _id has been assigned?
while( !_is_joined )
{
_callback(_id);
::Sleep(0);
}
// TODO: what should we return?
return 0;
}
#ifdef USE_BEGIN_END
unsigned int WINAPI Thread::ThreadProc( LPVOID lpParameter )
#else // USE_BEGIN_END
DWORD WINAPI Win32ThreadPolicy::ThreadProc( LPVOID lpParameter )
#endif // USE_BEGIN_END
{
Win32ThreadPolicy* tptr = static_cast<Win32ThreadPolicy*>( lpParameter );
tptr->_is_joined = false;
// when this function (ThreadProc) returns, ::ExitThread is used to terminate the thread with an "implicit" call.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453%28v=vs.85%29.aspx
return tptr->_run();
}
I know this is a bit in the assumption space but:
The rate you are talking about is set in "server" and "client" via a sleep that controls the speed with which the packets are sent. This is not necessarily the rate of actual transmission, as the OS can schedule your processes in a very asymmetric way (time wise).
This can mean that when the server gets more time, it will fill an OS buffer with packets (the client will get less processor time, thus, consumming at a lower rate => slowing down the meter). Then, when the client gets more time that the server, it will consume fast all packets, while the update thread will still do some waiting. But this doesn't mean it will "snap", because you are using a critical section to lock the packet update, so probably you don't get to consume too many packages from the OS buffer until a new update. (you may have a "snap to", but with a small step). I am basing this on the fact that i see no actual sleeping in your receive or update methods (the only sleep is done on server side).