in my application
I've a MainWindow (which is a QtMainWindow class) and a Acquisiton class (which is a QThread class)
Here my very simplified Acquisiton class
//entry point of the thread
void Acquisition::run ()
{
uint8_t* image_addr;
QSharedPointer<uint8_t> image(new uint8_t[IMG_SIZE]);
for (;;)
{
if (isInterruptionRequested())
return;
// here, usb_read() give me the adress of a elem in the ring buffer
img_addr = usb_read(...);
// the ring buffer can possibly be rewritten on the next usb_read() (if bufferlength = 1) so I copy the data into my QSharedPointer
std::memcpy(image.data(), image_addr, sizeof(IMG_SIZE));
// I send this image
emit imageSent(image);
}
}
and in my MainWindow I've
// the slot for the signal imageSent
void newImage(QSharedPointer<uint8_t> image)
{
// process and draw image
}
I don't understand the lifecycle of the QSharedPointer (and std::shared_ptr (imagine the samecode with std::shared_ptr)
Does my QSharedPointer is always valid ?
What append if during processing (MainWindow), the usb_read() occurs and the memcpy write on my image.
In a related question: Waiting slots to be executed before quitting
I see that QSharedPointer keeps my data valid if the acquisition threads stop during data is processing.
In this case, is my signal canceled, my values are copied somewhere or the thread wait for my MainWindow to finish processing ?
Thanks
As it was already written in Resurrection's answer shared pointers are valid as long as they are at least referenced at one location.
In your case you will only have once instance of the shared pointer, which is the one you create at the start of the Acquisition thread. It is referenced in the Acquisition thread as well as in the signal handlers that will be called by QT. As you have only one shared pointer (with one byte array in it) you are now updating the same data buffer on each acquisition and overwrite it, potentially at the same moment when another thread has not yet read it. You can however easily fix that by creating a new shared pointer instance for each sample and pass that one to the other thread in the signal.
The following small change should do it:
//entry point of the thread
void Acquisition::run ()
{
uint8_t* image_addr;
for (;;)
{
if (isInterruptionRequested())
return;
// here, usb_read() give me the adress of a elem in the ring buffer
img_addr = usb_read(...);
// Create a fresh shared pointer in the scope
QSharedPointer<uint8_t> image(new uint8_t[IMG_SIZE]);
// the ring buffer can possibly be rewritten on the next usb_read() (if bufferlength = 1) so I copy the data into my QSharedPointer
std::memcpy(image.data(), image_addr, sizeof(IMG_SIZE));
// I send this image
emit imageSent(image);
}
}
And regarding the cancellation and signaling:
When you call emit signals between different threads in QT then by default a queued connection will be used. This means on the emitting thread the data and the handler that should be called will be put in a queue. The data here is your shared pointer. The queue will held it alive, even if the acquisition thread finishes. Then when the other thread kicks in (MainThread, etc.) the data will be dequeued and the signal handler will be called with it.
Does my QSharedPointer is always valid?
Only after you copy the data to it but after that yes, it will be valid as long as any instance of it exists so as long as your object of type Acquisition exists.
What append if during processing (MainWindow), the usb_read() occurs
and the memcpy write on my image.
Race condition. You would have to use a mutex to lock the resource when processing in the MainWindow. Smart pointers are not inherently thread safe however QSharedPointer uses atomic integer for reference counting so sharing is thread safe. Again, the content is not!
In this case, is my signal canceled, my values are copied somewhere or
the thread wait for my MainWindow to finish processing ?
This depends on how you connect your objects. By default when two QObjects live in two different threads the connection is automatically Qt::QueuedConnection and in that case the arguments are first copied (even if sent as const reference) internally to be posted as event in the receiver's thread. This requires the argument to be copyable and the receiver's thread to be running an event loop. However if you for some reason do Qt::DirectConnection which is default for connection in the same thread it will be equivalent to direct call. This may happen in your case if you have connected the two objects before you moved one of them to a different thread (however maybe Qt does switch all connections to queued ones when QObject::moveToThread is called).
So to answer directly, when queued signal is used the arguments are copied and life time of the caller does no longer matter after the emit.
Related
tl;dr:
class Controller
{
public:
volatile Netconsole* nc;
void init(); //initialize the threads
void calculate(); // handler for the "mothership app"
void senderThreadLoop(); //also calls reinitNet() if connection is broken.
void listenerThreadLoop();
inline void reinitNet(){ delete nc; nc = new Netconsole(); }
}
// inside
Json::Value header = nc->Recv();
error: passing 'volatile Netconsole' as 'this' argument discards qualifiers [-fpermissive]
Pointer to an instance of a utility class (Netconsole) shared between two threads must be updated inside both threads if the utility class is re-instantiated, but declaring it as volatile generates the above error. If it's updated just inside one thread, the other thread may still use old, invalid pointer. How to assure it's updated in both but using methods through the pointer doesn't trigger the above error?
Extended info:
The "smart glue logic" library I'm writing is used to pass and convert messages between a 3rd party software and a custom device. It consists of three essential threads:
a handler: the main thread of the 3rd party app periodically calls a "calculate" function in my library to handle new updates - data to send, data received
a sender thread that converts and sends whatever the handler pushed into the send buffer
a listener thread that converts and pushes any data received from the device into receive buffer.
Both the sender and the listener threads use the same utility class that handles network communication with the device; upon initialization the class creates a connection to the device, and the two threads perform blocking reads or await for new data to send respectively. In case of any problems, the sender thread performs all "maintenance" work, while the listener thread enters a safe state awaiting return of connectivity.
Now, since the two threads share one connection to the device, they both share the same instance of the communication class, as a pointer to that class.
The problem is in the procedure of reconnect - it involves destroying and creating the helper class instance exploiting safe shutdown and initialization already present in the destructor and constructor. As result the pointer changes. Without volatile it's quite likely the listener won't receive the updated pointer. With volatile, it protests - needlessly, because nc (the pointer) won't change at a random moment - first the listener is notified of a problem, then it enters a safe state where it doesn't perform any operations on 'nc' and notifies the sender it's ready. Only then the sender performs the repair and notifies the listener to resume normal operation.
So what's the right solution in this situation?
What you need is a sequence of operations. The producing thread has 2 relevant operations : "initialize new Netconsole" and "write pointer". The consuming thread also has two operations: "read pointer" and "use new Netconsole object". Those 4 operations must be sequenced in exactly that order for the update to be visible.
By far the simplest way to achieve this are two memory barriers. A write barrier (std::memory_order_release on the pointer write) prevents the first two operations from being reordered, and the read barrier (std::memory_order_acquire on the pointer load) prevents the last two operations from being reordered.
As the two threads run independently, your program correctness shouldn't depend on whether a particular object update happened before a particular object use. The updating thread might just have been a bit slow, and that should not break your program. So the third ordering between write and read isn't really relevant and you shouldn't try to "fix" it.
To summarize: Yes, the 4 operations have to happen in exactly the right order for the result to be visible, but if the second and third operation are
reordered then the update is perfectly invisible to the consuming thread. It's an atomic update, all or nothing.
There's still a matter of cleaning up the old object. The producing thread cannot just assume that the consuming thread has already seen the pointer update. There must be synchronization to ensure both threads agree that the old object is unused. The easiest is if the producing thread strictly does not use the old object after the new object has been created (the memory barrier helps here), and the consuming thread cleans up the old object as soon as it knows there's a new object (because that happens strictly after the read barrier, thus after the write barrier and in turn after the last use by the producing thread)
Consider that object A is bound to thread T1 and a signal sig1 with an argument of QList < QVariantMap > is fired from thread T2.
The signal is queued on A's event loop, but before handling, A's thread (T1) is quit. The QList< QVariantMap > argument of sig1 is obviously allocated on the stack, I'm guessing that it should clean up itself.
But when? Perhaps when the QThread object itself is destroyed? Does anyone have specific knowledge in this area? In my system, this thread will not simply run once and end at shutdown. It may potentially be started, run for hours, and then quit and be started again later on.
The argument will not be deleted if the thread has been terminated in a correct way like
QThread t;
t.exit();
t.wait();
Now you assert that
The QList< QVariantMap > argument of sig1 is obviously allocated on
the stack
It is not that obvious. Because the receiving thread need to use the data in a deferred execution, emit(myVariantList) will eventually lead to a copy of myVariantList. You don't know where the copy is going to be allocated.
Now to allow posting events with eventual data , ie the classic
Mywidget m;
m.show(); <-- this post at least one event
app.exec();
Those posted events need to be saved just in case of an eventual execution of the event loop. I believe that if the object holding the event loop is not destroyed or the user doesn't explicitly ask for events to be removed, they stay for the lifetime of the QObject.
The last weeks I read a lot about RAII and thought that I should start using smart pointers in my applications. As an example I tried to modify one of my applications. It captures frames from a webcam in a thread, performes image processing in another thread and displays the processed and unprocessed images in QT widgets. One central Object is the CCameraHandler which controls the capturing thread and image processing thread. Up to this point I used 4 plain pointers as members in this class:
CCameraCapture* m_CameraCapture;
CImageProcessor* m_ImageProcessor;
QThread* m_CameraCaptureThread;
QThread* m_ProcessingThread;
In the constructor of CCameraHandler I created the Instances using new and moved the capture object to the thread:
m_CameraCaptureThread= new QThread();
m_CameraCapture= new CCameraCapture();
//Move camera capture object to thread
m_CameraCapture->moveToThread(m_CameraCaptureThread);
That approach worked nicely. Now I wanted to a first test with QScopedPointer and tried to change m_CameraCapture to a QScopedPointer using
QScopedPointer<CCameraCapture> m_CameraCapture;
and initializing it with CameraCapture(new CCameraCapture()) in the initialization list. It compiled nicely and works as it should but when I close the application an the destructors are called I get an error from Qt:"Cannot send events to objects owned by a different thread. Current thread 5ff590. Receiver '' (of type 'CCameraCapture') was created in thread 4b7780" I guess that it has to do with the m_CameraCapture->moveToThread(m_CameraCaptureThread); where I now move a scoped pointer. Is the QScopedPointer automatically parented by CCameraCapture? So far I used
//This connections guarantees that the m_CCameraCapture and m_CameraCapture are deleted after calling QThread::exit()
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
to delete thread an worker when camera capturing is stopped. If m_CameraCapture is now parented by CCameraHandler that might cause the problems. At the moment I am not so sure if it is a good Idea to use a SmartPointer in this case. Any Ideas what might cause this error on destruction?
Edit:
The CCameraHandler dtor looks like this (threads should be deleted before the worker):
CCameraHandler::~CCameraHandler(void)
{
//Stop grabbing and processing
emit stopGrabbing();
emit stopProcessing();
/*Wait for the capture thread to terminate. The destructor of CCamera Handler might be called on application close. Therefore it is important to wait for QThreads to terminate. Else the application might close before threads get deleted*/
m_CameraCaptureThread->exit();
m_CameraCaptureThread->wait();
//Wait for the processing thread to terminate
m_ProcessingThread->exit();
m_CameraCaptureThread->wait();
qDebug() << "CCameraHandler deleted";
}
An object that has been moved to another thread must be destructed either:
From the thread itself, or
From any thread after the thread itself has been destructed.
Caveat: QThread is not safe to be destructed before you stop it. To do that safely to a thread that merely runs an event loop, you should use the following subclass instead:
class Thread : public QThread {
using QThread::run; // final
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
};
Given such a class, destructed from the GUI thread, you simply need to destruct it before you destruct any objects that were moved to the thread. Of course, it's not necessary to hold such objects as pointers at all, but the code below will work whether you hold them directly or as pointers.
class Foo : public Bar {
CCameraCapture m_CameraCapture;
CImageProcessor m_ImageProcessor;
Thread m_CameraCaptureThread;
Thread m_ProcessingThread;
...
}
When the class is destructed, the following happens, in order:
~Foo() body runs (it may be empty).
Members in the ... section, if any, are destructed in reverse order of declaration.
m_ProcessingThread.~Thread runs, followed by superclass destructors (~QThread, and finally ~QObject). Any objects that were moved to that thread are now threadless.
m_CameraCaptureThread.~Thread runs, followed by superclass destructors. Any objects that were moved to that thread are now threadless.
m_ImageProcessor destructors run. As a threadless object, the destruction is safe from any thread.
m_CameraCapture destructors run. As a threadless object, the destruction is safe from any thread.
If you used QScopedPointer<...> to hold those instances, things would be exactly the same, just that every object's destruction would be wrapped in the body of ~QScopedPointer<...>.
Note that the use of even a raw pointer to hold those instances is a premature pessimization: you waste a bit of heap, and access to the instances is a bit slower due to an extra layer of indirection. Those things in isolation may not play a big role, but if there are thousands of objects all coded that way, things can add up.
Don't allocate class members in individual heap blocks unless absolutely necessary.
Problem is that you are doing some UI stuff from none UI thread.
It is hard to tell exactly where is the problem since you didn't give information what exacly CCameraCapture do.
I suspect that after capturing a frame you are setting a pixmap on label (to show frame) instead emit a signal with new frame and connect this signal with respective slot of UI element. So I think that scoped pointer and signal and slots have nothing to do with your problem, problem is that you didn't use signal slot mechanism in place where it was required.
The gripe I have with this otherwise good example: https://www.qt.io/blog/2006/12/04/threading-without-the-headache is that it is exchanging naked pointers and it is not using Qt::QueuedConnection.
Edit: here is the code snippet the above link shows (in case the link goes down before this post)
// create the producer and consumer and plug them together
Producer producer;
Consumer consumer;
producer.connect(&consumer, SIGNAL(consumed()), SLOT(produce()));
consumer.connect(&producer, SIGNAL(produced(QByteArray *)), SLOT(consume(QByteArray *)));
// they both get their own thread
QThread producerThread;
producer.moveToThread(&producerThread);
QThread consumerThread;
consumer.moveToThread(&consumerThread);
// go!
producerThread.start();
consumerThread.start();
If I used a unique_ptr in the producer, releasing it when I call the produced signal and directly put the naked pointer into another unique pointer in the connected consume slot it would be somewhat safer. Especially after some maintenance programmer has a go at the code ;)
void calculate()
{
std::unique_ptr<std::vector<int>> pi(new std::vector<int>());
...
produced(pi.release());
//prodiced is a signal, the connected slot destroys the object
//a slot must be connected or the objects are leaked
//if multiple slots are connected the objects are double deleted
}
void consume(std::vector<int> *piIn)
{
std::unique_ptr<std::vector<int>> pi(piIn);
...
}
this still has a few major problems:
I am not protecting against leaks when the slot is not connected
I am not protecting against double deletes if multiple slots were to be connected (should be a logic error on the part of the programmer if it happens, but I would like to detect it)
I don't know the inner working of Qt well enough to be sure that nothing leaks in transit.
If I were to use a shared pointer to const it would solve all my problems but be slower and as far as I know I would have to register it with the meta object system as described here: http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum is this a good idea?
Is there a better way of doing this that I'm not thinking of?
You shouldn't pass pointers in a signal while expecting a slot to destroy them, because the slot may not be available.
Pass a const reference instead, allowing the slot to copy the object. If you use Qt's container classes, this should not hinder performance, as Qt's container classes implement copy-on-write.
I have 3 objects(inherited from QObject) that each contain a separate std::list. Each object gets created in the main gui thread (with no parent) and then is pushed to it's own thread (using Qt's QObject::moveToThread()).
Each thread is hooked up to a gui and messages are sent between the different threads with data. Each thread is to essentially handle it's own list. For example:
Obj 1 : Consumer of data. It pop's the front off of its list(if data is present) to use. It also has a SLOT available so that other threads can push data to it. No other object can access this list directly only the the original QObject class.
Obj 2 : Producer of data. It pushes data to its list. It has SLOTS available for others to 'ping' it for data which will in turn emit a SIGNAL popping data from its list. No other object can access this list directly.
Obj 3: Produces data for obj 1 and consumes data from obj 2. It has it's own internal data structures that keep track of the data sent to obj 1 and the data coming from obj 2. It finally will push both data sets to some QwtPlots after it does some analysis.
Obj's 1 and 2 are real-time critial and use QueryPerformanceCounter style 'timing' which will essentially suck down a CPU each while they're running. They run QCoreApplication::processEvents() every loop to handle the events that come down through.
Is this an okay way to handle cross-thread data sharing? If it isn't, where are the holes and how would you correct them? I understand this will create a lot of 'copies' of data flying around, but memory bloat isn't a concern at this point.
thanks in advance :)
It's hard to say exactly whether it's thread-safe or not without all the implementation details as there are a lot of things that can go wrong when using threads.
Obj 1 : Consumer of data. It pop's the front off of its list(if data is present) to use. It also has a SLOT available so that other threads can push data to it. No other object can access this list directly only the the original QObject class.
If this slot is connected to signals in other threads (such as Obj 3) using queued or auto connection type, then the Obj 1 is probably safe. If the slot is called directly from other threads, then it obviously isn't thread safe unless you explicitly synchronize everything.
Obj 2 : Producer of data. It pushes data to its list. It has SLOTS available for others to 'ping' it for data which will in turn emit a SIGNAL popping data from its list. No other object can access this list directly.
You don't mention how "pinging" is implemented or which threads call those slots. If other threads call them directly and if pinging involves accessing the internal std::list, then you're in trouble. If those slots are only called via queued or auto connections (to some signal in Obj 3, for example), then it's fine. If those slots are thread safe (for example, they only put a "ping" message into some sort of internal synchronized message queue), then it's fine too. The latter way looks like custom reimplementation of the queued connection mechanism, though.
Overall, this whole thing looks too dangerous to me as slots can be called from anywhere by mistake. I'd try to avoid this kind of thing by putting some safety checks there, like this:
void Obj2::ping() {
if (QThread::currentThread() != this->thread()) {
// not sure how efficient it is
QMetaObject::invoke(this, "ping", Qt::QueuedConnection);
return;
}
// thread unsafe code goes here
}