While coding a seemingly simple part of a Qt application that would run a subprocess and read data from its standard output, I have stumbled upon a problem that has me really puzzled. The application should read blocks of data (raw video frames) from the subprocess and process them as they arrive:
start a QProcess
gather data until there is enough for one frame
process the frame
return to step 2
The idea was to implement the processing loop using signals and slots – this might look silly in the simple, stripped-down example that I provide below, but seemed entirely reasonable within the framework of the original application. So here we go:
app::app() {
process.start("cat /dev/zero");
buffer = new char[frameLength];
connect(this, SIGNAL(wantNewFrame()), SLOT(readFrame()), Qt::QueuedConnection);
connect(this, SIGNAL(frameReady()), SLOT(frameHandler()), Qt::QueuedConnection);
emit wantNewFrame();
}
I start here a trivial process (cat /dev/zero) so that we can be confident that it will not run out of data. I also make two connections: one starts the reading when a frame is needed and the other calls a data handling function upon the arrival of a frame. Note that this trivial example runs in a single thread so the connections are made to be of the queued type to avoid infinite recursion. The wantNewFrame() signal initiates the acquisition of the first frame; it gets handled when the control returns to the event loop.
bool app::readFrame() {
qint64 bytesNeeded = frameLength;
qint64 bytesRead = 0;
char* ptr = buffer;
while (bytesNeeded > 0) {
process.waitForReadyRead();
bytesRead = process.read(ptr, bytesNeeded);
if (bytesRead == -1) {
qDebug() << "process state" << process.state();
qDebug() << "process error" << process.error();
qDebug() << "QIODevice error" << process.errorString();
QCoreApplication::quit();
break;
}
ptr += bytesRead;
bytesNeeded -= bytesRead;
}
if (bytesNeeded == 0) {
emit frameReady();
return true;
} else
return false;
}
Reading the frame: basically, I just stuff the data into a buffer as it arrives. The frameReady() signal at the end announces that the frame is ready and in turn causes the data handling function to run.
void app::frameHandler() {
static qint64 frameno = 0;
qDebug() << "frame" << frameno++;
emit wantNewFrame();
}
A trivial data processor: it just counts the frames. When it is done, it emits wantNewFrame() to start the reading cycle anew.
This is it. For completeness, I'll also post the header file and main() here.
app.h:
#include <QDebug>
#include <QCoreApplication>
#include <QProcess>
class app : public QObject
{
Q_OBJECT
public:
app();
~app() { delete[] buffer; }
signals:
void wantNewFrame();
void frameReady();
public slots:
bool readFrame();
void frameHandler();
private:
static const quint64 frameLength = 614400;
QProcess process;
char* buffer;
};
main.cpp:
#include "app.h"
int main(int argc, char** argv)
{
QCoreApplication coreapp(argc, argv);
app foo;
return coreapp.exec();
}
And now for the bizarre part. This program processes a random number of frames just fine (I've seen anything from fifteen to more than thousand) but eventually stops and complains that the QProcess had crashed:
$ ./app
frame 1
...
frame 245
frame 246
frame 247
process state 0
process error 1
QIODevice error "Process crashed"
Process state 0 means "not running" and process error 1 means "crashed". I investigated into it and found out that the child process receives a SIGPIPE – i.e., the parent had closed the pipe on it. But I have absolutely no idea of where and why this happens. Does anybody else?
The code is a bit weird looking (not using the readyRead signal and instead relying on delayed signals/slots). As you pointed out in the discussion, you've already seen the thread on the qt-interest ML where I asked about a similar problem. I've just realized that I, too, used the QueuedConnection at that time. I cannot explain why it is wrong -- the queued signals "should work", in my opinion. A blind shot is that the invokeMethod which is used by the Qt's implementation somehow races with your signal delivery so that you empty your read buffer before Qt gets a chance to process the data. This would mean that Qt will ultimately read zero bytes and (correctly) interpret that as an EOF, closing the pipe.
I cannot find the referenced "Qt task 217111" anymore, but there is a couple of reports in their Jira about waitForReadyRead not working as users expect, see e.g. QTBUG-9529.
I'd bring this to the Qt's "interest" mailing list anmd stay clear of the waitFor... family of methods. I agree that their documentation deserves updating.
Related
I have some code use qtcpsocket to write and read,
write-->sleep-->read;
and ui had 2 and more timer to use this function; by i want i run synchronous;so i add mutex to lock it; by it deadlock;
qt4; qt5;
void MainWindow::Start()
{
pTimer = new QTimer(this);
pTimer->setInterval(100);
connect(pTimer,SIGNAL(timeout()), this, SLOT(OnTimer()) );
pTimer->start();
pTimer2 = new QTimer(this);
pTimer2->setInterval(100);
connect(pTimer2,SIGNAL(timeout()), this, SLOT(OnTimer()) );
pTimer2->start();
}
void MainWindow::OnTimer()
{
FunCal(); // in my real code it will MyObj.DoSometing();
}
void MainWindow::FunCal()
{
qDebug()<<"log in fun...";
QMutexLocker loc(&this->_mtx);
qDebug()<<"getted lock in fun...";
QEventLoop loop;
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();
qDebug()<<"log out fun...";
}
I Want i run and out put as:
log in fun...
getted lock in fun ...
log out fun...
log in fun...
getted lock in fun...
log out fun...
but It Run like this:
log in fun...
getted lock in fun ...
log in fun....
---------------------------------no more ---------------------
IMHO the issue of OP results from a basic misunderstanding:
QTimer doesn't introduce multithreading.
It's just a facility to queue events which will sent after a certain time.
That's why, the QEventLoop is necessary to make it running at all.
However, it's still a deterministic execution and this is what probably happens inside the code of OP:
pTimer->start(); → starts first timer
pTimer2->start(); → starts second timer
control flow returns to event loop of QApplication (not exposed in code)
first timer becomes due and calls MainWindow::FunCal()
qDebug()<<"log in fun..."; → output of log in fun...
QMutexLocker loc(&this->_mtx); → this->_mtx becomes locked
qDebug()<<"getted lock in fun..."; → output of getted lock in fun...
loop.exec(); → enter a nested event loop (Nested event loops are allowed in Qt.)
second timer becomes due and calls MainWindow::FunCal() (Please, remember that it was immediately started after first with same interval time.)
qDebug()<<"log in fun..."; → output of log in fun...
QMutexLocker loc(&this->_mtx); → PROBLEM!
To illustrate it further, imagine the following call stack at this point (above called below):
QApplication::exec()
QEventLoop::exec()
QEventLoop::processEvents()
QTimer::timerEvent()
QTimer::timeOut()
MainWindow::onTimer()
MainWindow::FunCal()
QEventLoop::exec()
QTimer::timerEvent()
QTimer::timeOut()
MainWindow::onTimer()
MainWindow::FunCal()
QMutexLocker::QMutexLocker()
QMutex::lock()
(Note: In reality, you will see much more entries in the call-stack which I considered as irrelevant details in this case.)
The problem is: This second call of MainWindow::FunCal() cannot lock the mutex because it is already locked. Hence, the execution is suspended until the mutex is unlocked but this will never happen. The locking of mutex happened in the same thread (in the first/outer call of MainWindow::FunCal()). Unlocking would require to return from this point but it cannot because it's suspended due to the locked mutex.
If you think this sounds like a cat byting into its own tail – yes, this impression is right. However, the official term is Deadlock.
The usage of a QMutex doesn't make much sense as long as there are no competing threads. In a single thread, a simple bool variable would do as well because there are no concurrent accesses possible in a single thread.
Whatever OP tried to achieve in this code: Concerning the event-based programming forced/required by Qt, the problem is simply modeled wrong.
In single threading, a function cannot be entered twice accept by
a (direct or indirect) recursive call
a call out of a triggered interrupt handler.
Leaving the 2. aside (irrelevant for OPs Qt issue), the recursive call happens explicitly due to establishing a second (nested) event loop. Without this, the whole (mutex) locking is unnecessary and should be removed as well.
To understand event-based programming in general – it's described in the Qt doc. The Event System.
Additionally, I found Another Look at Events by Jasmin Blanchette which IMHO gives a nice little introduction into how the Qt event-based execution works.
Note:
Event-based programming can become confusing as soon as the amount of involved objects and signals becomes large enough. While debugging my Qt applications, I noticed from time to time recursions which I didn't expect.
A simple example: A value is changed and emits a signal. One of the slots updates a Qt widget which emits a signal about modification. One of the slots updates the value. Hence, the value is changed and emits a signal...
To break such infinite recursions, a std::lock_guard might be used with a simple DIY class Lock:
#include <iostream>
#include <mutex>
#include <functional>
#include <cassert>
// a lock class
class Lock {
private:
bool _lock;
public:
Lock(): _lock(false) { }
~Lock() = default;
Lock(const Lock&) = delete;
Lock& operator=(const Lock&) = delete;
operator bool() const { return _lock; }
void lock() { assert(!_lock); _lock = true; }
void unlock() { assert(_lock); _lock = false; }
};
A sample object with
a property-like member: bool _value
a simplified signal emitter: std::function<void()> sigValueSet
and a lock used to prevent recursive calls to setValue(): Lock _lockValue
// a sample data class with a property
class Object {
private:
bool _value; // a value
Lock _lockValue; // a lock to prevent recursion
public:
std::function<void()> sigValueSet;
public:
Object(): _value(false) { }
bool value() const { return _value; }
void setValue(bool value)
{
if (_lockValue) return;
std::lock_guard<Lock> lock(_lockValue);
// assign value
_value = value;
// emit signal
if (sigValueSet) sigValueSet();
}
};
Finally, some code to force the lock into action:
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
DEBUG(Object obj);
std::cout << "obj.value(): " << obj.value() << '\n';
DEBUG(obj.sigValueSet = [&](){ obj.setValue(obj.value()); });
DEBUG(obj.setValue(true));
std::cout << "obj.value(): " << obj.value() << '\n';
}
To keep things short, I connected a slot to the signal which directly sets value again while the signal is emitted.
Output:
Object obj;
obj.value(): 0
obj.sigValueSet = [&](){ obj.setValue(obj.value()); };
obj.setValue(true);
obj.value(): 1
Live Demo on coliru
For a counter-example, I excluded the test if (_lockValue) return; and got the following output:
a.out: main.cpp:18: void Lock::lock(): Assertion `!_lock' failed.
Object obj;
obj.value(): 0
obj.sigValueSet = [&](){ obj.setValue(obj.value()); };
obj.setValue(true);
bash: line 7: 12214 Aborted (core dumped) ./a.out
Live Demo on coliru
This is similar to what happened in OPs case with the only difference that in my case double-locking just violated the assert().
To make this complete, I exluded the lock guard std::lock_guard<Lock> lock(_lockValue); as well and got the following output:
execution expired
Live Demo on coliru
The execution was trapped into an infinite recursion, and the online compiler aborted this after a certain time. (Sorry, coliru. I won't do it again.)
I get sine wave from server though TCP and plot it. Everything seems to be fine until I start sending something back at c>1000. After one byte sent, I still get data but the waveform of sine wave is changed. I'm sure that there are some missed data but I can't find bugs in my code. The transmission rate is about 1M bps.
The question is
When I write something to server, how it effects to socket?
Why the socket miss some data?
How can I fix it?
ssTcpClient::ssTcpClient(QObject *parent) :
QObject(parent)
{
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()),
this, SLOT(on_connected()));
connect(socket, SIGNAL(disconnected()),
this, SLOT(on_disconnected()));
}
void ssTcpClient::on_connected()
{
qDebug() << "Client: Connection established.";
connect(socket, SIGNAL(readyRead()),
this, SLOT(on_readyRead()));
in = new QDataStream(socket);
}
void ssTcpClient::on_readyRead(){
static quint32 c = 0;
qDebug() << "c" << c++;
QVector<quint8> data;
quint8 buf;
while(socket->bytesAvailable()>0){
//read data to buffer
*in >> buf;
data.append(buf);
}
//process data
emit data_read(data);
//if there are over 1000 data then send something back
if(c>1000){
char msg[10];
msg[0] = 'c';
socket->write(msg,1);
socket->flush();
}
}
You cannot rely on TCP traffic to be complete, data arrives in indeterminable chunks.
You are using QDataStream to read data from the socket. This is a really bad idea because QDataStream assumes that you have complete set of data. If there isn't enough data, it will silently fail.
I suggest you modify your data source so it either sends a byte count as the first thing, or it sends some kind of termination sequence that you can look out for to tell you that you have received enough to process.
Consider this thread, it acts like a timer, send some packet to serial:
void PlCThead::run()
{
while(1)
{
const char str[]={UPDATE_PACKET};
QByteArray built;
built.append(0x02);
built.append(0x05);
built.append(0x03);
emit requestForWriteAndReceive(built);
msleep(100);
}
}
emit works fine, it goes inside the slot, there, it writes only 78 or char x to serial instead of a packet of 3 bytes.
bool RS::rs_ThreadPlcDataAqustn(QByteArray byteArray)
{
QByteArray rd15Bytes;
char *data = byteArray.data();
int len = byteArray.length();
if(!rs_serialWrite(data, len))
{
qDebug() << "Failure:( rs_dataqustn: rs_plcWrite(data, len)";
emit plc_port_dscntd();
return false;
}
}
bool RS::rs_serialWrite(char* buff, size_t length)
{
int tries;
int len;
tries = 0;
QByteArray built((char*)buff, length);
qDebug() << built.toHex();
len = write(fd, buff, length);
qDebug() << len;
qDebug() << strerror(errno);
return true;
}
this is how fd created:
fd = open(portPath, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK, S_IWUSR | S_IRUSR | S_IXUSR);
this is how the thread created in mainwindow:
rs_plc->rs_plcOpenPort((char *)"/dev/ttyS0"); /*/dev/ttyS3*/
PlCThead *thread = new PlCThead();
connect(thread, SIGNAL(requestForWriteAndReceive(QByteArray)), rs_plc, SLOT(rs_ThreadPlcDataAqustn(QByteArray )));
thread->start();
rs_plc is a private member of MainWindow.
strerror returns back this warning:
> Resource temporarily unavailable
any ideas? this code works fine with timers, it has been checked and tested accurately, but now i need to add this thread instead of the timer. Thanks
Your question isn't complete enough for a full diagnosis since you aren't showing how fd is created, how the threads are set up (which you say is part of the problem), etc.
But... your resource temporarily unavailable line is a big hint. The write() function isn't succeeding to write everything because it's returning an error (probably EAGAIN or EWOULDBLOCK). The fd file descriptor is attached to something that either has a small buffer, no buffer, or a buffer which is already full. And it's full, and it's the applications job to not send it anything else until it can handle it. A common thing to do there is to sleep and then try the write again if the error code is EAGAIN or EWOULDBLOCK.
But, you said it's returning 3, which actually indicates "no error" too. And if that's the case then the error string won't be referring to write itself, and something else set errno previously. (which could have been write itself in the past).
In short, if this is getting called more than once (likely) you probably need to watch out for writing too fast (and it looks like a serial buffer, which definitely falls into the category of easy-to-fill-the-buffer).
In short: if it's not writing all the bytes to the fd than you want, it's because it can't handle more than that.
This likely has absolutely nothing to do with qt by the way. It's all about the write() call.
I use QProcess and connect it's readyReadStandardOutput to slot. But after starting slot execute twice. Tell me please why is it?
{
myProcess = new QProcess(parent);
myProcess->start("mayabatch.exe -file "+scene);
connect(myProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readOutput()));
}
void MainWindow::readOutput()
{
qDebug()<<"Read";
QByteArray outData = myProcess->readAllStandardOutput();
qDebug()<<QString(outData);
}
OUTPUT:
Read
"File read in 0 seconds.
"
Read
"cacheFi"
Read
"le -attachFile -fileName "nClothShape1" -directory ...
Last string was broken. "Read" appears between words.
From the documentation of QProcess::readyReadStandardOutput()
This signal is emitted when the process has made new data available through its standard output channel (stdout). It is emitted regardless of the current read channel.
The slot is executed more than once for the simple reason that the underlying process flushed the output in separate and random ways. You should not be caring about this because it depends on things you cannot control.
If you want to save the whole output you should be doing
void MainWindow::readOutput(){
bigbuffer.append(myProcess->readAllStandardOutput();)
}
If you want to read line by line, then
void MainWindow::readOutput(){
while(myProcess.canReadLine()){
qDebug() << myProcess.readLine();
}
}
The second call will leave data in the process buffer such that you don't have "broken" reads like cacheFi.
Does the POSIX standard allow a named shared memory block to contain a mutex and condition variable?
We've been trying to use a mutex and condition variable to synchronise access to named shared memory by two processes on a LynuxWorks LynxOS-SE system (POSIX-conformant).
One shared memory block is called "/sync" and contains the mutex and condition variable, the other is "/data" and contains the actual data we are syncing access to.
We're seeing failures from pthread_cond_signal() if both processes don't perform the mmap() calls in exactly the same order, or if one process mmaps in some other piece of shared memory before it mmaps the "/sync" memory.
This example code is about as short as I can make it:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/file.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <iostream>
#include <string>
using namespace std;
static const string shm_name_sync("/sync");
static const string shm_name_data("/data");
struct shared_memory_sync
{
pthread_mutex_t mutex;
pthread_cond_t condition;
};
struct shared_memory_data
{
int a;
int b;
};
//Create 2 shared memory objects
// - sync contains 2 shared synchronisation objects (mutex and condition)
// - data not important
void create()
{
// Create and map 'sync' shared memory
int fd_sync = shm_open(shm_name_sync.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
ftruncate(fd_sync, sizeof(shared_memory_sync));
void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0);
shared_memory_sync* p_sync = static_cast<shared_memory_sync*> (addr_sync);
// init the cond and mutex
pthread_condattr_t cond_attr;
pthread_condattr_init(&cond_attr);
pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&(p_sync->condition), &cond_attr);
pthread_condattr_destroy(&cond_attr);
pthread_mutexattr_t m_attr;
pthread_mutexattr_init(&m_attr);
pthread_mutexattr_setpshared(&m_attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&(p_sync->mutex), &m_attr);
pthread_mutexattr_destroy(&m_attr);
// Create the 'data' shared memory
int fd_data = shm_open(shm_name_data.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR);
ftruncate(fd_data, sizeof(shared_memory_data));
void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0);
shared_memory_data* p_data = static_cast<shared_memory_data*> (addr_data);
// Run the second process while it sleeps here.
sleep(10);
int res = pthread_cond_signal(&(p_sync->condition));
assert(res==0); // <--- !!!THIS ASSERT WILL FAIL ON LYNXOS!!!
munmap(addr_sync, sizeof(shared_memory_sync));
shm_unlink(shm_name_sync.c_str());
munmap(addr_data, sizeof(shared_memory_data));
shm_unlink(shm_name_data.c_str());
}
//Open the same 2 shared memory objects but in reverse order
// - data
// - sync
void open()
{
sleep(2);
int fd_data = shm_open(shm_name_data.c_str(), O_RDWR, S_IRUSR|S_IWUSR);
void* addr_data = mmap(0, sizeof(shared_memory_data), PROT_READ|PROT_WRITE, MAP_SHARED, fd_data, 0);
shared_memory_data* p_data = static_cast<shared_memory_data*> (addr_data);
int fd_sync = shm_open(shm_name_sync.c_str(), O_RDWR, S_IRUSR|S_IWUSR);
void* addr_sync = mmap(0, sizeof(shared_memory_sync), PROT_READ|PROT_WRITE, MAP_SHARED, fd_sync, 0);
shared_memory_sync* p_sync = static_cast<shared_memory_sync*> (addr_sync);
// Wait on the condvar
pthread_mutex_lock(&(p_sync->mutex));
pthread_cond_wait(&(p_sync->condition), &(p_sync->mutex));
pthread_mutex_unlock(&(p_sync->mutex));
munmap(addr_sync, sizeof(shared_memory_sync));
munmap(addr_data, sizeof(shared_memory_data));
}
int main(int argc, char** argv)
{
if(argc>1)
{
open();
}
else
{
create();
}
return (0);
}
Run this program with no args, then another copy with args, and the first one will fail at the assert checking the pthread_cond_signal().
But change the order of the open() function to mmap() the "/sync" memory before the "/data" and it will all work fine.
This seems like a major bug in LynxOS to me, but LynuxWorks claim that using mutex and condition variable within named shared memory in this way is not covered by the POSIX standard, so they are not interested.
Can anyone determine if this code does actually violate POSIX?
Or does anyone have any convincing documentation that it is POSIX compliant?
Edit: we know that PTHREAD_PROCESS_SHARED is POSIX and is supported by LynxOS. The area of contention is whether mutexes and semaphores can be used within named shared memory (as we have done) or if POSIX only allows them to be used when one process creates and mmaps the shared memory and then forks the second process.
The pthread_mutexattr_setpshared function may be used to allow a pthread mutex in shared memory to be accessed by any thread which has access to that memory, even threads in different processes. According to this link, pthread_mutex_setpshared conforms to POSIX P1003.1c. (Same thing goes for the condition variable, see pthread_condattr_setpshared.)
Related question: pthread condition variables on Linux, odd behaviour
I can easily see how PTHREAD_PROCESS_SHARED can be tricky to implement on the OS-level (e.g. MacOS doesn't, except for rwlocks it seems). But just from reading the standard, you seem to have a case.
For completeness, you might want to assert on sysconf(_SC_THREAD_PROCESS_SHARED) and the return value of the *_setpshared() function calls— maybe there's another "surprise" waiting for you (but I can see from the comments that you already checked that SHARED is actually supported).
#JesperE: you might want to refer to the API docs at the OpenGroup instead of the HP docs.
May be there is some pointers in pthread_cond_t (without pshared), so you must place it into the same addresses in both threads/processes. With same-ordered mmaps you may get a equal addresses for both processes.
In glibc the pointer in cond_t was to thread descriptor of thread, owned mutex/cond.
You can control addresses with non-NULL first parameter to mmap.