4

(Note that this is not primarily a Qt question)

It seems to me that the return value of QFile::exists() is sometimes incorrect.

Consider the following two unit-test-like snippets (each of which I have executed a few thousand times in a loop)

// create file
QFile file("test.tmp");
QVERIFY(file.open(QIODevice::WriteOnly));
QVERIFY(file.write("some data") != -1);
file.close();

// delete file
QVERIFY(file.remove());

// assert file is gone
QVERIFY(!file.exists());  // <-- 5..10 % chance of failure

and

// create file
QFile file("test.tmp");
QVERIFY(file.open(QIODevice::WriteOnly));
QVERIFY(file.write("some data") != -1);
file.close();

// delete file
QVERIFY(file.remove());

// retry until file is gone (or until timeout)
for (auto i = 0; i < 10; i++)
{
  if (!file.exists())  // <-- note that only the check is retried, not the actual delete
    return;

  QThread::yieldCurrentThread();
}

QFAIL("file is still reported as existing");  // <-- never reached in my tests

The first unit test fails about 8 out of 100 times. Always on the last line of code (indicating that the file still exists). The second unit test never fails.

This behavior was observed on a Windows 10 system using NTFS (with Qt 5.2.1). It could not be reproduced using ubuntu 16.04 LTS using ext4 on a VM (with Qt 5.8.0)

Not sure if this helps:

So my questions are:

  • what is happening?
  • what are implications that I might be interested in?

update:

For clarification: I am hoping for an answer like "this is caused by the NTFS feature 'bills-fancy-caching-magic'". From there I would like to find out, whether Qt does look over this feature intentionally.

simon
  • 273
  • 1
  • 7
  • 1
    I suggest this is very much a Qt question, since both samples of code are using Qt types or functions for each statement. The workings of those types or functions are critical - for example, do they lock a file in some way that potentially prevents it being deleted after closing? Certainly, without such knowledge of how Qt does things, nobody with C++ or windows knowledge can help you. – Peter Jul 12 '18 at 09:05
  • 3
    I would say that `file.remove()` (might) be a not blocking operation and done asynchronously. – Jarod42 Jul 12 '18 at 09:08
  • @Jarod42 this is what I thought. However `file.remove()`s API documentation suggests that it "_Returns true if successful; otherwise returns false._". This does not sound like an asynchronous implementation to me. Apart from that: it works as expected on ubuntu / ext4. – simon Jul 12 '18 at 10:14
  • @simon - that depends on what is meant by "successful". There are plenty of APIs that do things asynchronously, and indicate success if they succeed in initiating the asynchronous operation rather than waiting for that operation to complete. – Peter Jul 12 '18 at 10:30
  • @Peter - yes, you are of course right. As far as I understand the [API documentation](http://doc.qt.io/qt-5/qfile.html#remove), this is not what is meant in my case. – simon Jul 12 '18 at 10:58
  • Try use QFileInfo with setCaching(false); – Deep Jul 12 '18 at 21:17
  • 1
    `Windows 10 NTFS Qt 5.10.1` didn't observe using 1MB size test file...BTW what about using `QFileSystemWatcher` watch `fileChanged` then check the `exists`. – JustWe Jul 13 '18 at 02:18
  • @Jiu thanks for your answer. Are you saying, that you could not reproduce the behavior? `QFileSystemWatcher` is one way to get things straight. But what I am more concerned about is that `QFile::exists` is sometimes lying. Should I replace every instance in my whole codebase? I would like to have this function to either always tell the truth (or to get off my lawn)... – simon Jul 13 '18 at 06:10
  • I created about 1MB size `QString`, repeated the test case 100 times, didn't see failure..is there anyone else tested Qt 5.10.1? maybe Qt fixed it? – JustWe Jul 13 '18 at 06:38
  • @Deep Using `QFileInfo` produces the exact same behavior. – simon Jul 13 '18 at 07:16

1 Answers1

0

According to the Windows API documentation, it is defined behaviour:

The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.

It seems to be a property of the Windows kernel and therefore not to be limited to NTFS.

The behaviour seems to be unpredictable, as other services (think virus scanners) might open the file in question.

simon
  • 273
  • 1
  • 7