23 Apr 2006 richdawe   » (Journeyer)

std::string in shared memory

The Problem

A tricky problem I faced at work a while back was trying to get some code using C++ std::strings in shared memory to work with libstdc++ from gcc 3.2.x.

This didn't work because of PR/16612. The effect of this bug is that you cannot store empty strings in shared memory. The implementation of std::string in libstdc++ 3.2.x uses a shared representation of the empty string. The problem arises because this shared representation of the empty string may live at different addresses in each of the processes using the shared memory block. If one process stores the empty string in shared memory and then another accesses it, you are likely to get a segfault.

The Solution

A shared allocator had been written, to allow the std::string and other STL types to be stored in the shared memory, e.g.:

typedef std::basic_string<char, std::char_traits<char>, SomeSharedAllocator<char> > SharedString;

SomeSharedAllocator handles allocations, deallocations and accessing STL objects in the shared memory. (I didn't write any of that awesome code; I was just porting it to an older Linux version.)

The solution was to stop using the common representation of the empty string -- to actually store an empty string in shared memory. This sounds pretty straightforward, but was actually quite tricky. It involved implementing specific functions in the implementation of std::string to suppress the default common representation: the default constructor and the C-style string constructor.

From the header file:

// Default constructor
template<>
    basic_string<char, std::char_traits<char>, SomeSharedAllocator<char> >
    ::basic_string ()
    : _M_dataplus(_S_construct((size_type) 1,
                               char(' '),
                               SomeSharedAllocator<char>()),
                      SomeSharedAllocator<char>())
      {
        this->resize(0, char());
      };

// C string constructor template<gt; basic_string<char, std::char_traits<char>, SomeSharedAllocator<char> > ::basic_string (const char *__s, const SomeSharedAllocator<char>& __alloc);

From the source module:

// C string constructor
  // std::string(NULL) segfaults; preserve that behaviour.
  template<>
    basic_string<char, std::char_traits<char>, SomeSharedAllocator<char> >
    ::basic_string (const char *__s,
                    const SomeSharedAllocator<char>& __alloc)
    : _M_dataplus(*__s
                  ? _S_construct(__s,
                                 __s + traits_type::length(__s),
                                 __alloc)
                  : _S_construct((size_type) 1,
                                 char(' '),
                                 __alloc),
                  __alloc)
      {
        if (!*__s)
          this->resize(0, char());
      }

The source module was linked into the shared memory library. All access to the shared memory using strings goes via the SharedString type.

Consider an empty string created by the above code. An empty std::string created by that code has a representation stored somewhere. Assigning a std::string into a SharedString will copy the original representation into one allocated in shared memory. So the code above guarantees that multiple empty strings in shared memory would be stored multiple times, rather than using a common representation, fixing the original problem.

Prologue

This problem has a work-around in gcc 3.4.3 and later -- #define _GLIBCXX_FULLY_DYNAMIC_STRING. But how do you know whether your version needs the work-around above or the #define?

  • #if defined(__GLIBCPP__) && (__GLIBCPP__ < 20041228) -- you need the code listed above.
  • #if defined(__GLIBCXX__) && (__GLIBCXX__ < 20041228) -- you need the code listed above.
  • Otherwise you need to #define _GLIBCXX_FULLY_DYNAMIC_STRING.

Music

"Protection" by Massive Attack

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!