What is the reason asio doesn't do any delay? - asynchronous

I am new to asio.
Here is guide I was following writing my daytime tcp-server: https://think-async.com/Asio/asio-1.18.1/doc/asio/tutorial/tutdaytime3.html . I was trying to reproduce a reasonable example that would show that asunchronous code is actually asynchronous. I didn't modify anything else, just small piece of code in tcp_server class. I am adding this delay in order to see that after we are waiting timer to expire, we can gracefully handle other client connections/requests. So, did I miss something? Because in my case delay basically doesn't work ;(
void handle_accept(const tcp_connection::pointer &new_connection,
const asio::error_code &error) {
asio::steady_timer timer(io_context_, asio::chrono::seconds(5));
std::cout << "Before timer" << std::endl;
timer.async_wait(std::bind(&tcp_server::handle_wait, this, error, new_connection));
}
void handle_wait(asio::error_code &ec, const tcp_connection::pointer &new_connection) {
if (!ec) {
new_connection->start();
}
std::cout << "Inside timer" << std::endl;
start_accept();
}

void handle_accept(const tcp_connection::pointer &new_connection,
const asio::error_code &error) {
asio::steady_timer timer(io_context_, asio::chrono::seconds(5));
std::cout << "Before timer" << std::endl;
timer.async_wait(std::bind(&tcp_server::handle_wait, this, error, new_connection));
}
The timer is a local variable.
async_wait returns immediately, without blocking.
The function exits, destructing the timer. The timer cancels all pending operations (with error::operation_aborted).
🖛 Make sure the lifetime of the timer extends well enough to allow for it expire (or at least until it stops being relevant). In your case there will probably already be a tcp::acceptor member in your class, and the timer could reside next to it.

Related

Strange signal/slot behaviour

(Working with Qt 5.15.2)
I have a method which receives a signal (sig_responseReady) from the same thread. The purpose of this method is to wait until a response is received. While this method is waiting for the signal, the method is called again because of an event which calls this method (so eventloops get nested). However, the first waitForResponse on the stack does not get the sig_responseReady signal for up to 10 seconds (for no reason I can see), while later called waitForResponse get their signal first.
The result of this design is that I have nested eventloops, which according to this stackoverflow post causes slot/signal handling errors, and should be avoided. I suspect this is the cause of my problem.
Is there a design which accomplishes the same, with only a single eventloop? (I can't come up with it) The function which calls waitForResponse must pause until waitForResponse returns. ..I can't make it asynchronous. I tried replacing eventloop.exec with QCoreApplication::processEvents(QEventLoop::AllEvents,100) in the function below, but still get same strange result. (So maybe nested event loops is not the problem...but I can't figure out the cause)
MyClass::SRequest MyClass::waitForResponse(const QUuid queryID) {
reentryCount++; // Static var to determing nesting/depth count
QEventLoop eventloop;
qDebug() << "MyClass::waitForResponse connect for uid " << queryID << ", depth " << reentryCount;
connect(this,&MyClass::sig_responseReady,&eventloop, &QEventLoop::quit); //, Qt::QueuedConnection); // Allow matching response to stop eventloop
do {
eventloop.exec();
qDebug() << "MyClass::waitForResponse got signal for uid " << queryID;
} while (!queryResponseReady(queryID) && !queryExpired(queryID));
qDebug() << "MyClass::waitForResponse exitted loop for signal for uid " << queryID << ", depth " << reentryCount;
reentryCount--;
return queryTakeResult(queryID);
}

How to fix "In Qt two timer to one function, use qmutex will qeventloop for sleep"

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.)

QTimer slot triggered for multiple time?

I'm creating a UI application for Raspberry PI to read data from sensor on definite timeout (5 seconds). Problem is the QTimer timeout slot is called for multiple times
{ //at system init
readTempCur = new QTimer(this);
connect(readTempCur, SIGNAL(timeout()), this, SLOT(readSensor()));
readTempCur->start(SAMPLINGTIME);
readSensor(); //added to call on boot itself, can be removed
}
void HomePage::readSensor(void) {
readTempCur->stop();
qDebug() << "Read Sensor triggerred at " <<QDateTime::currentDateTime().toString();
//DO my actions
readTempCur->start(SAMPLINGTIME);
}
[edit for answer]
The most probable case for such issue is conneccting the slot to the signal that already conneccted; this will trigger slot for 'n' number times it got connected, design should take care not to reconnect again.
The QTimer::start function will start/restart the timer.
Your readSensor function stops the timer and then restart it again.
remove the start to fix it.
void HomePage::readSensor(void) {
readTempCur->stop();
qDebug() << "Read Sensor triggerred at " <<QDateTime::currentDateTime().toString();
//DO my actions
//readTempCur->start(SAMPLINGTIME);
}
P.S.
If you want to run the timer once you can use the singleShoot
QTimer::singleShot(SAMPLINGTIME, this, SLOT(readSensor()));
Do not stop nor restart the timer in readSensor(). Just do:
void HomePage::readSensor(void)
{
qDebug() << "Read Sensor triggerred at " <<QDateTime::currentDateTime().toString();
//DO my actions
}
Also, make sure SAMPLINGTIME is given in milliseconds. For 5 seconds, SAMPLINGTIME should be 5000.

If I reuse a QProcess variable, can there be leftover data in the stdout/stderr channels?

I have the following scenario:
QProcess*p;
// later
p->start();
//later
p->terminate(); // there might be unread data in stdout
//later
p->start();
I read the process stdout. After I call p->start() the second time, could there still be unread data left in the stdout buffers from the first p->start()? That would be a problem for me. Do I need to flush the buffers or something?
Okay, I've checked the sources. The QProcess::start() method explicitly clears both output buffers, so it should be okay, at least in this sense:
void QProcess::start(const QString &program, const QStringList &arguments, OpenMode mode)
{
Q_D(QProcess);
if (d->processState != NotRunning) {
qWarning("QProcess::start: Process is already running");
return;
}
#if defined QPROCESS_DEBUG
qDebug() << "QProcess::start(" << program << "," << arguments << "," << mode << ")";
#endif
d->outputReadBuffer.clear();
d->errorReadBuffer.clear();
I still think it's a bad style to reuse the same object, though.

qt - qprocess start in a loop

I am calling a process in a loop. Need to ensure that one process ends before it starts again. how is it possible.
void MainWindow::setNewProjectInQueueList()
{
// this is already gotten in queueList now loop thru the list and add project
QStringList arguments;
projNm = ui->lineEditCreateProject->text();
qDebug() << " projNm " << projNm;
for (int j= 0; j < queueList.length(); j++)
{ if (! QString(queueList[j]).isEmpty())
{
// call process
// QString queueName = queueList[j];
arguments << "-sq" << queueList[j];
qDebug() << " arguments sq " << queueList[j];
procQueueList.start("qconf",arguments);
}
}
// and append for each queue with new project name
// and store into the system
}
Brgds,
kNish
Call QProcess::waitForFinished() to wait until the process terminates.
Using the waitForFinished approach from within a loop in the main thread will freeze the application. Instead, putting the loop in a separate thread, or making a queue of processes to start and then launch upon the finished signal from the previous one is good alternatives.

Resources