-1

I am trying to save a shared pointer (boost interprocess) in an interprocess unordered map as the value. In my test program, I am inserting about 100 elements. Each element has a string as the key and a shared_ptr as the value. The shared_ptr is a pointer to a complex data type.

The creation and insertion for the 1st element completes successfully. However, I get an exception when I am creating the shared_ptr instance for the second element. I am getting the following exception:

error: boost::interprocess_exception::library_error. error-code: Bad file descriptor. native-error: 0.

I've pasted the code below. Any help is appreciated. Thanks.

defs.h

// basic shared memory defs.
#pragma once

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>

namespace bip = boost::interprocess;

namespace ip {

namespace shmem {

  using segment = bip::managed_shared_memory;
  using segment_manager = segment::segment_manager;

  using void_allocator = bip::allocator<void, segment_manager>;
  using char_allocator = bip::allocator<char, segment_manager>;
  using int_allocator = bip::allocator<int, segment_manager>;
  using double_allocator = bip::allocator<double, segment_manager>;
  using size_t_allocator = bip::allocator<size_t, segment_manager>;
  using string = bip::basic_string<char, std::char_traits<char>, char_allocator>;

} // namespace shmem

} // namespace ip

map-value.h

#pragma once
#include <boost/interprocess/smart_ptr/deleter.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <cassert>

#include "defs.h"

namespace ip
{

template <typename ValueType>
class MapValue
{
public:
  using MVType = MapValue<ValueType>;
  typedef typename bip::deleter<MVType, shmem::segment_manager> MVDeleter;
  typedef typename bip::managed_shared_ptr<MVType, shmem::segment>::type MapValuePtr;

  MapValue(const shmem::void_allocator&, ValueType value=ValueType(), size_t=0);

  MapValue(const MapValue& other);
  MapValue& operator=(const MapValue& other);
  MapValue(const MapValue&& other) noexcept;
  MapValue& operator=(const MapValue&& other) noexcept;
  void swap(const MapValue& other) noexcept;

  size_t GetTimestamp() const { return mTimestamp; }
  ValueType GetValue() const { return mValue; }

private:
  size_t mTimestamp;
  ValueType mValue;
};

} // namespace ip

map-value.cc

#include "map-value.h"

namespace ip
{

template <typename ValueType>
MapValue<ValueType>::MapValue(const shmem::void_allocator &void_allocator
                                          , ValueType value, size_t expire_ts)
: mTimestamp(expire_ts)
, mValue(value, void_allocator)
{
}

template <typename ValueType>
MapValue<ValueType>::MapValue(const MapValue& other)
: mTimestamp(other.mTimestamp)
, mValue(other.mValue)
{
}

template <typename ValueType>
MapValue<ValueType>&
MapValue<ValueType>::operator=(const MapValue& other)
{
  MapValue tmp(other);
  swap(tmp);
  return *this;
}

template <typename ValueType>
MapValue<ValueType>::MapValue(const MapValue&& other) noexcept
: mTimestamp(std::move(other.mTimestamp))
, mValue(std::move(other.mValue))
{
}

template <typename ValueType>
MapValue<ValueType>&
MapValue<ValueType>::operator=(const MapValue&& other) noexcept
{
  auto tmp = std::move(other);
  swap(tmp);
  return *this;
}

template <typename ValueType>
void
MapValue<ValueType>::swap(const MapValue& other) noexcept
{
  mTimestamp = other.mTimestamp;
  mValue = other.mValue;
}

template class MapValue<shmem::string>;

} // namespace ip

unorderd_map_master.cc

#include <chrono>
#include <cstdlib>
#include <iostream>
#include <functional>                 //std::equal_to
#include <sstream>
#include <string.h>

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/unordered_map.hpp>    //boost::unordered_map
#include <boost/functional/hash.hpp>  //boost::hash

#include "map-value.h"

using namespace boost::interprocess;
using namespace ip;

