vendredi 26 août 2016

Share std::map between processes with mmap()

I'm trying to share a std::map<std::string, std::chrono::system_clock::time_point> map: each string is a hostname identifying a site, and the time_point is the last time a process visited that site.

I was trying with mmap but each process still see its own copy of the map.

Here's my code (I took away all the methods and variables not concerning my problem):

#include <sys/mman.h>
#include <unistd.h>

#include <iostream>
#include <map>
#include <string>
#include <chrono>

typedef std::map<std::string, std::chrono::system_clock::time_point> mymap;
typedef mymap::iterator iter;
typedef mymap* mapPointer;

class MmapManager {
    private:
        MmapManager() {
            frequency = (mapPointer) mmap(NULL, sizeof(frequency), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
            if (frequency == MAP_FAILED) {
                std::cout << "mapping failed" << std::endl;
            }
        };

        ~MmapManager() {
            std::cout << "~MmapManager()" << std::endl;
        }

    public:
        // my class was designed with the singleton pattern
        static MmapManager& getInstance() {
            static MmapManager instance;
            return instance;
        }

    private:
        // pointer to my map
        mapPointer frequency;

    public:
        // check if the process already visited site "host"
        bool isHostAlreadyVisited(std::string host) {
            return frequency->find(host) != frequency->end();
        }
        // add new visited site and time of the visit
        void addHost(std::string host) {
            (*frequency)[host] = std::chrono::system_clock::now();
            std::cout << "PROC " << getpid() << " added " << host << std::endl;
        }
        // get time of the visit for site "host"
        std::chrono::system_clock::time_point getElement(std::string host) {
            return (*frequency)[host];
        }
        // print the map
        void showMap(void) {
            std::cout << "PROC " << getpid() << " prints map keys" << std::endl;
            for (auto it = frequency->begin(); it != frequency->end(); ++it) {
                std::cout << it->first << std::endl;
            }
        }
};

int main(void) {

    // simulate the processes
    for (int i=0; i<5; i++) {
        // child process
        if (fork() == 0) {
            // if child never visited this site...
            if (! MmapManager::getInstance().isHostAlreadyVisited("www.google.com")) {
                std::cout << "PID " << getpid() << " www.google.com is new" << std::endl;
                // ...add it to the map
                MmapManager::getInstance().addHost("www.google.com");
            }
            else {
                // if child already visited it, calculate 
                // how much time passed since last visit
                auto now = std::chrono::system_clock::now();
                auto before = MmapManager::getInstance().getElement("www.google.com");
                std::chrono::duration<double> diff = now-before;
                std::cout << "PID " << getpid() << " visited www.google.com " << diff.count() << " seconds ago" << std::endl;
            }
            MmapManager::getInstance().showMap();
            _exit(EXIT_SUCCESS);
        }
    }
    return 0;
}

Here's one of the possible outputs:

PID 12457 www.google.com is new
PID 12459 www.google.com is new
PID 12458 www.google.com is new
PID 12460 www.google.com is new
PID 12461 www.google.com is new

I can't use other external libraries like Boost or use threads: I know they share memory, but the program was designed this way (with child processes doing stuff) and I can't modify it (original code is not mine).

Why does each process still see its own copy of the map?

Aucun commentaire:

Enregistrer un commentaire