std::unordered_map with boost::interprocess allocator in shared memory - drawbacks?

前端 未结 1 1365
借酒劲吻你
借酒劲吻你 2020-12-11 09:15

I\'m right now getting into shared memory using boost::interprocess.

I\'ve defined a few std::unordered_map and std::unordered_set

相关标签:
1条回答
  • 2020-12-11 09:51

    unordered_map will cope with Boost Interprocess allocators IFF your library implementation has support for stateful allocators¹ and allocators using non-raw pointer types.

    Even so, like @rustyx mentions, you're going to be in deep trouble if you actually share the memory with another process. The other process is likely to map the segment at a different base address, making all pointers stored inside the memory region invalid.

    ☞ You need to use a Interprocess allocator with the string too!

    Here's what I usually prefer to write:

    #include <boost/interprocess/allocators/allocator.hpp>
    #include <boost/interprocess/containers/string.hpp>
    #include <boost/interprocess/managed_shared_memory.hpp>
    
    #include <unordered_map>
    
    namespace ipc = boost::interprocess;
    
    namespace Shared {
        using Segment = ipc::managed_shared_memory;
        using Manager = Segment::segment_manager;
        template <typename T> using Alloc = ipc::allocator<T, Manager>;
        template <typename K, typename V, typename KH = std::hash<K>, typename KEq = std::equal_to<K> >
            using HashMap = std::unordered_map<K, V, KH, KEq, Alloc<std::pair<const K, V>> >;
    
        using String = ipc::basic_string<char, std::char_traits<char>, Alloc<char> >;
    }
    
    using OBJ_MAP_TYPE = Shared::HashMap<size_t, Shared::String>;
    
    int main() {
        Shared::Segment msm(ipc::open_or_create, "test", 10ul<<20);
    
        Shared::Manager* mgr = msm.get_segment_manager();
        OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(msm.get_segment_manager());
    
        m.emplace(42, Shared::String("LtUaE", msm.get_segment_manager()));
    }
    

    Notable details:

    1. This bit:

      Shared::Manager* mgr = msm.get_segment_manager();
      OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(mgr);
      

      is a convenient short-cut for doing:

      Shared::Alloc<OBJ_MAP_TYPE::value_type> alloc_inst(msm.get_segment_manager());
      OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(alloc_inst);
      

      This works because the implicit conversion from segment-manager pointer to allocator instance is allowed.

    Enter MAGIC

    You'll note that the nested allocator is clumsy to work with:

    m.emplace(42, Shared::String("LtUaE", msm.get_segment_manager()));
    

    That's what the designers of scoped_allocator_adaptor tried to solve. If you change the allocator into:

    template <typename T> using Alloc = std::scoped_allocator_adaptor<ipc::allocator<T, Manager> >;
    

    You can suddenly just write:

    m.emplace(42, "LtUaE");
    

    This is because in-place construction is defined in terms of uses- allocator construction (see [allocator.uses.construction])

    See it Live On Coliru


    ¹ prepare to be surprised, @SergeyA. Libstdc++ didn't support this last time I checked, but its unordered_map supports it since GCC 4.9.0, and OP seems to have anecdotal evidence that libc++ does (although we don't even know whether there was ever an instance of the typedef :))

    0 讨论(0)
提交回复
热议问题