Boost is full of both amazing and scary things.
A simple workaround on Windows, could be to switch to managed_windows_shared_memory
instead of managed_shared_memory
, you can solve a variety of nasty crash/hang problems, and one sort of crash/hang problem appears to be caused, in turn by the differences between Windows file system behaviour and unix file system behaviour, and in particular, it seems that with boost and managed_shared_memory
on Windows, it is possible to run afoul of Windows file system locking limitations. I am informed that an effort to deal with this has been completed in BOost 1.53, but I am using Boost 1.53 and I still have this problem.
With regular managed_shared_memory
on Windows, you get persistence beyond the life of any of the client or server applications. This might be desirable in some people's cases thus the workaround is not a real fix for those people.
However, in my case, I didn't really need that anyways, although I had thought it would be handy, it turns out to be more pain than it's worth, at least with the current Boost implementation on Windows.
I would like to also point out that deletion of the shared memory file appears to be the root cause of the race condition that is causing the problem experienced in the question above. Proper synchronization around creation and checking, and deletion of the file appears to be essential to a real world implementation of the system, and in particular, it appears to be a devastating problem, if you have your master (server) delete the shared memory file while some clients are still using it. A reboot appears necessary to clear the resulting lock+NTFS-filesystem mess.
If I find a real solution I'll post it, but the above is more information than I could find anywhere else. Be wary of managed_shared_memory
and consider using managed_windows_shared_memory
and forget about trying to make the "persistent shared memory" idea work. Rather, use non-persistent windows-only managed_windows_shared_memory
.
Solving this, while keeping the managed_shared_memory
class in my application probably means wrapping all access to the managed_shared_memory
object in yet another level of interprocess synchronization primitives, or even with a raw Win32 API mutex. Boost could do something equivalent, but probably would introduce yet more accidental complexity.
(Aside: Am I the only one here who thinks that Template-All-the-things has been carried too far in general use, and especially in Boost, these days?)
Update 2: I have found an alternative way of freezing up managed_shared_memory
and this in turn freezes up any app you use it from. I did not expect it to be so easy to create deadlocks with Boost but it is pretty easy to do. The mutex code inside the implementation will freeze forever waiting for a mutex that some other user of the managed shared memory has gone away without releasing. This endless sleep waiting for a mutex that is never going to be released, is another deep design flaw in this boost interprocess implementation that so far, I have counted several serious design flaws in, at least on windows. Maybe it works beautifully on Linux.
The code that exhibits this is the find() method, called like this:
boost::interprocess::managed_shared_memory * segment;
std::pair<MyType*, std::size_t> f = segment->find<MyType>(name);
Here is the stack trace for a mutex deadlock (aka endless wait, frozen task):
Only solution when you get here, is to delete the shared memory area, after stopping or killing all hung processes that are waiting for this mutex.
> myapp.exe!boost::interprocess::winapi::sched_yield() Line 998 C++
myapp.exe!boost::interprocess::ipcdetail::thread_yield() Line 60 + 0xe bytes C++
myapp.exe!boost::interprocess::ipcdetail::spin_mutex::lock() Line 71 C++
myapp.exe!boost::interprocess::ipcdetail::spin_recursive_mutex::lock() Line 91 C++
myapp.exe!boost::interprocess::interprocess_recursive_mutex::lock() Line 161 C++
myapp.exe!boost::interprocess::scoped_lock<boost::interprocess::interprocess_recursive_mutex>::lock() Line 280 C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_get_lock(bool use_lock=true) Line 1340 C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_generic_find<char>(const char * name=0x00394290, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0> > > & index={...}, boost::interprocess::ipcdetail::in_place_interface & table={...}, unsigned int & length=1343657312, boost::interprocess::ipcdetail::bool_<1> is_intrusive={...}, bool use_lock=true) Line 854 + 0x11 bytes C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_find_impl<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290, bool lock=true) Line 728 + 0x25 bytes C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290) Line 423 + 0x1e bytes C++
myapp.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index,8>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 346 + 0x23 bytes C++
myapp.exe!boost::interprocess::basic_managed_shared_memory<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 208 + 0x10 bytes C++
myapp.exe!CCommonMemory::AllocateOrFindAreaMap(const char * name=0x00394290) Line 128 C++