c++linuxboostg++boost-interprocess

Why does boost::interprocess::managed_shared_memory throw a boost::interprocess_exception upon construction?


In the code below, I'm trying to initialize a managed_shared_memory object. When the constructor is invoked I see the below error message -

terminate called after throwing an instance of 'boost::interprocess::interprocess_exception' what(): boost::interprocess_exception::library_error Aborted

why is this exception being thrown? I'm running this on an ubuntu 16.04 linux OS, compiled the program using g++ 9.3.0. Boost version 1.58.0

struct test_obj {
    size_t x;
    size_t y;
    uint8_t buf[32];
    bool is_valid;
};

class shm_wrapper {
        public:
            shm_wrapper() : m_shm(
                                boost::interprocess::open_or_create, 
                                "my_shm",
                                sizeof(test_obj) )
            {};
        private:
        boost::interprocess::managed_shared_memory m_shm;
};

Solution

  • It aborts because the size is insufficient for the segment manager control blocks.

    sizeof(test_obj) is just 56 bytes (on my system).

    If you give the segment 10 KiB, it reports 224 bytes effectively used:

    Live On Coliru

    #include <boost/interprocess/managed_mapped_file.hpp>
    #include <boost/interprocess/managed_shared_memory.hpp>
    
    namespace bip = boost::interprocess;
    
    #ifdef COLIRU
        using segment_type = bip::managed_mapped_file;
    #else
        using segment_type = bip::managed_shared_memory;
    #endif
    
    static constexpr size_t SegmentSize = 10 * 1024;
    
    struct test_obj {
        size_t x;
        size_t y;
        uint8_t buf[32];
        bool is_valid;
    };
    
    class shm_wrapper {
      public:
        shm_wrapper() : m_shm(bip::open_or_create, "my_shm", SegmentSize){};
    
        size_t free() const { return m_shm.get_free_memory(); }
    
      private:
        segment_type m_shm;
    };
    
    #include <iostream>
    int main() {
        std::cout << sizeof(test_obj) << std::endl;
        shm_wrapper w;
        std::cout << w.free() << "\n";
        std::cout << "Effectively used:" << (SegmentSize - w.free()) << "\n";
    }
    

    Prints

    56
    10016
    Effectively used:224
    

    Summary

    Maybe you didn't want a segment manager with dynamic allocation features. In that case, have a look at shared_memory_object instead.

    BONUS: Example Using Mapped Region

    To store a "dumb object" in a fixed-size region, you don't need a segment manager. You'd use a mapped_region on a shared_memory_object (or a file_mapping).

    Here's another sample Live On Coliru

    #include <boost/interprocess/shared_memory_object.hpp>
    #include <boost/interprocess/file_mapping.hpp>
    #include <boost/interprocess/mapped_region.hpp>
    #include <iostream>
    namespace bip = boost::interprocess;
    
    struct test_obj {
        size_t x;
        size_t y;
        uint8_t buf[32];
        bool is_valid;
    };
    
    #ifdef COLIRU
        #include <fstream>
        using mapping_type = bip::file_mapping;
    #else
        using mapping_type = bip::shared_memory_object;
    #endif
    
    template <typename T>
    class shm_wrapper {
        static_assert(std::is_trivial_v<T>);
        static_assert(std::is_standard_layout_v<T>);
    
    #ifdef COLIRU // file mappings require more work to cater for the storage
        struct backing_t { } backing;
        backing_t ensure_file(std::string name, size_t size) {
            std::filebuf fbuf;
            fbuf.open(name, std::ios::in | std::ios::out | std::ios::app | std::ios::binary);
            //set the size, sparsely
            fbuf.pubseekoff(size-1, std::ios_base::beg);
            fbuf.sputc(0);
            fbuf.close();
            return {};
        }
      public:
        shm_wrapper()
            : backing { ensure_file("my_shm", sizeof(T)) },
              m_mappable("my_shm", bip::read_write),
              m_reg(m_mappable, bip::read_write, 0, sizeof(T))
        { }
    #else
      public:
        shm_wrapper()
            : m_mappable(bip::open_or_create, "my_shm", bip::read_write),
              m_reg(m_mappable, bip::read_write, 0, sizeof(T))
        {
            m_mappable.truncate(sizeof(T));
        }
    #endif
    
        T& get()             { return *static_cast<T*>(m_reg.get_address()); }
        T const& get() const { return *static_cast<T const*>(m_reg.get_address()); }
    
        auto size() const { return m_reg.get_size(); } 
        auto flush()      { return m_reg.flush();    } 
    
      private:
        mapping_type m_mappable;
        bip::mapped_region m_reg;
    };
    
    int main() {
        shm_wrapper<test_obj> w;
        std::cout << "x:" << w.get().x << "\n";
        w.get().x ^= 0xa7;
        return w.flush()? 0 : 1;
    }
    

    Prints, when run 4x in succession:

    x:0
    x:167
    x:0
    x:167