Using a memory mapped file for persistence - is volatile required?

Viewed 596

I need persistence of a uint64_t tag across restarts.

To achieve this I am using boost::interprocess::mapped_region to memory map a file which I create in the same process:

bip::file_mapping file(filename.c_str(), bip::read_write);
auto region = std::make_unique<bip::mapped_region>(file, bip::read_write);

I then cast the address to my uint64_t type

using Tag = uint64_t;
Tag& curr_ = *reinterpret_cast<Tag*>(region->get_address());

Now I can post-increment tag, obtaining the "next tag", and the results are persisted across restarts

Tag next = curr_++;

Note that this file is written to and read from only by this process. It's purpose is purely to provide persistence.

Question:

Is my Tag& curr_, being non-volatile, and performing I/O to a memory mapped region, undefined-behaviour?

To be correct, does my code require the volatile keyword?

Full working example below:

#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <sys/stat.h>
#include <fstream>
#include <cstdint>
#include <memory>
#include <iostream>

namespace bip = boost::interprocess;

using Tag = uint64_t;

Tag& map_tag(const std::string& filename,
             std::unique_ptr<bip::mapped_region>& region)
{
    struct stat buffer;
    if (stat(filename.c_str(), &buffer) != 0)
    {
        std::filebuf fbuf;
        fbuf.open(filename.c_str(), std::ios_base::in | 
                                    std::ios_base::out | 
                                    std::ios_base::trunc | 
                                    std::ios_base::binary);

        Tag tag = 1;
        fbuf.sputn((char*)&tag, sizeof(Tag));
    }

    bip::file_mapping file(filename.c_str(), bip::read_write);

    // map the whole file with read-write permissions in this process
    region = std::make_unique<bip::mapped_region>(file, bip::read_write);

    return *reinterpret_cast<Tag*>(region->get_address());
}

class TagBroker
{
public:
    TagBroker(const std::string& filename)
        : curr_(map_tag(filename, region_))
    {}

    Tag next()
    {
        return curr_++;
    }

private:
    std::unique_ptr<bip::mapped_region> region_;
    Tag& curr_;
};

int main()
{
    TagBroker broker("/tmp/tags.bin");

    Tag tag = broker.next();

    std::cout << tag << '\n';
    return 0;
}

Output:

Across runs, persistence is kept.

$ ./a.out
1
$ ./a.out
2
$ ./a.out
3
$ ./a.out
4

I don't know if this is correct, since my process is the only one reading from/writing to Tag& curr_, or if it's just working by accident, and is, in fact, undefined behaviour.

1 Answers
Related