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:
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
Bring Window to Front -> Raise(),Show(),Activatewindow() Don't Work
Most Efficient Replacement for Isbadreadptr
Add Library Search Path to Clang
Using C++ Aggregate Initialization in Std::Make_Shared
Thread Safety of Matlab Engine API
Adding Quotes to Argument in C++ Preprocessor
Undefined Reference to Symbol '_Zn5Boost6System15System_Categoryev' Error
Static Initialization and Destruction of a Static Library's Globals Not Happening with G++
Tool to Generate Xml File from Xsd (For Testing)
Why How to Implicitly Convert an Int Literal to an Int * in C But Not in C++
What Does a Comma Separated List of Values, Enclosed in Parenthesis Mean in C? a = (1, 2, 3);
How to Format a Datetime to String Using Boost
What Is the Fastest Way to Compute Large Power of 2 Modulo a Number
Specializing Single Method in a Big Template Class
Why Doesn't the Program Crash When I Call a Member Function Through a Null Pointer in C++