tbb concurrent_bounded_queue multiple threads access - c++

I have the following code:
tbb::concurrent_bounded_queue<Image> camera_queue_;
camera_queue_.set_capacity(1);
struct Image
{
int hour_;
int minute_;
int second_;
int msec_;
QImage image_;
Image(){hour_ = -1; minute_ = -1; second_ = -1; msec_ = -1; image_ = QImage();}
Image& operator=(Image const& copy)
{
this->hour_ = copy.hour_;
this->minute_ = copy.minute_;
this->second_ = copy.second_;
this->msec_ = copy.msec_;
this->image_ = copy.image_;
return *this;
}
};
In a Qt Thread :
ThreadA:
tbb::concurrent_bounded_queue<Image> image_queue_;
image_queue_.set_capacity(1);
Image cur_image_;
void Worker::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.pop(cur_image_);
image_queue_.push(cur_image_);
}
emit imageReady();
}
emit finished();
}
Image Worker::getCurrentImage()
{
Image tmp_image;
image_queue_.pop(tmp_image);
return tmp_image;
}
In Another Thread:
ThreadB:
Producer::Producer(){
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Producer::operate, work_, &Worker::process);
QObject::connect(work_, &Worker::imageReady, this, &Producer::displayImage);
QObject::connect(this, &Producer::stopDecode, work_, &Worker::stop);
workerThread_->start();
emit operate();
}
void Producer::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.push(GetImage());
}
}
}
void Producer::displayImage()
{
Image tmp = std::move(work_->getCurrentImage());
widget_->showImage(tmp.image_);
}
However, In main thread, I have a function that enables user to click a button to get current image:
bool Producer::SaveImage()
{
Image img = std::move(work_->getCurrentImage());
std::string fileName = std::to_string(img.hour_) + "-" + std::to_string(img.minute_) + "-" + std::to_string(img.second_) + "-" + std::to_string(img.msec_/1000) + ".jpg";
std::string outFileName = folder + "/" + fileName;
return img.image_.save(QString::fromStdString(outFileName));
}
The problem is:
When user does not click the button to invoke Producer::SaveImage(), the Image Decoding and Showing runs smoothly. But when user invoke Producer::SaveImage(), the whole program will get stuck (Caton phenomenon ?). The GUI response becomes not that smooth. The more user invokes SaveImage, the slower the GUI response becomes.
Can anyone help to explain why ? Is there a way to solve that ?

Why do you want to use concurrent queue? It looks like there is a syncronization mechanism in place and you rely mostly on it instead of using concurrent_queue for synchronisation and communication as it is supposed for.
The issue is that when you set capacity = 1, both operations of concurrent_bounded_queue will block until there is enough space of items in the queue. E.g. if the queue contains an item already, the push operation will block. And since you control your threads with another notification mechanism, you might catch a deadlock.
In particular, try to swap the operations like below:
camera_queue_.pop(cur_image_);
emit imageReady();
image_queue_.push(cur_image_);
This should prepare the thread which receives images (if I understand this correctly) and it will block on its image_queue_.pop() method, then this thread will put new image and unblocks the recipient. There might be other issues similar to this, so, please rethink all of your synchronization.

Related

In GTKMM, on_draw method stops being called after invalidate occurs in separated thread