int
main ()
{
  shared_memory_object::remove("MySharedMemory");

  // Create shared memory
  managed_shared_memory segment(create_only, "MySharedMemory", 1024*64);

  // Note that unordered_map<Key, MappedType>'s value_type is 
  // std::pair<const Key, MappedType>, so the allocator must allocate a pair.
  // 
  typedef int  KeyType;
  //typedef size_t  MappedType;
  typedef std::pair<const int, MapValue<shmem::string>::MapValuePtr> ValueType;

  // Typedef the allocator
  typedef allocator<ValueType, shmem::segment_manager> ShmemAllocator;

  // Alias an unordered_map of ints that uses the previous STL-like allocator.
  typedef boost::unordered_map<KeyType, MapValue<shmem::string>::MapValuePtr
                               , boost::hash<KeyType>, std::equal_to<KeyType>
                               , ShmemAllocator> SharedHashMap;

  // Construct a shared memory hash map.
  // Note that the first parameter is the initial bucket count and
  // after that, the hash function, the equality function and the allocator
  SharedHashMap *hashmap = segment.construct<SharedHashMap>("SharedHashMap")      //object name
                               (4                                       // bucket count
                                , boost::hash<KeyType>()                // hash func
                                , std::equal_to<KeyType>()              // equality func
                                , segment.get_allocator<ValueType>());  //allocator instance

  int i;
  std::string test("test");

  using MVString = MapValue<shmem::string>;
  using MVStringPtr = MVString::MapValuePtr;
  using MVStringDeleter = MVString::MVDeleter;
  shmem::void_allocator alloc_inst (segment.get_segment_manager());

  try {
    // Insert data in the hash map
    for (i = 0; i < 100; ++i) {
      // Create a shared pointer in shared memory
      // pointing to a newly created object in the segment
      std::ostringstream ostr;
      ostr << test << i;
      const uint64_t now = std::chrono::duration_cast<std::chrono::microseconds>(
                                  std::chrono::steady_clock::now().time_since_epoch()).count();
      size_t ts = now + 60*1000000;   // 60s from now.

      shmem::string name(ostr.str().c_str(), alloc_inst);
      std::cout << "created name: " << name << '\n';
      MVStringPtr sh_ptr_instance =
          make_managed_shared_ptr(segment.construct<MVString>("MVString")(alloc_inst
                                                                          , name, ts), segment);

      assert(sh_ptr_instance.use_count() == 1);
      std::cout << "created shared_ptr" << '\n';

      hashmap->insert(ValueType(i, sh_ptr_instance));
      std::cout << "Inserting at " << i << ": "
                << "value: " << ostr.str() << ". "
                << "timestamp: " << ts << '\n';
    }
  }
  catch (const bip::interprocess_exception& e) {
    std::cout << "error: " << e.what() << ". "
              << "error-code: " << strerror(e.get_error_code()) << ". "
              << "error-msg: " << e.get_native_error() << ". " << '\n';
    std::cout << "Error while inserting element #" << (i+1) << '\n';
    shared_memory_object::remove("MySharedMemory");
    std::exit(-1);
  }

  std::cout << i << " values inserted into shared memory" << '\n';
  return 0;
}

unorder_map_slave.cc

#include <cstdlib>
#include <iostream>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/unordered_map.hpp>   //boost::unordered_map
#include <functional>      //std::equal_to
#include <boost/functional/hash.hpp> //boost::hash

#include "map-value.h"

using namespace boost::interprocess;
using namespace ip;

