|
Boost Users : |
Subject: Re: [Boost-users] Interprocess container segmentation fault (example provided)
From: Anthony Foiani (tkil_at_[hidden])
Date: 2012-03-02 03:33:13
Josh --
"Davidson, Josh" <josh.davidson_at_[hidden]> 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;
}
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net