Using GTKMM, I'm extending the DrawingArea widget with the idea that an external process provides it with images. My CameraDrawingArea will then display the images at the right size using Cairo.
Each time an image arrives, I store it and I call the invalidate method, which eventually ends up in a call to on_draw, where I can resize and display the image.
My problem is the following:
The first 10 or 20 images are displayed as I expected.
After a while, the images keep coming from the provider process, I keep calling invalidate
but on_draw is not called any more.
To show it here, I've simplified the code so that there is nothing external to the class, and no link with other libraries. I've replaced the process providing the images by a method with for-loops, and the display of the image by printing a simple text in the middle of the widget area:
In the constructor I launch a new std::thread to call the doCapture method in the same instance. I also set up a font description, to use it later.
The doCapture method is a silly CPU eater, that does nothing except calling from time to time the refreshDrawing method, as long as keepCapturing is not false.
refreshDrawing invalidates the whole window's rectangle via a call to invalidate.
Gtk's magic is suppose to call on_draw and provide a Cairo context to draw whatever. In my case, for tests purposes, I draw a brownish centered integer.
The class destructor stops the thread by set keepCapturing to false, and waits for termination with a join.
#include "camera-drawing-area.hpp"
#include <iostream>
CameraDrawingArea::CameraDrawingArea():
captureThread(nullptr) {
fontDescription.set_family("Monospace");
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_size(30 * Pango::SCALE);
keepCapturing = true;
captureThread = new std::thread([this] {
doCapture();
});
}
void CameraDrawingArea::doCapture() {
while (keepCapturing) {
float f = 0.0;
for (int n = 0; n < 1000; n++) {
for (int m = 0; m < 1000; m++) {
for (int o = 0; o < 500; o++) {
f += 1.2;
}
}
}
std::cout << "doCapture - " << f << std::endl;
refreshDrawing();
}
}
void CameraDrawingArea::refreshDrawing() {
auto win = get_window();
if (win) {
win->invalidate(false);
std::cout << "refreshDrawing" << std::endl;
}
}
bool CameraDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {
std::cout << "on_draw" << std::endl;
static char buffer[50];
static int n = 0;
sprintf(buffer, "-%d-", n++);
Gtk::Allocation allocation = get_allocation();
const int width = allocation.get_width();
const int height = allocation.get_height();
auto layout = create_pango_layout(buffer);
layout->set_font_description(fontDescription);
int textWidth, textHeight;
layout->get_pixel_size(textWidth, textHeight);
cr->set_source_rgb(0.5, 0.2, 0.1);
cr->move_to((width - textWidth) / 2, (height - textHeight) / 2);
layout->show_in_cairo_context(cr);
cr->stroke();
return true;
}
CameraDrawingArea::~CameraDrawingArea() {
keepCapturing = false;
captureThread->join();
free(captureThread);
}
And this is my header file:
#ifndef CAMERA_DRAWING_AREA_HPP
#define CAMERA_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <thread>
class CameraDrawingArea : public Gtk::DrawingArea {
public:
CameraDrawingArea();
virtual ~CameraDrawingArea();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
private:
bool keepCapturing;
void doCapture();
void refreshDrawing();
std::thread* captureThread;
Pango::FontDescription fontDescription;
};
#endif
The problem manifests itself as follows:
When starting the application, it faithfully displays 1, 2, 3...
Between 5th and 20th iteration (it's random, but rarely outside these ranges), it stops refreshing.
Because of the cout, I can see that refreshDrawing is called be sure that invalidate is also called, but on_draw isn't.
Also, if I stop the application before it stops refreshing, then it ends up nicely. But, if I stop the application after it stops refreshing, then I see this message below (the ID value varies):
GLib-CRITICAL **: 10:05:04.716: Source ID 25 was not found when attempting to remove it
I'm quite sure that I do something wrong, but clueless about what. Any help would be appreciated.
I also checked the following questions, but they don't seem to be related with my case:
Draw signal doesn't get fired in GTKMM, when derived class doesn't call a superclass's constructor
You can't use GTK methods from any other thread than the one in which you started the GTK main loop. Probably the win->invalidate() call is causing things to go wrong here.
Instead, use Glib::Dispatcher to communicate with the main thread, or use gdk_threads_add_idle() for a more C-style solution.
Based on the answer form #ptomato, I've rewritten my example code. The golden rule is do not call GUI functions from another thread, but if you do, then acquire some specific GDK locks first. That's the purpose of Glib::Dispatcher :
If a Glib::Dispatcher object is constructed in the main GUI thread (which will therefore be the receiver thread), any worker thread can emit on it and have the connected slots safely execute gtkmm functions.
Based on that, I've added a new private member Glib::Dispatcher refreshDrawingDispatcher that will allow threads to safely the invalidate the windows area:
#ifndef CAMERA_DRAWING_AREA_HPP
#define CAMERA_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <thread>
class CameraDrawingArea :
public Gtk::DrawingArea {
public:
CameraDrawingArea();
virtual ~CameraDrawingArea();
protected:
bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
private:
bool keepCapturing;
void doCapture();
void refreshDrawing();
Glib::Dispatcher refreshDrawingDispatcher;
std::thread* captureThread;
Pango::FontDescription fontDescription;
};
#endif
Then, I've connected the dispatcher to the refreshDrawing method. I do this in the class constructor, which is called during GUI start up and therefore in the main GUI thread:
CameraDrawingArea::CameraDrawingArea():
refreshDrawingDispatcher(),
captureThread(nullptr) {
fontDescription.set_family("Monospace");
fontDescription.set_weight(Pango::WEIGHT_BOLD);
fontDescription.set_size(30 * Pango::SCALE);
keepCapturing = true;
captureThread = new std::thread([this] {
doCapture();
});
refreshDrawingDispatcher.connect(sigc::mem_fun(*this, &CameraDrawingArea::refreshDrawing));
}
Finally, the thread has to call the dispatcher:
void CameraDrawingArea::doCapture() {
while (keepCapturing) {
float f = 0.0;
for (int n = 0; n < 1000; n++) {
for (int m = 0; m < 1000; m++) {
for (int o = 0; o < 500; o++) {
f += 1.2;
}
}
}
std::cout << "doCapture - " << f << std::endl;
refreshDrawingDispatcher.emit();
}
}
And now, this works without further problems.

C++ Break a loop from another function

I'm developing a video recording software but I am stuck on an issue.
I want to stop the recording when the button Stop is clicked but nothing happens when I click it...
Here is my core (simplified):
MySoftware.hpp:
bool b_Stop = false;
MySoftware.cpp
MainWindow::MainWindow() : QWidget() {
qpb_StartCapture = new QPushButton("Start Capture", this);
QObject::connect(qpb_StartCapture, SIGNAL(clicked()), this, SLOT(startCapture()));
qpb_StopCapture = new QPushButton("Stop Capture", this);
QObject::connect(qpb_StopCapture, SIGNAL(clicked()), this, SLOT(stopCapture()));
}
void MainWindow::startCapture() {
b_Stop = false;
// CAMERAS INITIALIZATION
while (!b_Stop) {
for (int i = 0; i < v_cp_Cameras.size(); i++) {
// IMAGE CAPTURE
}
}
// IMAGES PROCESSING
}
void MainWindow::stopCapture() {
b_Stop = true;
}
The way I see it is that startCapture is probably called from your event loop. This blocks any other events from being processed. Try putting your loop into a separate thread and see if that works.
You're having the image capture inside of a for loop that doesn't check whether or not the stop button is activated. You're checking the while loop, which probably never starts over. put if (b_stop) {break;} inside the for loop and it might work.

Abnormal output when reading from serial port in QT

I am using QT and QCustomPlot to create a real time plotting tool, and the data of the plot is read from the Arduino UNO board. My application succeeded in plotting while the data is a total mess. Here is my code below (Some code is from QCustomPlot website):
void Dialog::realtimeDataSlot()
{
bool currentPortNameChanged = false;
QString currentPortName;
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
}
QString currentRequest = request;
QSerialPort serial;
if (currentPortNameChanged) {
serial.close();
serial.setPortName(currentPortName);
if (!serial.open(QIODevice::ReadOnly)) {
return;
}
}
static QTime time(QTime::currentTime());
// calculate two new data points:
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
// add data to lines:
if(serial.waitForReadyRead(-1)){
data = serial.readAll();
QTextStream(stdout) << "HERE:" << data.toDouble() << endl;
customPlot->graph(0)->addData(key, data.toDouble());
customPlot->graph(0)->rescaleValueAxis(); //rescale value (vertical) axis to fit the current data:
lastPointKey = key;
customPlot->xAxis->setRange(key, 8, Qt::AlignRight);
customPlot->replot();
static double lastFpsKey;
static int frameCount;
++frameCount;
if (key-lastFpsKey > 2) // average fps over 2 seconds
{
lastFpsKey = key;
frameCount = 0;
}
}
}
// calculate frames per second:
if (currentPortName != portName) {
currentPortName = portName;
currentPortNameChanged = true;
} else {
currentPortNameChanged = false;
}
}
When I Tried to print out the data I read from the serial port, I found the following:
HERE:1
HERE:15
HERE:150
HERE:149
HERE:149
HERE:149
HERE:150
HERE:150
HERE:15
HERE:150
HERE:149
HERE:49
HERE:150
HERE:150
HERE:1
HERE:150
The values around 150 are normal while the value that are 0, 1 to others are not. Also it is not print out at a stable speed. I don't know what happened to this, and thanks to whoever may help, and I would appreciate it if there is any better ways to implement this.
The problem here is that it is not guaranteed that the serial transmission is received all at once. So it is better to let the serial to be processed somewhere else, for instance:
// in the class definition
QSerialPort serialPort;
private slots:
void handleReadyRead();
private:
QByteArray serialBuffer;
volatile double lastSerialValue;
// In the initialization part (not the realtimeDataSlot function)
lastSerialValue = qQNaN();
serialPort.setPortName(currentPortName);
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
serialBuffer.clear();
// Other functions:
void Dialog::realtimeDataSlot()
{
...
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
if (!qIsNaN(lastSerialData))
{
// use lastSerialValue as the data.toDouble() you had before, then, at the end
lastSerialValue = qQNaN();
}
...
}
void Dialog::handleReadyRead()
{
serialBuffer.append(serialPort.readAll());
int serPos;
while ((serPos = serialBuffer.indexOf('\n')) >= 0)
{
bool ok;
double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok);
if (ok) lastSerialValue = tempValue;
serialBuffer = serialBuffer.mid(serPos+1);
}
}
Explanation: whenever you receive something from the arduino the bytes are appended to a buffer. Then the byte array is parsed looking for a terminator, and if found the byte array is split and analysed. When the other function needs the data, it simply pulls the most recent one saved in the variable.
NOTE 1: I saw that you used a binary transmission. The problem is that you do not have any way to determine where the data begins and end in this way. For instance, if you receive 0x01 0x02 0x03 0x04 and you know that there are 3 bytes, are they 01..03 or 02..04 or 03, 04 and a missing one or...? The version I implemented requires you to send data in string format with a new-line terminator (simplest version, you just have to write Serial.println(doubleValue); in the arduino code), but if you need the binary version I can give you some hints
NOTE 2: The code I wrote is NOT thread safe. It will work only if the realtimeDataSlot and the handleReadyRead are called in the same thread. Note that if they belong to the same object and are called through signals this is guaranteed.
Now, this should work. But I highly discourage you from doing this. I don't know who needs to call the realtimeDataSlot(), but I think that the most correct version is something like this:
// in the class definition
QSerialPort serialPort;
private slots:
void handleReadyRead();
void receivedData(double val);
private:
QByteArray serialBuffer;
signals:
void newData(double data);
// In the initialization part (not the realtimeDataSlot function)
serialPort.setPortName(currentPortName);
connect(&serialPort, &QSerialPort::readyRead, this, &Dialog::handleReadyRead, Qt::UniqueConnection);
connect(this, &Dialog::newData, this, &Dialog::receivedData, Qt::UniqueConnection);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
serialBuffer.clear();
// Other functions:
void Dialog::receivedData(double val)
{
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if (key-lastPointKey > 0.002) // at most add point every 2 ms
{
QTextStream(stdout) << "HERE:" << data.toDouble() << endl;
customPlot->graph(0)->addData(key, data.toDouble());
customPlot->graph(0)->rescaleValueAxis();
...
}
}
void Dialog::handleReadyRead()
{
serialBuffer.append(serialPort.readAll());
int serPos;
while ((serPos = serialBuffer.indexOf('\n')) >= 0)
{
bool ok;
double tempValue = QString::fromLatin1(serialBuffer.left(serPos)).toDouble(&ok);
if (ok) emit newData(tempValue);
serialBuffer = serialBuffer.mid(serPos+1);
}
}
So keep the graph responsive to events (received a new data) instead of to a timer.
One more thing: I removed the port change on purpose. I suggest you to handle it in another way: put a button to start and stop the serial, and when the serial port is started prevent the user from changing the port name. This way the user will explicitely need to shut it down when he needs to change the port. If you want your version, however, don't include it in your code, but make a slot on its own to call whenever you need to change the port name:
void changeSerialPortName(QString newName)
{
if (newName != serialPort.portName()) {
if (serialPort.isOpen())
serialPort.close();
serialPort.setPortName(newName);
if (!serialPort.open(QIODevice::ReadOnly)) {
return;
}
}
}