int
main (int argc, char *argv[])
{
  using namespace boost::interprocess;

  int c;
  bool clear_shmem = false;

  while ((c = getopt(argc, argv, "c")) != -1) {
    switch(c) {
      case 'c':
        clear_shmem = true;
        break;
      default:
        std::cout << "Invalid input params to " << argv[0] << '\n';
        break;
    }
  }

  try
  {
    managed_shared_memory segment(open_or_create, "MySharedMemory", 65536);

    // Again the map<Key, MappedType>'s value_type is std::pair<const Key, MappedType>,
        // so the allocator must allocate that pair.
    typedef int  KeyType;
    //typedef size_t  MappedType;
    typedef std::pair<const int, MapValue<shmem::string>::MapValuePtr> ValueType;

    // Assign allocator
    typedef allocator<ValueType, managed_shared_memory::segment_manager> ShmemAllocator;

    // The map
    typedef boost::unordered_map<KeyType, MapValue<shmem::string>::MapValuePtr
                                 , boost::hash<KeyType>, std::equal_to<KeyType>
                                 , ShmemAllocator> SharedHashMap;

    // Initialize the shared memory STL-compatible allocator
    ShmemAllocator alloc_inst(segment.get_segment_manager());

    // access the map in SHM through the offset ptr
    SharedHashMap::iterator iter;
    offset_ptr<SharedHashMap> m_pmap = segment.find<SharedHashMap>("SharedHashMap").first;

    if (!m_pmap) {
      std::cout << "Shared memory not initalized... Exiting..." << '\n';
      std::exit(-1);
    }

    iter = m_pmap->begin();
    std::cout<< "size: " << m_pmap->size();

    using MVString = MapValue<shmem::string>;
    using MVStringPtr = MVString::MapValuePtr;
    using MVStringDeleter = MVString::MVDeleter;

    for (; iter != m_pmap->end(); iter++) {
      MapValue<shmem::string>::MapValuePtr val(iter->second);
      std::cout << "\n " << iter->first << " : "
                << "value: " << val->GetValue() << ". "
                << "timestamp: " << val->GetTimestamp() << '\n';
    }

    std::cout<< "\n";
  }

  catch (const bip::interprocess_exception& e) {
    std::cout << "error: " << e.what() << ". "
              << "error-code: " << strerror(e.get_error_code()) << ". "
              << "error-msg: " << e.get_native_error() << ". " << '\n';
    shared_memory_object::remove("MySharedMemory");
  }

  if (clear_shmem) {
    shared_memory_object::remove("MySharedMemory");
  }

  return 0;
}
Dnj Abc
  • 352
  • 1
  • 4
  • 13
  • 2
    Please provide a [mcve]. – adentinger May 30 '17 at 02:04
  • Not sure what you mean by complete example. I've provided the complete code that's needed. Please save each code block into separate files (I've even provided the file names). After that you can do the following: g++ -std=c++1y -o umap_master map-value.cc unordered_map_master.cc; g++ -std=c++1y -o umap_slave map-value.cc unordered_map_slave.cc; You can run 'umap_master', you'll see the error. Not sure why you downvoted? If you needed more info, can't ask for more info without downvoting? – Dnj Abc May 30 '17 at 03:25
  • 1
    I'm sure your example is indeed complete and verifiable. The first word is the keyword : Minimal. That means you must provide a small piece of code that still reproduces the problem. This is also a super-effective way to debug very strange errors. Either 1) write a small program and try to reproduce the problem so that you have fewer things to think about, or 2) Remove some code ; see if the problem is still there. Repeat until you no longer see the error, at which point you know the error came from those last things you removed. – adentinger May 30 '17 at 11:15
  • It is indeed minimal. My problem was to create a shared pointer for a complex data type that can be inserted into the map as a value. This example is exactly that and nothing more. Perhaps, I could have removed the slave.cc code since it's not needed to reproduce the issue. However, for an interprocess example, I felt it was best to provide provide both (since every interprocess question on SO has done that). Folks comes to SO to get answers to their issues. You didn't even take the time to look or answer the question and have gone on an unnecessary tangent that doesn't help anyone. – Dnj Abc May 30 '17 at 18:20

1 Answers1

0

I've figured out the answer for myself. It looks like the name string given to the shared pointer must be unique.

MVStringPtr sh_ptr_instance =
       make_managed_shared_ptr(segment.construct<MVString>("MVString")(alloc_inst, name, ts), segment);

For example, the string "MVString" in the above code must be unique. I was seeing the error because I was creating multiple shared pointers with the same name. Making the name string unique resolved my issue.

Dnj Abc
  • 352
  • 1
  • 4
  • 13