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.