Qt pass additional argument to slot AND keep emitted signal data

I have searched the web on this issue and I've repeatedly got answers referring to the use of QSignalMapper. But my problem is pretty clear, QSignalMapper automatically gets rid of whatever is originally emitted and replaces it with basically nothing, plus the new data that is set via setMapping().
The problem here is simple.
I have a QNetworkAccessManager that parses html and updates a vector containing text data:
void DataManager::startHttpRequest(QString url, int index)
{
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
//QSignalMapper* signalMapper = new QSignalMapper(this);
//connect(manager,SIGNAL(finished(QNetworkReply*)), signalMapper,SLOT(map()));
//signalMapper->setMapping(manager, index);
//connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(insertUpdate(int)));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishHttpRequest(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl(url)));
qDebug() << index;
}
and here is what happens when the request is finished, the normal way:
void DataManager::finishHttpRequest(QNetworkReply *reply)
{
QString html = QString(reply->readAll()).simplified();
QString info;
int start = html.indexOf("<span id=\"SalePrice\" >");
if(start != -1)
{
QString price = html.mid(start + 23, 30);
int end = price.indexOf("</span>");
info = price.mid(0, end - 1);
qWarning() << price.mid(0, end - 1);
}
else
{
info = "NA";
}
// Do more stuff
}
Using the normal way of signals and slots, I would not be able to know the index of the vector I am updating,
Or,
If I am using QSignalMapper, I know the index, but not the data that comes with it.
How do I get BOTH working (index + data)?
(something like mySlot(QNetworkReply *reply, int *index), but we all know that won't work)
Many thanks in advance.
While it's probably not the best,
sender()->setObjectName(const QString & name) allows the sender to name itself.
The sender's name can be accessed from the receiving slot via sender()->ObjectName()
As documented on http://qt-project.org/doc/qt-5/qobject.html#objectName-prop.

Windows Form Show vs ShowDialog

I have a small application that I am trying to create for windows. I am running into an issue with mixing a background thread designed to process some data. This background engine someone needs to both update the application gui (A windows Form) and get information from it.
Here is the basic application main.
int main() {
Engine engine;
Gui g;
engine.run(); // creates a new thread for engine logic
g.ShowDialog();
bool running = false;
while(1)
{
// Update gui with information from the engine
g.update(engine.GetState());
// transition to running
if(g.isRunning() && !running)
{
engine.play();
running = true;
}
// transition to stopped
else if(!g.isRunning() && running)
{
engine.stop();
running = false;
}
}
}
My main problem comes from the fact that Gui class is managed. See Class declaration below.
public ref class Gui : public System::Windows::Forms::Form
I am not really able to mix these two things, at first I had wanted to just throw the Engine into the Gui but that doesn't work since it is unmanaged.
You will note that the problem here is calling ShowDialog() as this makes the dialog modal and no code afterwards is executed. However, if I use Show() ... the Gui simply doesn't update or process any inputs.
SOLUTION:
I created a background worker in the Gui class so the engine is contained within the Gui but is ran on another thread.
void InitializeBackgoundWorker()
{
this->backgroundWorker1 = gcnew System::ComponentModel::BackgroundWorker;
backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &Gui::backgroundWorker1_DoWork );
backgroundWorker1->RunWorkerAsync( );
}
delegate void UpdateCallback(int hp, int maxhp);
void UpdateGui(int hp, int maxhp)
{
this->playerHealthBar->Value = ((float)(hp)/(float)(maxhp) * 100.0f);
};
void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
{
aBotEngine engine;
while(true)
{
engine.runnable(NULL);
array<Object^>^args = gcnew array<Object^>(2);
args[0] = engine.getPlayerHp();
args[1] = engine.getPlayerMaxHp();
this->playerHealthBar->Invoke(gcnew UpdateCallback(this, &Gui::UpdateGui), args);
}
};
As far as I can tell this is the proper way to have background thread that updates your windows form. I'm sure its not the only way.
void InitializeBackgoundWorker()
{
this->backgroundWorker1 = gcnew System::ComponentModel::BackgroundWorker;
backgroundWorker1->DoWork += gcnew DoWorkEventHandler( this, &Gui::backgroundWorker1_DoWork );
backgroundWorker1->RunWorkerAsync( );
}
delegate void UpdateCallback(int hp, int maxhp);
void UpdateGui(int hp, int maxhp)
{
this->playerHealthBar->Value = ((float)(hp)/(float)(maxhp) * 100.0f);
};
void backgroundWorker1_DoWork( Object^ sender, DoWorkEventArgs^ e )
{
aBotEngine engine;
while(true)
{
engine.runnable(NULL);
array<Object^>^args = gcnew array<Object^>(2);
args[0] = engine.getPlayerHp();
args[1] = engine.getPlayerMaxHp();
this->playerHealthBar->Invoke(gcnew UpdateCallback(this, &Gui::UpdateGui), args);
}
};