Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost, Shared Memory and Vectors

I need to share a stack of strings between processes (possibly more complex objects in the future). I've decided to use boost::interprocess but I can't get it to work. I'm sure it's because I'm not understanding something. I followed their example, but I would really appreciate it if someone with experience with using that library can have a look at my code and tell me what's wrong. The problem is it seems to work but after a few iterations I get all kinds of exceptions both on the reader process and sometimes on the writer process. Here's a simplified version of my implementation:

using namespace boost::interprocess;
class SharedMemoryWrapper
{
public:
    SharedMemoryWrapper(const std::string & name, bool server) :
      m_name(name),
      m_server(server)
    {
        if (server)
        {
            named_mutex::remove("named_mutex");
            shared_memory_object::remove(m_name.c_str());
            m_segment = new managed_shared_memory (create_only,name.c_str(),65536);         
            m_stackAllocator = new StringStackAllocator(m_segment->get_segment_manager());
            m_stack = m_segment->construct<StringStack>("MyStack")(*m_stackAllocator);
        }
        else
        {
            m_segment = new managed_shared_memory(open_only ,name.c_str());
            m_stack = m_segment->find<StringStack>("MyStack").first;
        }
        m_mutex = new named_mutex(open_or_create, "named_mutex");
    }

    ~SharedMemoryWrapper()
    {
        if (m_server)
        {
            named_mutex::remove("named_mutex");
            m_segment->destroy<StringStack>("MyStack");
            delete m_stackAllocator;
            shared_memory_object::remove(m_name.c_str());
        }
        delete m_mutex;
        delete m_segment;
    }

    void push(const std::string & in)
    {
        scoped_lock<named_mutex> lock(*m_mutex);
        boost::interprocess::string inStr(in.c_str());
        m_stack->push_back(inStr);
    }
    std::string pop()
    {
        scoped_lock<named_mutex> lock(*m_mutex);
        std::string result = "";
        if (m_stack->size() > 0)
        {
            result = std::string(m_stack->begin()->c_str());
            m_stack->erase(m_stack->begin());
        }
        return result;
    }
private:
    typedef boost::interprocess::allocator<boost::interprocess::string, boost::interprocess::managed_shared_memory::segment_manager> StringStackAllocator;
    typedef boost::interprocess::vector<boost::interprocess::string, StringStackAllocator> StringStack;
    bool m_server;
    std::string m_name;
    boost::interprocess::managed_shared_memory * m_segment;
    StringStackAllocator * m_stackAllocator;
    StringStack * m_stack;  
    boost::interprocess::named_mutex * m_mutex;
};

EDIT Edited to use named_mutex. Original code was using interprocess_mutex which is incorrect, but that wasn't the problem.

EDIT2 I should also note that things work up to a point. The writer process can push several small strings (or one very large string) before the reader breaks. The reader breaks in a way that the line m_stack->begin() does not refer to a valid string. It's garbage. And then further execution throws an exception.

EDIT3 I have modified the class to use boost::interprocess::string rather than std::string. Still the reader fails with invalid memory address. Here is the reader/writer

//reader process
SharedMemoryWrapper mem("MyMemory", true);
std::string myString;
int x = 5;
do
{
    myString = mem.pop();
    if (myString != "") 
    {
        std::cout << myString << std::endl;
    }
} while (1); //while (myString != ""); 

//writer
SharedMemoryWrapper mem("MyMemory", false);
for (int i = 0; i < 1000000000; i++)
{
    std::stringstream ss;
    ss <<  i;  //causes failure after few thousand iterations
    //ss << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" << i; //causes immediate failure
    mem.push(ss.str());
}
return 0;
like image 944
Budric Avatar asked Apr 23 '09 21:04

Budric


1 Answers

There are several things that leaped out at me about your implementation. One was the use of a pointer to the named mutex object, whereas the documentation of most boost libraries tends to bend over backwards to not use a pointer. This leads me to ask for a reference to the program snippet you worked from in building your own test case, as I have had similar misadventures and sometimes the only way out was to go back to the exemplar and work forward one step at a time until I come across the breaking change.

The other thing that seems questionable is your allocation of a 65k block for shared memory, and then in your test code, looping to 1000000000, pushing a string onto your stack each iteration.

With a modern PC able to execute 1000 instructions per microsecond and more, and operating systems like Windows still doling out execution quanta in 15 millisecond. chunks, it won't take long to overflow that stack. That would be my first guess as to why things are haywire.

P.S. I just returned from fixing my name to something resembling my actual identity. Then the irony hit that my answer to your question has been staring us both in the face from the upper left hand corner of the browser page! (That is, of course, presuming I was correct, which is so often not the case in this biz.)

like image 155
Jamie Fenton Avatar answered Nov 12 '22 02:11

Jamie Fenton