Re: [Boost-users] Interprocess container segmentation fault (example provided)

Ion, Terribly sorry, but I goofed when I cobbled that code together. My actual application constructs "TestStore" from the segment, and not on the stack like what I pasted in before. I'm including the full test code again, although only the main function changed. Since the map itself a member of TestStore and TestStore is being constructed from the segment, I would think that should be roughly equivalent to just creating the map by itself from the segment. However, as you'll see if you run it, it still seg faults when accessing the list iterator. ===================================== BEGIN CODE============================================== #include <stdint.h> #include <cstddef> #include <string> #include <functional> #include <iostream> #include <utility> using std::cerr; using std::endl; using std::pair; #include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/offset_ptr.hpp> #include <boost/interprocess/containers/string.hpp> #include <boost/interprocess/containers/list.hpp> #include <boost/interprocess/containers/map.hpp> #include <boost/ref.hpp> using namespace boost::interprocess; class SharedMemoryDeleter { public: SharedMemoryDeleter(const std::string& name) : mName(name) { shared_memory_object::remove(mName.c_str()); } ~SharedMemoryDeleter() { shared_memory_object::remove(mName.c_str()); } protected: std::string mName; }; class TestStore { public: struct TestData { uint16_t commandStatus; size_t wordCount; uint16_t data[32]; }; ///Allocates characters in shared memory typedef allocator<char, managed_shared_memory::segment_manager> CharAllocator; ///String in shared memory typedef basic_string<char, std::char_traits<char>, CharAllocator> SharedMemoryString; ///Allocates list in shared memory typedef allocator<TestData, managed_shared_memory::segment_manager> ListAllocator; ///List of TestData items in shared memory typedef list<TestData, ListAllocator> SharedMemoryList; ///Key/Value pair for the map (SA, Data List) typedef pair<const unsigned, SharedMemoryList> data_map_value_t; ///Allocates maps in shared memory typedef allocator<data_map_value_t, managed_shared_memory::segment_manager> MapAllocator; ///Maps lists to SAs in shared memory typedef map<unsigned, SharedMemoryList, std::less<unsigned>, MapAllocator> SharedMemoryMap; TestStore(managed_shared_memory& segment, const std::string& name) : mSegment(&segment), mSegmentName(name.c_str(), CharAllocator(mSegment->get_segment_manager())), mData(std::less<unsigned>(), MapAllocator(mSegment->get_segment_manager())){ } /////////////////////////////////////////////////////////////////////////// /// Destructor /////////////////////////////////////////////////////////////////////////// virtual ~TestStore() { } void printData() { TestStore::SharedMemoryMap::iterator it = mData.begin(); while(it != mData.end()) { TestStore::SharedMemoryList::iterator lIt = it->second.begin(); cerr << "SA: " << it->first << endl; cerr << it->second.size() << endl; while (lIt != it->second.end()) { cerr << "\titerating..." << endl; cerr << "wc: " << lIt->wordCount << endl; cerr << "cs: " << lIt->commandStatus << endl; cerr << "\t" << lIt->commandStatus << ", WC: " << lIt->wordCount << endl; ++lIt; } ++it; } cerr << endl; } bool addMessage(unsigned key, const TestData& data) { TestStore::SharedMemoryMap::iterator it = mData.find(key); cerr << "Adding word count " << data.wordCount << endl; if(it != mData.end()) { it->second.push_back(data); } else { cerr << "Looking for " << mSegmentName.c_str() << endl; managed_shared_memory m(open_only, mSegmentName.c_str()); cerr << "found it" << endl; cerr << m.check_sanity() << " - " << m.get_free_memory() << "/" << m.get_size() << endl; cerr << mSegment->check_sanity() << " * " << mSegment->get_free_memory() << "/" << mSegment->get_size() << endl; ///!!!!!!!!!!!!!!!BROKE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TestStore::SharedMemoryList sList(TestStore::ListAllocator( m.get_segment_manager())); //==========This Works (obviously only for current process)=========== // TestStore::SharedMemoryList sList(TestStore::ListAllocator( // mSegment->get_segment_manager())); sList.push_back(data); cerr << "pushed" << endl; cerr << sList.front().wordCount << endl; mData.insert(TestStore::SharedMemoryMap::value_type(key, sList)); cerr << "added list" << endl; } cerr << "====================Data post add===================" << endl; printData(); return true; } private: ///Segment housing this class. Only valid in the constructing process managed_shared_memory* mSegment; ///Name of the segment SharedMemoryString mSegmentName; SharedMemoryMap mData; }; int main() { std::string segName = "MemSegmentHere"; int num_bytes = 645360; SharedMemoryDeleter deleter(segName); managed_shared_memory shm(open_or_create, segName.c_str(), num_bytes); std::string storeName = "TestStoreHere"; shm.destroy<TestStore>(storeName.c_str()); TestStore* store = shm.construct<TestStore>(storeName.c_str())(boost::ref(shm), boost::ref(segName)); TestStore::TestData data; data.commandStatus = 10; data.wordCount = 14; store->addMessage(3, data); return 0; } -----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Ion Gaztañaga Sent: Thursday, March 01, 2012 3:24 PM To: Boost User List Subject: EXTERNAL: Re: [Boost-users] Interprocess container segmentation fault (example provided) El 01/03/2012 19:01, Davidson, Josh escribió:
I'm running into issues with using interprocess containers have other containers added to them during runtime. A very high level description is I have a set of data structures in shared memory that can be accessed and modified by more than one process. I'm creating the data structures in the first process and storing the name of the segment within the data structure. Methods that modify the data by adding another container first lookup the segment using its name, build the new container, and then insert it into the existing container. In the example I'm providing below, I have a map of lists. For some reason, when I add a new list, if I open the segment by name, and allocate the list using the segment manager returned via the lookup, the list isn't initialized correctly and accessing the list (via printData here) results in a segfault. However, if I bypass the segment lookup and just cache the raw pointer to the segment, it works. It's probably easiest just to look at the code below. In addMessage, you can see how I'm trying to initialize the list in a manner that will work across multiple processes. This breaks. Commented out directly below it is a hack that will work. I can't see any reason why one works and the other doesn't. The "managed_shared_memory" object returned from the lookup appears to be valid and exactly the same as the pointer I'm caching with the data structure in this example.
I am currently using 1.49 final, but I've observed similar behavior in other 1.4x releases.
You are storing a *local* map that allocates memory in shared memory (that is, the internal pointers of the map are in the stack, whereas the allocated nodes are in shared memory), which is really a bad idea as once you unmap the segment, as the local map's internal pointers point to segmentation-fault friendly memory. I know that "managed_shared_memory shm" outlives "TestStore store" but the problem is that you open the shm again in "managed_shared_memory m" and that maps the shared memory *in another address, different to the address mapped by "managed_shared_memory shm"* (two views of the same shared memory, as you map a file twice). So you end up with a local map pointing to *two different address ranges* (although the share the same underlying shared memory object, just think it's like a file). Then you unmap "managed_shared_memory m" (the second address range disappears) and then when you try to iterate, when the local map tries to read an element through the second address range, you get a segmentation fault. If you put the map itself in shared memory (e.g. use a named construct) you can avoid the problem. And if you have already mapped the shared memory once, avoid wasting OS resources (the OS must synchronize the two address ranges in the same process with the same underlying device/shm/mapped file) and application address space mapping the same shared memory/file several times. Sometimes is useful to open the shared memory segment more than once from the same process (say, when you communicate a DLL with an application, or when trying to implement a intermodule singleton type), but in general, try to avoid this. Best, Ion _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Josh -- "Davidson, Josh" <josh.davidson@lmco.com> writes:
Since the map itself a member of TestStore and TestStore is being constructed from the segment, I would think that should be roughly equivalent to just creating the map by itself from the segment. However, as you'll see if you run it, it still seg faults when accessing the list iterator.
I believe that Ion's comments about the use of 'm' in TestStore::addMessage are still correct and are near the root of the problem. I'd recommend arranging your class so that successful construction implies: 1. the named shared segment itself has been found or created and mapped; 2. the named object within the segment has been found or created; 3. the object is in a valid state and ready for operations. Additionally, you should treat this as a Singleton within each process (to avoid the multiple-mapping problems that Ion mentioned). You can do that with simple discipline / review, or you can force it by hiding it behind a factory method, employing "boost::noncopyable", etc. This means that your "addMessage" method will always have exactly the correct segment manager to allocate additional resources. I wrote a very similar container, although it only mapped strings to strings: http://scrye.com/~tkil/cpp/boost-interprocess/SharedMap.hpp http://scrye.com/~tkil/cpp/boost-interprocess/SharedMap.cpp http://scrye.com/~tkil/cpp/boost-interprocess/SharedMapTest.cpp (The test won't compile, as it relies on other classes from the original codebase; but it should show you how the SharedMap class is intended to be used.) I also wrote a few replies on the list, a month or two back, that touch on these same topics: http://groups.google.com/group/boost-list/msg/ee619a75277b2146 My version of your code follows; it is also available at: http://scrye.com/~tkil/cpp/boost-interprocess/josh-shared-data.cpp The main changes are: a. Make all the typedefs private to the class; they're an implementation detail. b. Have the class do all the work of lookup, mapping, and allocation; this makes for a much simpler interface. c. Minor issues: within the class, there is only one Map, List, and Datum type, so remove some of the very long prefixes. And a few other bits. Hopefully you'll find this useful. Best regards, Anthony Foiani ======================================================================== // linux compile line: // g++ -O2 -o josh-shared-data josh-shared-data.cpp -lpthread -lrt #include <stdint.h> #include <string> #include <iostream> #include <utility> #include <boost/foreach.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/containers/list.hpp> #include <boost/interprocess/containers/map.hpp> #include <boost/interprocess/managed_shared_memory.hpp> namespace bi = boost::interprocess; class TestStore { public: struct Datum { uint16_t commandStatus; std::size_t wordCount; uint16_t data[32]; Datum( uint16_t commandStatus, std::size_t wordCount ) : commandStatus( commandStatus ), wordCount( wordCount ) { for ( std::size_t i = 0; i < 32; ++i ) data[i] = 0; } friend std::ostream & operator << ( std::ostream & os, const Datum & data ) { os << "[Datum: cs=" << data.commandStatus << ", wc=" << data.wordCount << ", data=<" << data.data[0]; for ( std::size_t i = 1; i < 32; ++i ) os << " " << data.data[i]; os << ">]"; return os; } }; enum ReplaceFlag { REPLACE_EXISTING, KEEP_EXISTING }; TestStore( const std::string & segName, const std::string & mapName, const ReplaceFlag replace = KEEP_EXISTING, const std::size_t segSize = 600U*1024U ) : mReplacer( segName, replace ), mSegment( bi::open_or_create, segName.c_str(), segSize ), mData( mSegment.find_or_construct< Map >( mapName.c_str() )( std::less< Key >(), mSegment.get_allocator< MapValueType >() ) ) { } void printData() { using std::clog; using std::endl; BOOST_FOREACH( const Map::value_type & outer, *mData ) { clog << "key: " << outer.first << endl; BOOST_FOREACH( const List::value_type & inner, outer.second ) clog << " " << inner << endl; } } bool addMessage( unsigned key, const Datum & datum ) { Map::iterator it( mData->find(key) ); if ( it == mData->end() ) { ListAllocator alloc( mSegment.get_segment_manager() ); std::pair< Map::iterator, bool > rv( mData->insert( Map::value_type( key, List( alloc ) ) ) ); it = rv.first; } it->second.push_back( datum ); return true; } private: typedef bi::managed_shared_memory Segment; typedef Segment::segment_manager Manager; typedef bi::allocator< Datum, Manager > ListAllocator; typedef bi::list< Datum, ListAllocator > List; typedef unsigned int Key; typedef std::pair< const Key, List > MapValueType; typedef bi::allocator< MapValueType, Manager > MapAllocator; typedef bi::map< Key, List, std::less< Key >, MapAllocator> Map; struct Replacer { Replacer( const std::string segName, const ReplaceFlag action ) { if ( action == REPLACE_EXISTING ) bi::shared_memory_object::remove( segName.c_str() ); } }; Replacer mReplacer; Segment mSegment; Map * mData; }; int main() { const std::string segName = "JoshSharedSeg"; const std::string mapName = "map"; TestStore store( segName, mapName, TestStore::REPLACE_EXISTING ); using std::clog; using std::endl; clog << "before:" << endl; store.printData(); TestStore::Datum datum( 10, 14 ); store.addMessage( 3, datum ); clog << "after:" << endl; store.printData(); return 0; }
participants (2)
-
Anthony Foiani
-
Davidson, Josh