Skip to content

Commit

Permalink
Implement new ABI for segment_manager that supports overaligned types.
Browse files Browse the repository at this point in the history
  • Loading branch information
igaztanaga committed Sep 21, 2024
1 parent d6c4649 commit cbeb5b5
Show file tree
Hide file tree
Showing 6 changed files with 1,140 additions and 630 deletions.
3 changes: 3 additions & 0 deletions doc/interprocess.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -6774,6 +6774,9 @@ thank them:

* Some workarounds for Cygwin were added. Regression tests now work on Cygwin64.

* [*ABI breaking]: `segment_manager` nows uses a new implementation that supports overaligned types and fixes some existing bugs.
You can obtain the pre-Boost 1.87 ABI #defining `BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI` to `1` before including Boost.Interprocess headers.

* Fixed bugs:
* [@https://github.com/boostorg/interprocess/issues/210 GitHub #210 (['"Bug in boost::interprocess::ipcdetail::sync_handles::obtain_mutex"])].
* [@https://github.com/boostorg/interprocess/issues/192 GitHub #192 (['"managed_windows_shared_memory crash on destruction"])].
Expand Down
211 changes: 201 additions & 10 deletions include/boost/interprocess/detail/segment_manager_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ class mem_algo_deallocator
{ if(m_ptr) m_algo.deallocate(m_ptr); }
};

#if !defined(BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI)
#define BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI 2
#endif //#if !defined(BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI)

#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI == 1)

template<class size_type>
struct block_header
{
Expand All @@ -91,6 +97,7 @@ struct block_header
, m_alloc_type_sizeof_char( (unsigned char)((al_type << 5u) | ((unsigned char)szof_char & 0x1F)) )
{};

template<std::size_t>
size_type total_size() const
{
if(alloc_type() != anonymous_type){
Expand All @@ -104,13 +111,13 @@ struct block_header
size_type value_bytes() const
{ return m_value_bytes; }

template<class Header>
template<std::size_t , class Header>
size_type total_size_with_header() const
{
return get_rounded_size
( size_type(sizeof(Header))
, size_type(::boost::container::dtl::alignment_of<block_header<size_type> >::value))
+ total_size();
+ this->template total_size<0>();
}

unsigned char alloc_type() const
Expand Down Expand Up @@ -176,6 +183,10 @@ struct block_header
return hdr;
}

template<std::size_t, class >
static size_type front_space()
{ return 0u; }

private:
size_type value_offset() const
{
Expand All @@ -188,26 +199,200 @@ struct block_header
}
};

#elif (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI == 2)

template <class BlockHeader, class Header>
struct header_to_block_header_offset
{
BOOST_STATIC_CONSTEXPR std::size_t value = sizeof(Header)
+ ct_rounded_size< sizeof(BlockHeader)
, boost::move_detail::alignment_of<Header>::value
>::value
- sizeof(BlockHeader);
};

template <std::size_t MemAlignment, class BlockHeader, class Header>
struct prefix_offsets
{
BOOST_INTERPROCESS_STATIC_ASSERT(MemAlignment >= boost::move_detail::alignment_of<Header>::value);
BOOST_INTERPROCESS_STATIC_ASSERT(MemAlignment >= boost::move_detail::alignment_of<BlockHeader>::value);

BOOST_STATIC_CONSTEXPR std::size_t both_headers =
header_to_block_header_offset<BlockHeader, Header>::value + sizeof(BlockHeader);

BOOST_STATIC_CONSTEXPR std::size_t total_prefix = ct_rounded_size<both_headers, MemAlignment>::value;

BOOST_STATIC_CONSTEXPR std::size_t block_header_prefix = total_prefix - sizeof(BlockHeader);

BOOST_STATIC_CONSTEXPR std::size_t front_space = total_prefix - both_headers;
};

template <std::size_t MemAlignment, class BlockHeader>
struct prefix_offsets<MemAlignment, BlockHeader, void>
{
BOOST_INTERPROCESS_STATIC_ASSERT(MemAlignment >= boost::move_detail::alignment_of<BlockHeader>::value);

BOOST_STATIC_CONSTEXPR std::size_t total_prefix = ct_rounded_size<sizeof(BlockHeader), MemAlignment>::value;

BOOST_STATIC_CONSTEXPR std::size_t block_header_prefix = total_prefix - sizeof(BlockHeader);

BOOST_STATIC_CONSTEXPR std::size_t front_space = block_header_prefix;
};

template<class size_type>
struct block_header
{
const size_type m_value_bytes;
const unsigned short m_num_char;
const unsigned char m_alloc_type_sizeof_char;

block_header(size_type val_bytes
,size_type
,unsigned char al_type
,std::size_t szof_char
,std::size_t num_char
)
: m_value_bytes(val_bytes)
, m_num_char((unsigned short)num_char)
, m_alloc_type_sizeof_char( (unsigned char)((al_type << 5u) | ((unsigned char)szof_char & 0x1F)) )
{};

template<std::size_t MemAlignment>
size_type total_size() const
{
BOOST_CONSTEXPR_OR_CONST std::size_t block_header_prefix =
prefix_offsets<MemAlignment, block_header, void>::block_header_prefix;
if(alloc_type() != anonymous_type){
return block_header_prefix + name_offset() + (m_num_char+1u)*sizeof_char();
}
else{
return block_header_prefix + this->value_offset() + m_value_bytes;
}
}

template<std::size_t MemAlignment, class Header>
size_type total_size_with_header() const
{
BOOST_CONSTEXPR_OR_CONST std::size_t block_header_prefix =
prefix_offsets<MemAlignment, block_header, Header>::block_header_prefix;
return block_header_prefix + name_offset() + (m_num_char + 1u) * sizeof_char();
}

size_type value_bytes() const
{ return m_value_bytes; }

unsigned char alloc_type() const
{ return (m_alloc_type_sizeof_char >> 5u)&(unsigned char)0x7; }

unsigned char sizeof_char() const
{ return m_alloc_type_sizeof_char & (unsigned char)0x1F; }

template<class CharType>
CharType *name() const
{
return const_cast<CharType*>(move_detail::force_ptr<const CharType*>
(reinterpret_cast<const char*>(this) + name_offset()));
}

unsigned short name_length() const
{ return m_num_char; }

void *value() const
{
return const_cast<char*>((reinterpret_cast<const char*>(this) + this->value_offset()));
}

template<class T>
static block_header *block_header_from_value(T *value)
{
BOOST_ASSERT(is_ptr_aligned(value, ::boost::container::dtl::alignment_of<T>::value));
block_header* hdr =
const_cast<block_header*>
(move_detail::force_ptr<const block_header*>
(reinterpret_cast<const char*>(value) - value_offset()));

//Some sanity checks
BOOST_ASSERT(hdr->m_value_bytes % sizeof(T) == 0);
return hdr;
}

template<class Header>
static block_header *from_first_header(Header *header)
{
BOOST_ASSERT(is_ptr_aligned(header));
block_header * const hdr = move_detail::force_ptr<block_header*>(
reinterpret_cast<char*>(header) + header_to_block_header_offset<block_header, Header>::value
);
//Some sanity checks
BOOST_ASSERT(is_ptr_aligned(hdr));
return hdr;
}

template<class Header>
static const block_header *from_first_header(const Header *header)
{ return from_first_header(const_cast<Header*>(header)); }

template<class Header>
static Header *to_first_header(block_header *bheader)
{
BOOST_ASSERT(is_ptr_aligned(bheader));
Header * hdr = move_detail::force_ptr<Header*>(
reinterpret_cast<char*>(bheader) - header_to_block_header_offset<block_header, Header>::value
);
//Some sanity checks
BOOST_ASSERT(is_ptr_aligned(hdr));
return hdr;
}

template<std::size_t MemAlignment, class Header>
static size_type front_space()
{
return prefix_offsets<MemAlignment, block_header, Header>::front_space;
}

private:

static size_type value_offset()
{
return size_type(sizeof(block_header));
}

size_type name_offset() const
{
return this->value_offset() + get_rounded_size(size_type(m_value_bytes), size_type(sizeof_char()));
}
};


#else //(BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI == )

#error "Incorrect BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI value!"

#endif

template<class CharT>
struct intrusive_compare_key
{
typedef CharT char_type;

intrusive_compare_key(const CharT *str, std::size_t len)
: mp_str(str), m_len(len)
intrusive_compare_key(const CharT* str, std::size_t len)
: mp_str(str), m_len(len)
{}

const CharT *str() const
{ return mp_str; }
const CharT* str() const
{
return mp_str;
}

std::size_t len() const
{ return m_len; }
{
return m_len;
}

const CharT * mp_str;
const CharT* mp_str;
std::size_t m_len;
};


//!This struct indicates an anonymous object creation
//!allocation
template<instance_type type>
Expand Down Expand Up @@ -377,7 +562,13 @@ struct index_config

template<class HeaderBase>
struct intrusive_value_type
{ typedef intrusive_value_type_impl<HeaderBase, CharT, typename segment_manager_base::size_type> type; };
{
typedef intrusive_value_type_impl
< HeaderBase
, CharT
, typename segment_manager_base::size_type
> type;
};

typedef intrusive_compare_key<CharT> compare_key_type;
};
Expand Down
Loading

3 comments on commit cbeb5b5

@pdimov
Copy link
Member

@pdimov pdimov commented on cbeb5b5 Sep 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit seems to cause a failure in the Lockfree tests:

          Start  764: run-boost_lockfree-queue_interprocess_test
 704/1970 Test  #764: run-boost_lockfree-queue_interprocess_test ............................................Subprocess aborted***Exception:   0.08 sec
terminate called after throwing an instance of 'boost::interprocess::bad_alloc'
  what():  boost::interprocess::bad_alloc

See https://github.com/boostorg/boost/actions/runs/10976479575/job/30477262572.

@igaztanaga
Copy link
Member Author

@igaztanaga igaztanaga commented on cbeb5b5 Sep 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange, it only fails with cmake and ubuntu latest. b2 with older ubuntus seems fine...

Anyway, it seems that the new ABI has a bit more overhead because it properly aligns data as requested (and lockfree requires queue to be BOOST_LOCKFREE_CACHELINE_BYTES bytes aligned) so that the memory size of the segment must be slightly bigger.

Changing the following line:

https://github.com/boostorg/lockfree/blob/boost-1.86.0/test/queue_interprocess_test.cpp#L30

managed_shared_memory segment(create_only, "boost_queue_interprocess_test_shm", 262144);

with something 1% bigger (262144*101/100), fixes the test failure. I guess the previous value was selected empirically with the old implementations as the minimum size that was enough to hold the queue.

@pdimov
Copy link
Member

@pdimov pdimov commented on cbeb5b5 Sep 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timblechmann Please see above

Please sign in to comment.