1

Before I start, I would like to clarify two things:

  1. I cannot use Boost IPC for this project, and I cannot use anything but original C/C++ libraries.

  2. I have looked here and here for answers and still couldn't get the answer I wanted.

I am trying to store a vector in shared memory location. I have mapped a shared memory page and got its pointer. I also have created the vector itself on the shared memory using placement new and giving it the LPVOID of the map view:

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,    // use paging file
    NULL,                    // default security
    PAGE_READWRITE,          // read/write access
    0,                       // maximum object size (high-order DWORD)
    BUF_SIZE,                // maximum object size (low-order DWORD)
    szName);                 // name of mapping object


if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not create file mapping object (%d).\n"),
        GetLastError());
    return 1;
}

pBuf = MapViewOfFile(hMapFile,   // handle to map object
    FILE_MAP_ALL_ACCESS, // read/write permission
    0,
    0,
    BUF_SIZE);

if (pBuf == NULL)
{
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());

    CloseHandle(hMapFile);

    return 1;
}

std::vector<int, shared_allocator<int>>* vec = new(pBuf) std::vector<int, shared_allocator<int>>;

Now, when I try accessing the memory from the second process, I am able to get hold of the vector itself and even print its size(). Obviously, I cannot access any of its data since it is all stored in the first process's heap.

From the code, it is clear I am using my custom allocator, which looks like this:

template <class T>
struct shared_allocator
{
    typedef T value_type;
    shared_allocator() noexcept {} //default ctor not required by C++ Standard Library    

                                                                 // A converting copy constructor:    
    template<class U> shared_allocator(const shared_allocator<U>&) noexcept {}
    template<class U> bool operator==(const shared_allocator<U>&) const noexcept
    {
        return true;
    }
    template<class U> bool operator!=(const shared_allocator<U>&) const noexcept
    {
        return false;
    }
    T* allocate(const size_t n) const;
    void deallocate(T* const p, size_t) const noexcept;
};

template <class T>
T* shared_allocator<T>::allocate(const size_t n) const
{
    if (n == 0)
    {
        return nullptr;
    }
    if (n > static_cast<size_t>(-1) / sizeof(T))
    {
        throw std::bad_array_new_length();
    }
    //void* const pv = malloc(n * sizeof(T));
    //if (!pv) { throw std::bad_alloc(); }
    //return static_cast<T*>(pv);

    return (T*) ::operator new(n*sizeof(T));
}

template<class T>
void shared_allocator<T>::deallocate(T * const p, size_t) const noexcept
{
    // free(p);
    delete(p);
}

This code was done following Microsoft's documentation.

I replaced malloc and free with C++ new and delete. The code above works well and the first process can access everything.

Now I was trying to modify the allocator so it would allocate the newly created objects on the shared memory. So I changed the allocate function to:

template <class T>
T* shared_allocator<T>::allocate(const size_t n) const
{
    if (n == 0)
    {
        return nullptr;
    }
    if (n > static_cast<size_t>(-1) / sizeof(T))
    {
        throw std::bad_array_new_length();
    }
    //void* const pv = malloc(n * sizeof(T));
    //if (!pv) { throw std::bad_alloc(); }
    //return static_cast<T*>(pv);

    cnt += n*sizeof(T);
    void* p = (void*) (static_cast<char*>(pBuf) + cnt);

    return (T*) ::operator new(n*sizeof(T), p);
}

cnt is a global variable that starts as 0.

This is (obviously) not working. It crashes with some memory violation exceptions and all. I tried tracking the change of cnt and the first time it is incremented by 16, then by 4s.

The vector is not being filled with correct data since each time the _MyFirst() inside vector is being replaced with an address on the shared-memory.

I know I'm doing it wrong, but I have looked in many places and cannot seem to figure out how to get it right. How do I modify my allocator to correctly allocate on the shared-memory? If it alone can't, then would I need to re-create my own vector?

I have been following this article and it shows that I only need to write an allocator to make this work.

Everyone
  • 1,751
  • 13
  • 36
  • 2
    shouldn't that be `(char*)pBuf + sizeof(vector >) + cnt`? Since you constructed the vector in-place at the beginning of pBuf? – PeterT Oct 27 '17 at 10:34
  • @PeterT Very true. I have changed it, at least now, my first pushed value is correct (I used to `push_back(0)` and get a random value as `vec[0]`). . Anyway, `vec[0]` is still stored in the process's heap, not in the shared memory. My second process is still unable to see `vec[0]`. – Everyone Oct 27 '17 at 10:45
  • unless you want to rewrite pointers or use some relative-pointer wrapper type you're going to have to use `MapViewOfFileEx` with the same base-address in both processes. Not sure whether this is possible in windows, never tried it. But theretically using the same virtual address in two different processes should be fine – PeterT Oct 27 '17 at 11:02
  • @PeterT Specifying base-address is not really practical and can break at any given moment. I would like a robust solution. As you mentioned, I need to work with pointers. My question is, where do I start and how should I modify them? – Everyone Oct 27 '17 at 11:07
  • Does your shared memory occupy the same virtual address in both processes? – Richard Critten Oct 27 '17 at 11:32
  • @RichardCritten Not necessarily. – Everyone Oct 27 '17 at 11:33
  • Then the pointer (for the allocated memory) stored in the vector is going to point to the wrong virtual address in one of the processes. – Richard Critten Oct 27 '17 at 11:34
  • I understand that, how can I make it point relatively? – Everyone Oct 27 '17 at 11:36
  • @Everyone I don't think that's really feasible with std::vector – PeterT Oct 27 '17 at 11:42
  • That is part of my question when I asked whether or not I need to re-create my own vector. – Everyone Oct 27 '17 at 11:44
  • Try using `MapViewOfFile2` it lets you pick the base address (although how you choose a valid base address I have no idea). – Richard Critten Oct 27 '17 at 11:48
  • @RichardCritten Most operating systems randomize the address space of the guest programs for security purposes. Even if I were able to pick one correctly and ran the programs well, I might run into trouble later in the future with this. – Everyone Oct 27 '17 at 11:51
  • @Everyone have you managed to find a solution ? – Mecanik Dec 12 '19 at 06:40
  • @NorbertBoros i am afraid there is no easy way out. You need to re-implement the allocator in a custom way to make it allocate on shared memory. It's no easy task. Personally, I made a much simpler data structure and stored it instead. – Everyone Dec 12 '19 at 09:22
  • @Everyone well.... :(. Can you share the structure design ? – Mecanik Dec 12 '19 at 09:37
  • @NorbertBoros it isn't very much related to the question. It's basically an array with fixed-size elements and fixed amount of entries. – Everyone Dec 13 '19 at 18:17
  • @Everyone thanks, I did the same. – Mecanik Dec 13 '19 at 19:05

0 Answers0