What Happens to Interprocess Memory If One of The Processes Dies Unexpectedly

What happens to interprocess memory if one of the processes dies unexpectedly?

What happens to shared memory (in Linux, but you can A for Win also) when one of the procs die?

Nothing. When a process dies, the shared memory is left as it is. It is mapped as a file under /dev/shm/ directory. It is removed either when the system reboots, or when all processes unmmap the shared memory file and the shm_unlink() is called.

Is it UB?

No, it is well defined. See the man page for shm_overview(7) :

POSIX shared memory objects have kernel persistence: a shared memory object will exist until the system is shut down, or until all processes have unmapped the object and it has been deleted with shm_unlink(3)

What happens to shared memory if one of the process sharing the memory is killed?

Provided at least one other thread in another process has an open handle to the file mapping, I would expect the shared memory to remain intact.

Loose inter process communication

I've done it like Milag sugested in the comments:

a signal could trigger activity in the other proc; if you need a block of data as well, maybe use shared memory or R/W with a named pipe

Crash if boost managed_shared_memory is used on Windows

I also had the similar problem. The process was crashing inside boost. In my case when two processes tried to create or open a particular shared memory region simultaneously then one of the processes crashed afterward in the "for" loop.
I used "boost named mutex" while creating a shared memory region. That worked for me. If we take sehe's code then I think following modification would solve the problem.

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <string>
#include <iostream>

#define RESERVED_BUFFER_SIZE_WRITE (8 * 0x0100000)

namespace bip = boost::interprocess;

//Typedefs of allocators and containers
typedef bip::allocator<char, bip::managed_shared_memory::segment_manager> char_allocator;
typedef bip::basic_string<char, std::char_traits<char>, char_allocator> char_string;

int main()
{
boost::interprocess::named_mutex MyBookkeeperMutex(boost::interprocess::open_or_create, "MyBookkeeperMutex");

MyBookkeeperMutex.lock();
bip::managed_shared_memory m_sharedMemUsage(bip::open_or_create, "MyBookkeeper", 2 * RESERVED_BUFFER_SIZE_WRITE);
MyBookkeeperMutex.unlock();

char_allocator alloc_inst3(m_sharedMemUsage.get_segment_manager());
for (int count = 0; count < 100000; ++count) {
char_string key_obect("AAAAAAAAAAAAAAAAAAAAAAA", alloc_inst3);
}
boost::interprocess::named_mutex::remove("MyBookkeeperMutex");
}

How do I take ownership of an abandoned boost::interprocess::interprocess_mutex?

Unfortunately, this isn't supported by the boost::interprocess API as-is. There are a few ways you could implement it however:

If you are on a POSIX platform with support for pthread_mutexattr_setrobust_np, edit boost/interprocess/sync/posix/thread_helpers.hpp and boost/interprocess/sync/posix/interprocess_mutex.hpp to use robust mutexes, and to handle somehow the EOWNERDEAD return from pthread_mutex_lock.

If you are on some other platform, you could edit boost/interprocess/sync/emulation/interprocess_mutex.hpp to use a generation counter, with the locked flag in the lower bit. Then you can create a reclaim protocol that will set a flag in the lock word to indicate a pending reclaim, then do a compare-and-swap after a timeout to check that the same generation is still in the lock word, and if so replace it with a locked next-generation value.

If you're on windows, another good option would be to use native mutex objects; they'll likely be more efficient than busy-waiting anyway.

You may also want to reconsider the use of a shared-memory protocol - why not use a network protocol instead?

std::unordered_map with boost::interprocess allocator in shared memory - drawbacks?

unordered_map will cope with Boost Interprocess allocators IFF your library implementation has support for stateful allocators¹ and allocators using non-raw pointer types.

Even so, like @rustyx mentions, you're going to be in deep trouble if you actually share the memory with another process. The other process is likely to map the segment at a different base address, making all pointers stored inside the memory region invalid.

☞ You need to use a Interprocess allocator with the string too!

Here's what I usually prefer to write:

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

#include <unordered_map>

namespace ipc = boost::interprocess;

namespace Shared {
using Segment = ipc::managed_shared_memory;
using Manager = Segment::segment_manager;
template <typename T> using Alloc = ipc::allocator<T, Manager>;
template <typename K, typename V, typename KH = std::hash<K>, typename KEq = std::equal_to<K> >
using HashMap = std::unordered_map<K, V, KH, KEq, Alloc<std::pair<const K, V>> >;

using String = ipc::basic_string<char, std::char_traits<char>, Alloc<char> >;
}

using OBJ_MAP_TYPE = Shared::HashMap<size_t, Shared::String>;

int main() {
Shared::Segment msm(ipc::open_or_create, "test", 10ul<<20);

Shared::Manager* mgr = msm.get_segment_manager();
OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(msm.get_segment_manager());

m.emplace(42, Shared::String("LtUaE", msm.get_segment_manager()));
}

Notable details:

  1. This bit:

    Shared::Manager* mgr = msm.get_segment_manager();
    OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(mgr);

    is a convenient short-cut for doing:

    Shared::Alloc<OBJ_MAP_TYPE::value_type> alloc_inst(msm.get_segment_manager());
    OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(alloc_inst);

    This works because the implicit conversion from segment-manager pointer to allocator instance is allowed.

Enter MAGIC

You'll note that the nested allocator is clumsy to work with:

m.emplace(42, Shared::String("LtUaE", msm.get_segment_manager()));

That's what the designers of scoped_allocator_adaptor tried to solve. If you change the allocator into:

template <typename T> using Alloc = std::scoped_allocator_adaptor<ipc::allocator<T, Manager> >;

You can suddenly just write:

m.emplace(42, "LtUaE");

This is because in-place construction is defined in terms of uses-
allocator construction (see [allocator.uses.construction])

See it Live On Coliru


¹ prepare to be surprised, @SergeyA. Libstdc++ didn't support this last time I checked, but its unordered_map supports it since GCC 4.9.0, and OP seems to have anecdotal evidence that libc++ does (although we don't even know whether there was ever an instance of the typedef :))



Related Topics



Leave a reply



Submit