Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do fsync on an ofstream?

Tags:

I want to make sure that an ofstream has been written to the disk device. What's the portable way (portable on POSIX systems) of doing this?

Does that solve the problem if I open the file separately in read-only append mode to get a file descriptor and call fsync with it? Like this:

ofstream out(filename);
/* ...
   write content into out
   ...
*/
out.close();

int fd = open(filename, O_APPEND);
fsync(fd);
close(fd);
like image 342
Vik Avatar asked Mar 24 '09 10:03

Vik


People also ask

What is fsync used for?

The fsync() function transfers all data for the file indicated by the open file descriptor file_descriptor to the storage device associated with file_descriptor. fsync() does not return until the transfer is complete, or until an error is detected.

Is Ofstream a header file?

Whenever there is a need to represent the output file stream and to create a file and write information to the file, we make use of ofstream by including the header file <fstream> in the source file.


2 Answers

Unfortunately, looking through the standard there is nothing provided by basic_filebuf or any of the basic_[io]?fstream class templates to allow you to extract the underlying OS file descriptor (in the way that fileno() does for C stdio I/O).

Nor is there an open() method or constructor that takes such a file descriptor as a parameter (which would allow you to open the file using a different mechanism and record the filehandle).

There is basic_ostream::flush(), however I suspect that this does not in fact call fsync() -- I expect that, like fflush() in stdio, it only makes sure that the user-space runtime library buffers are flushed, meaning that the OS could still be buffering the data.

So in short there appears to be no way to do this portably. :(

What to do? My suggestion is to subclass basic_filebuf<C, T>:

template <typename charT, typename traits = std::char_traits<charT> >
class my_basic_filebuf : public basic_filebuf<charT, traits> {
    ....

public:
    int fileno() { ... }
    ....
};

typedef my_basic_filebuf<char> my_filebuf;

To use it, you can construct an ofstream using the default constructor, then assign the new buffer with rdbuf():

my_filebuf buf;
buf.open("somefile.txt");

ofstream ofs;
ofs.rdbuf(&buf);

ofs << "Writing to somefile.txt..." << endl;
int fd = static_cast<my_filebuf*>(ofs.rdbuf())->fileno();

Of course you could also derive a new class from basic_ostream to make the process of opening a file and retrieving its file descriptor more convenient.

like image 23
j_random_hacker Avatar answered Sep 18 '22 09:09

j_random_hacker


If you're able to use Boost, try a file_descriptor_sink based stream, eg.:

boost::filesystem::path filePath("some-file");
boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file(filePath);

//  Write some stuff to file.

//  Ensure the buffer's written to the OS ...
file.flush();

//  Tell the OS to sync it with the real device.
//
::fdatasync(file->handle());
like image 54
mrtimdog Avatar answered Sep 21 '22 09:09

mrtimdog