Reputation: 2860
I was wondering how QFile
behaves when multiple handles are opened to the same file (using C++ in Visual Studio 2013 on Windows 7), so I wrote the following little program:
QFile file("tmp.txt");
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
QTextStream ts(&file);
ts << "Hallo\n";
QFile file2("tmp.txt");
file2.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream ts2(&file2);
ts2 << "Hallo 2\n";
file.close();
ts2 << "Hello again\n";
file2.close();.
This produces the following output in file tmp.txt:
Hallo 2
Hello again
So the first Hallo
statement got lost. If I do a ts.flush()
right after the ts << "Hallo\n"
this does not happen, which makes me think that the statement got lost in the internal buffers of QString
or it was overwritten by the subsequent output statements. However, I want to use QFile
in a logging framework, so I don't want to always flush, as this would decrease the performance.
I also tried the same thing with std::basic_ostream<char>
instead of QFile
:
std::basic_ofstream<char> file;
file.open("tmp.txt", std::ios_base::out | std::ios_base::ate | std::ios_base::app);
file << "Hallo\n";
std::basic_ofstream<char> file2;
file2.open("tmp.txt", std::ios_base::out | std::ios_base::ate | std::ios_base::app);
file2 << "Hallo 2\n";
file.close();
file2 << "Hello again\n";
file2.close();
which outputs as I would expect:
Hallo
Hallo 2
Hello again
So what is the problem with the QFile
example? Is QFile
not intended to be used with multiple handles pointing to the same file or what is going on here exactly? I thought that my use case is quite a common one, so I'm a bit surprised to find this behaviour. I couldn't find more specifics in the Qt documentation. I've read here that Qt opens the file in shared mode, so this shouldn't be a problem.
I eventually want to use QFile
for logging (where access to the function that does the actual writing is of course synchronized), but this little example worries me that some log statements might get lost on the way. Do you think it would be better to use STL streams instead of QFile
?
Edit
As it was pointed out, std::endl
causes a flush, so I changed the STL example above to only use \n
which according to here does not cause a flush. The behavior described above is unchanged, though.
Upvotes: 1
Views: 1720
Reputation: 5557
I seems you want it both ways.
If you want several write buffers and don't want to flush them, it's hard to be sure of having all the writes in the file, and in the right order.
Your small test with std::basic_ostream
is not a proof: will it work with larger writes ? Will it work on other OSes ? Do you want to risk your process for a (yet unproven) speed gain ?
Upvotes: 1
Reputation: 903
QFile file("tmp.txt");
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
QTextStream ts(&file);
ts << "Hallo\n";
QFile file2("tmp.txt");
file2.open(QIODevice::Append);
QTextStream ts2(&file2);
ts2 << "Hallo 2\n";
file.close();
ts2 << "Hello again\n";
file2.close();
Try this, i changed it so the Truncate not getting invoke by WriteOnly. Silly me didn't read that one. {UPDATE}
QIODevice::WriteOnly 0x0002 The device is open for writing. Note that this mode implies Truncate.
QIODevice::Truncate 0x0008 If possible, the device is truncated before it is opened. All earlier contents of the device are lost.
Reading Source : http://doc.qt.io/qt-5/qiodevice.html#OpenModeFlag-enum
Upvotes: 0
Reputation: 22796
There are several suspicious things going on. For starters, you are introducing two levels of buffering.
First and foremost QTextStream has an internal buffer, that you can flush by calling flush
on it.
Second, QFile is also buffering (or, better, it's using the buffered APIs from your library -- fopen
, fwrite
, and so on). Pass QIODevice::Unbuffered
to open
to make it use the unbuffered APIs (open
, write
, ...).
Now since this is terribly error prone, QTextStream::flush actually flushes also the underlying file device.
Also, you're passing WriteOnly | Append
which doesn't make sense. It's only one of the two.
However, note that your writes may still interleave. POSIX.1-2013 says that
A write is atomic if the whole amount written in one operation is not interleaved with data from any other process. This is useful when there are multiple writers sending data to a single reader. Applications need to know how large a write request can be expected to be performed atomically. This maximum is called {PIPE_BUF}.
(On Windows I have no idea).
Upvotes: 0