简体   繁体   中英

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

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.

In this case, no.

Under the hood, Boost's interprocess/mapped_region.hpp is using mmap which will return you a pointer to you memory mapped region.

You only need to use volatile if you suspect another process (or hardware) might be writing to your file.

(That would be the most basic synchronization you should provide, because volatile enforces a read from memory on each access. If you have control over the processes, you may try more advanced synchronization like a semaphore.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM