new QFile("some.txt") fails with "device not open" when windows screen locked

473 views Asked by At

Consider this very simple example:

for(;;){
   QFile *file = new QFile("some.txt");//this is where it fails with "device not open" when windows screen is locked
   if(file->exists()){
      file->open(QFile::ReadOnly);
      QString contents = file->readAll();
      file->close();
   }
   file->deleteLater();
   QThread::sleep(2);
}

It was continuing to work as normal when windows screen is locked up until recently but I'm not sure if it started to act like this because I started to use Qt 5.9 or if it was a windows update that prevent file accesses while windows screen is locked.

So please advise a workaround or a solution. Thank you.

Edit: It turns out that QFile or file access was not the issue and the problem was where and by whom it was called. So I'm accepting @Kuba 's answer since it was informative and in the right direction.

1

There are 1 answers

5
Kuba hasn't forgotten Monica On BEST ANSWER

Your code leaks memory and resources: the QFile instances will never be destroyed, since you never let the event loop run in the current thread, and it's the event loop that would destroy them.

There's no reason to create the file object on the heap, and neither is there any reason to explicitly close the file: QFile is a proper C++ class that implements RAII. Destroying it is safe in any state, without resource leaks. Just destroy the file to close its underlying handle.

Incorporating the above suggestions, the code should be:

for (;;){
   QFile file("some.txt");
   if (file.exists()){
      file.open(QFile::ReadOnly);
      auto contents = file->readAll();
      qDebug() << "read, n=" << contents.size();
   }
   QThread::sleep(2);
}

Ideally, though, you should run your code from the event loop and not block the thread. Threads are expensive resources and having a thread that does nothing most of the time is rather wasteful.

auto timer = new QTimer;
timer->start(2000);
auto reader = []{
  QFile file("some.txt");
  if (file.exists()){
    file.open(QFile::ReadOnly);
    auto contents = file->readAll();
    qDebug() << "read, n=" << contents.size();
  }
});
connect(timer, &QTimer::timeout, reader);

To end the reading, stop or destroy the timer.

The thread should be spinning an event loop - and the default implementation of QThread::run will do that for you. Thus the code above can be wrapped in a QObject and moved to a dedicated thread, that should also be used for other things.

Alternatively, you could use the thread pool and do the reading in a worker thread, but run the timer in the GUI thread. Only the connect statement above would need to be changed to:

connect(timer, &QTimer::timeout, [reader]{ QtConcurrent::run(reader); });