PDA

View Full Version : Seeking design advise



TorAn
1st March 2019, 23:33
I have following scenario:

My code invokes external function (some type of print) in the loop.
In each iteration, after variable amount of time (< 2 mins) it saves
the file in the specific directory or it may fail to print and I need to advance to the next loop cycle.

Logically it is like that:

while (1)
{
// waitfor returns true if file is found. If 3 minutes passed and file
// is not found where it is supposed to be, do nothing
if (waitfor (printfile(), 3))
{
// do something with the file
}
}

Printfile should monitor the appearance of the file in a certain directory. If not found in 2 mins, proceed to the next loop.
I am perfectly capable of doing something with straight c++ or boost. I am interested in understanding
how it can be implemented using specialized Qt classes, like QFileSystemWatcher, QFuture or others.
In QFileSystemWatcher, for example, I don't see how can I do wait without putting some wait conditions in the slot;

Advise is greatly appreciated.

Thanks.

ChrisW67
2nd March 2019, 06:04
Are you expecting your waiting program to be doing something while it waits?

d_stranz
2nd March 2019, 06:46
My code invokes external function (some type of print) in the loop.

Is this something you spawn using QProcess? Then you can implement a slot that handles one of the QProcess signals.


Are you expecting your waiting program to be doing something while it waits?

If not, then using something like QFileSystemWatcher might be a good solution, but it won't handle the case where creating the file fails (ie. the print doesn't work). But any code that uses a while( true ) infinite loop is potential for big trouble. If you have to do that, then you should at least insert a processEvents() call in the loop to keep the app from freezing.

The QFuture methods that query for a result are blocking, so they will cause your app to hang until that process is complete. That's about as bad as an infinite loop.

I am curious, though. Most operating systems I am familiar with implement some kind of print queue. So you should be able to send multiple print requests, and the printing system will queue them up and process them in order. Why do you have to wait until one finishes before starting the next one? The OS print queue will do that for you. So if you can do away with waiting, then QFileSystemWatcher will probably serve to tell you when a print job has created a file successfully.

TorAn
2nd March 2019, 09:45
Are you expecting your waiting program to be doing something while it waits?
No, just sit and wait until either new file is generated or timeout occures.

Added after 6 minutes:

The process I am describing is a specialized screen scraping process, so it is sequential in nature and I do have to wait until individual screen view is processed.

anda_skoa
2nd March 2019, 13:23
In QFileSystemWatcher, for example, I don't see how can I do wait without putting some wait conditions in the slot;


With this approach the waiting is done implicitly, i.e. the object registers itself with the platform's file notification system and relays notifications for files that it is told to watch.

The "loop" in this case is the thread's event loop so it is not really useful of you are running your own loop.

So its main use case is to trigger functionality when a file appears, is changed or disappears.
I.e. as the source that triggers an action, not as part of a larger processing chain.

Cheers,
_

TorAn
6th March 2019, 02:46
I wrote the test that I think covers suggestions that were given (and much appreciated):


class scraper: public QRunnable
{
private:
int _id;
QMutex& _mtx;
QString _path;
public:
scraper(int id, QMutex& m, QString p) : _id(id), _mtx(m), _path(p) {}
void run() {
QFileSystemWatcher qfs;
qfs.addPath(_path);

// this slot is never called, despite moving file to the path. I think it is because it executes in the main thread;
if (! QObject::connect(&qfs, &QFileSystemWatcher::directoryChanged, [&](const QString& p){
qDebug() << "cycle:" << QString::number(_id) << "file:" <<p;
_mtx.unlock();
})) {
qDebug() << "failure to connect to QFileSystemWatcher::directoryChanged signal";
return;
}
qDebug() << "waiting for file in cycle " << QString::number(_id);
}
};

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<< "started";
QMutex mtx;
QString pt="c:/temp/files";
QWaitCondition wc;
QTimer::singleShot(2000,&a, [&](){
for (int i = 0; i < 20; ++i) {
qDebug() << "starting cycle " << QString::number(i);
scraper* myscraper = new scraper(i, mtx, pt);
QThreadPool::globalInstance()->start(myscraper);
mtx.lock();
wc.wait(&mtx, 20000);
mtx.unlock();
}
});
return a.exec();
}

The issue I am having is that the signal directoryChanged is never called. I think it is because it is a queued call and it is executed in the context of the main thread which is blocked. Perhaps I should use QThread instead of QRunnable? Suggestions and critiques are always welcomed and appreciated. Thanks!

Lesiok
6th March 2019, 07:47
I think that directoryChanged signal is emited but qfs is deleted on exit from run(). Declare qfs like _mtx and _path.

TorAn
6th March 2019, 11:54
Thanks, I will try that.

Update - I don't think that the destruction of the instance is the problem. I changed the code to this:


class scraper: public QRunnable
{
private:
int _id;
QMutex& _mtx;
QString _path;
QFileSystemWatcher qfs;
public:
scraper(int id, QMutex& m, QString p) : _id(id), _mtx(m), _path(p) {
setAutoDelete(false);
}

So, autodestruction does not happen and QFileSystemWatcher is "alive". Slot is not called though when I copy the file to the directory.

anda_skoa
9th March 2019, 10:46
The issue I am having is that the signal directoryChanged is never called.

Your thread doesn't run an event loop, so the file system watcher never start its work.
I.e. your run() method immediately exists after setting up the object and its connection.



void run() {
QEventLoop loop;
QFileSystemWatcher qfs;
qfs.addPath(_path);

// this slot is never called, despite moving file to the path. I think it is because it executes in the main thread;
if (! QObject::connect(&qfs, &QFileSystemWatcher::directoryChanged, [&](const QString& p){
qDebug() << "cycle:" << QString::number(_id) << "file:" <<p;
loop.quit();
_mtx.unlock();
})) {
qDebug() << "failure to connect to QFileSystemWatcher::directoryChanged signal";
return;
}
qDebug() << "waiting for file in cycle " << QString::number(_id);
loop.exec();
}




I think it is because it is a queued call and it is executed in the context of the main thread which is blocked.

No, the QFileSystemWatcher is created in run(), which is executed by a separate thread.
So all its event handling happens in that thread.
But handling events requires a running event loop.



Perhaps I should use QThread instead of QRunnable?

I don't really understand why you need a thread at all.

Cheers,
_