diff --git a/doc/interprocess.qbk b/doc/interprocess.qbk index 24d5ed2..98a06c5 100644 --- a/doc/interprocess.qbk +++ b/doc/interprocess.qbk @@ -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"])]. diff --git a/include/boost/interprocess/detail/segment_manager_helper.hpp b/include/boost/interprocess/detail/segment_manager_helper.hpp index 8c4efbd..4e5c65f 100644 --- a/include/boost/interprocess/detail/segment_manager_helper.hpp +++ b/include/boost/interprocess/detail/segment_manager_helper.hpp @@ -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 struct block_header { @@ -91,6 +97,7 @@ struct block_header , m_alloc_type_sizeof_char( (unsigned char)((al_type << 5u) | ((unsigned char)szof_char & 0x1F)) ) {}; + template size_type total_size() const { if(alloc_type() != anonymous_type){ @@ -104,13 +111,13 @@ struct block_header size_type value_bytes() const { return m_value_bytes; } - template + template size_type total_size_with_header() const { return get_rounded_size ( size_type(sizeof(Header)) , size_type(::boost::container::dtl::alignment_of >::value)) - + total_size(); + + this->template total_size<0>(); } unsigned char alloc_type() const @@ -176,6 +183,10 @@ struct block_header return hdr; } + template + static size_type front_space() + { return 0u; } + private: size_type value_offset() const { @@ -188,26 +199,200 @@ struct block_header } }; +#elif (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI == 2) + +template +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
::value + >::value + - sizeof(BlockHeader); +}; + +template +struct prefix_offsets +{ + BOOST_INTERPROCESS_STATIC_ASSERT(MemAlignment >= boost::move_detail::alignment_of
::value); + BOOST_INTERPROCESS_STATIC_ASSERT(MemAlignment >= boost::move_detail::alignment_of::value); + + BOOST_STATIC_CONSTEXPR std::size_t both_headers = + header_to_block_header_offset::value + sizeof(BlockHeader); + + BOOST_STATIC_CONSTEXPR std::size_t total_prefix = ct_rounded_size::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 +struct prefix_offsets +{ + BOOST_INTERPROCESS_STATIC_ASSERT(MemAlignment >= boost::move_detail::alignment_of::value); + + BOOST_STATIC_CONSTEXPR std::size_t total_prefix = ct_rounded_size::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 +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 + size_type total_size() const + { + BOOST_CONSTEXPR_OR_CONST std::size_t block_header_prefix = + prefix_offsets::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 + size_type total_size_with_header() const + { + BOOST_CONSTEXPR_OR_CONST std::size_t block_header_prefix = + prefix_offsets::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 + CharType *name() const + { + return const_cast(move_detail::force_ptr + (reinterpret_cast(this) + name_offset())); + } + + unsigned short name_length() const + { return m_num_char; } + + void *value() const + { + return const_cast((reinterpret_cast(this) + this->value_offset())); + } + + template + static block_header *block_header_from_value(T *value) + { + BOOST_ASSERT(is_ptr_aligned(value, ::boost::container::dtl::alignment_of::value)); + block_header* hdr = + const_cast + (move_detail::force_ptr + (reinterpret_cast(value) - value_offset())); + + //Some sanity checks + BOOST_ASSERT(hdr->m_value_bytes % sizeof(T) == 0); + return hdr; + } + + template + static block_header *from_first_header(Header *header) + { + BOOST_ASSERT(is_ptr_aligned(header)); + block_header * const hdr = move_detail::force_ptr( + reinterpret_cast(header) + header_to_block_header_offset::value + ); + //Some sanity checks + BOOST_ASSERT(is_ptr_aligned(hdr)); + return hdr; + } + + template + static const block_header *from_first_header(const Header *header) + { return from_first_header(const_cast(header)); } + + template + static Header *to_first_header(block_header *bheader) + { + BOOST_ASSERT(is_ptr_aligned(bheader)); + Header * hdr = move_detail::force_ptr( + reinterpret_cast(bheader) - header_to_block_header_offset::value + ); + //Some sanity checks + BOOST_ASSERT(is_ptr_aligned(hdr)); + return hdr; + } + + template + static size_type front_space() + { + return prefix_offsets::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 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 @@ -377,7 +562,13 @@ struct index_config template struct intrusive_value_type - { typedef intrusive_value_type_impl type; }; + { + typedef intrusive_value_type_impl + < HeaderBase + , CharT + , typename segment_manager_base::size_type + > type; + }; typedef intrusive_compare_key compare_key_type; }; diff --git a/include/boost/interprocess/segment_manager.hpp b/include/boost/interprocess/segment_manager.hpp index 769c831..720395f 100644 --- a/include/boost/interprocess/segment_manager.hpp +++ b/include/boost/interprocess/segment_manager.hpp @@ -75,6 +75,7 @@ class segment_manager_base typedef segment_manager_base segment_manager_base_type; typedef typename MemoryAlgorithm::void_pointer void_pointer; typedef typename MemoryAlgorithm::mutex_family mutex_family; + static const std::size_t MemAlignment = MemoryAlgorithm::Alignment; typedef MemoryAlgorithm memory_algorithm; #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) @@ -310,6 +311,7 @@ class segment_manager typedef segment_manager_base segment_manager_base_type; static const size_type PayloadPerAllocation = segment_manager_base_t::PayloadPerAllocation; + static const size_type MemAlignment = segment_manager_base_t::MemAlignment; #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) private: @@ -698,6 +700,7 @@ class segment_manager { typedef typename Proxy::object_type object_type; BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment = boost::move_detail::alignment_of::value; + BOOST_CONSTEXPR_OR_CONST std::size_t alloc_alignment = t_alignment > MemAlignment ? t_alignment : MemAlignment; block_header_t block_info ( size_type(sizeof(object_type)*num) , size_type(t_alignment) , anonymous_type @@ -705,7 +708,12 @@ class segment_manager , 0); //Check if there is enough memory - void *ptr_struct = this->allocate(block_info.total_size(), nothrow<>::get()); + const std::size_t total_size = block_info.template total_size(); + #if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI < 2) + void *ptr_struct = this->allocate(total_size, nothrow<>::get()); + #else + void* ptr_struct = this->allocate_aligned(total_size, alloc_alignment, nothrow<>::get()); + #endif if(!ptr_struct){ return ipcdetail::null_or_bad_alloc(dothrow); } @@ -715,7 +723,10 @@ class segment_manager (ptr_struct, *static_cast(this)); //Now construct the header - block_header_t * hdr = ::new(ptr_struct, boost_container_new_t()) block_header_t(block_info); + const std::size_t front_space = block_header_t::template front_space< alloc_alignment, void>(); + + block_header_t * const hdr = ::new((char*)ptr_struct + front_space, boost_container_new_t()) block_header_t(block_info); + BOOST_ASSERT(is_ptr_aligned(hdr)); void *ptr = 0; //avoid gcc warning ptr = hdr->value(); @@ -724,14 +735,16 @@ class segment_manager //All constructors successful, disable rollback mem.release(); - return static_cast(ptr); + object_type* const pret = static_cast(ptr); + BOOST_ASSERT(is_ptr_aligned(pret)); + return pret; } //!Calls the destructor and makes an anonymous deallocate template void priv_anonymous_destroy(const T *object) { - + BOOST_ASSERT(is_ptr_aligned(object)); //Get control data from associated with this object block_header_t *ctrl_data = block_header_t::block_header_from_value(object); @@ -745,12 +758,19 @@ class segment_manager //Call destructors and free memory //Build scoped ptr to avoid leaks with destructor exception priv_destroy_n(object, ctrl_data->m_value_bytes/sizeof(T)); - this->deallocate(ctrl_data); + + BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment = + boost::move_detail::alignment_of::value; + BOOST_CONSTEXPR_OR_CONST std::size_t alloc_alignment = + t_alignment > MemAlignment ? t_alignment : MemAlignment; + const std::size_t front_space = block_header_t::template front_space(); + this->deallocate((char*)ctrl_data-front_space); } template void priv_destroy_ptr(const T *ptr) { + BOOST_ASSERT(is_ptr_aligned(ptr)); block_header_t *ctrl_data = block_header_t::block_header_from_value(ptr); switch(ctrl_data->alloc_type()){ case anonymous_type: @@ -909,7 +929,7 @@ class segment_manager template bool priv_generic_named_destroy_impl - (const typename IndexType >::iterator &it, + (typename IndexType >::iterator it, IndexType > &index) { typedef IndexType > index_t; @@ -926,13 +946,20 @@ class segment_manager index.erase(it); void *memory; + BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment = + boost::move_detail::alignment_of::value; + BOOST_CONSTEXPR_OR_CONST std::size_t alloc_alignment = + t_alignment > MemAlignment ? t_alignment : MemAlignment; + BOOST_IF_CONSTEXPR(is_node_index_t::value || is_intrusive_t::value){ index_data_t*ihdr = block_header_t::template to_first_header(ctrl_data); + const std::size_t front_space = block_header_t::template front_space(); + memory = (char*)ihdr - front_space; ihdr->~index_data_t(); - memory = ihdr; } else{ - memory = ctrl_data; + const std::size_t front_space = block_header_t::template front_space(); + memory = (char*)ctrl_data - front_space; } //Call destructors and free memory @@ -967,6 +994,7 @@ class segment_manager typedef typename Proxy::object_type object_type; std::size_t namelen = std::char_traits::length(name); BOOST_CONSTEXPR_OR_CONST std::size_t t_alignment = boost::move_detail::alignment_of::value; + BOOST_CONSTEXPR_OR_CONST std::size_t alloc_alignment = t_alignment > MemAlignment ? t_alignment: MemAlignment ; block_header_t block_info ( size_type(sizeof(object_type)*num) , size_type(t_alignment) @@ -1016,23 +1044,40 @@ class segment_manager //Allocates buffer for name + data, this can throw (it hurts) void *buffer_ptr; block_header_t * hdr; + std::size_t front_space; //Allocate and construct the headers BOOST_IF_CONSTEXPR(is_node_index_t::value || is_intrusive_t::value){ - size_type total_size = block_info.template total_size_with_header(); + const size_type total_size = block_info.template total_size_with_header(); + #if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI < 2) buffer_ptr = this->allocate(total_size, nothrow<>::get()); + #else + buffer_ptr = this->allocate_aligned(total_size, alloc_alignment, nothrow<>::get()); + #endif + if(!buffer_ptr) return ipcdetail::null_or_bad_alloc(dothrow); - hdr = block_header_t::template from_first_header(static_cast(buffer_ptr)); + + front_space = block_header_t::template front_space(); + hdr = block_header_t::template from_first_header(reinterpret_cast((void*)((char*)buffer_ptr+front_space))); } else{ - buffer_ptr = this->allocate(block_info.total_size(), nothrow<>::get()); + const size_type total_size = block_info.template total_size(); + #if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI < 2) + buffer_ptr = this->allocate(total_size, nothrow<>::get()); + #else + buffer_ptr = this->allocate_aligned(total_size, alloc_alignment, nothrow<>::get()); + #endif + + front_space = block_header_t::template front_space(); + //Check if there is enough memory if (!buffer_ptr) return ipcdetail::null_or_bad_alloc(dothrow); - hdr = static_cast(buffer_ptr); + hdr = reinterpret_cast((void*)((char*)buffer_ptr + front_space)); } + BOOST_ASSERT(is_ptr_aligned(hdr)); hdr = ::new(hdr, boost_container_new_t()) block_header_t(block_info); //Build scoped ptr to avoid leaks with constructor exception @@ -1048,7 +1093,8 @@ class segment_manager index_it it; BOOST_INTERPROCESS_TRY{ BOOST_IF_CONSTEXPR(is_node_index_t::value || is_intrusive_t::value) { - index_data_t* index_data = ::new(buffer_ptr, boost_container_new_t()) index_data_t(); + index_data_t* index_data = ::new((char*)buffer_ptr + front_space, boost_container_new_t()) index_data_t(); + BOOST_ASSERT(is_ptr_aligned(index_data)); it = index.insert_commit(compare_key_t(name_ptr, namelen), hdr, *index_data, commit_data); } else{ @@ -1074,7 +1120,9 @@ class segment_manager //Release rollbacks since construction was successful v_eraser.release(); mem.release(); - return static_cast(ptr); + object_type* const pret = static_cast(ptr); + BOOST_ASSERT(is_ptr_aligned(pret)); + return pret; } private: diff --git a/test/segment_manager_test.cpp b/test/segment_manager_test.cpp index dcd2273..edac653 100644 --- a/test/segment_manager_test.cpp +++ b/test/segment_manager_test.cpp @@ -8,609 +8,5 @@ // ////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "get_process_id_name.hpp" -#include -#include -#include -#include - -using namespace boost::interprocess; - -template -struct atomic_func_test -{ - SegmentManager &rsm; - int *object; - - atomic_func_test(SegmentManager &sm) - : rsm(sm), object() - {} - - void operator()() - { - object = rsm.template find("atomic_func_find_object").first; - } - private: - atomic_func_test operator=(const atomic_func_test&); - atomic_func_test(const atomic_func_test&); -}; - - -template -bool test_allocate_deallocate(SegmentManager* seg_mgr, mapped_region& mapping) -{ - typedef typename SegmentManager::size_type size_type; - {//test get_free_memory() / allocate()/deallocate() - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - const std::size_t MappedRegionSize = mapping.get_size(); - const size_type Size = MappedRegionSize / 2; - void* mem = seg_mgr->allocate(Size + 1); - const size_type free_mem = seg_mgr->get_free_memory(); - if (free_mem >= Size) - return false; - if (seg_mgr->all_memory_deallocated()) - return false; - const size_type Size2 = free_mem / 2; - void* mem2 = seg_mgr->allocate(size_type(Size2 + 1), std::nothrow); - if (seg_mgr->get_free_memory() >= Size2) - return false; - if (seg_mgr->size(mem) < (Size + 1)) - return false; - if (seg_mgr->size(mem2) < (Size2 + 1)) - return false; - seg_mgr->deallocate(mem); - seg_mgr->deallocate(mem2); - if (!seg_mgr->all_memory_deallocated()) - return false; - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - BOOST_INTERPROCESS_TRY{ seg_mgr->allocate(MappedRegionSize * 2); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - if (seg_mgr->allocate(MappedRegionSize * 2, std::nothrow)) - return false; - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - } - return true; -} - -template -bool test_allocate_aligned(SegmentManager* seg_mgr, mapped_region& mapping) -{ - const std::size_t MappedRegionSize = mapping.get_size(); - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - const std::size_t InitialAlignment = SegmentManager::memory_algorithm::Alignment; - const std::size_t RegionAlignment = mapped_region::get_page_size(); - - for( std::size_t alignment = InitialAlignment - ; (alignment <= MappedRegionSize/8 && alignment <= RegionAlignment/4) - ; alignment <<= 1u) { - - //Allocate two buffers and test the alignment inside the mapped region - void *mem = seg_mgr->allocate_aligned(MappedRegionSize/8, alignment); - if(seg_mgr->all_memory_deallocated()) - return false; - - std::size_t offset = static_cast - (static_cast(mem) - static_cast(mapping.get_address())); - if(offset & (alignment -1)) - return false; - void *mem2 = seg_mgr->allocate_aligned(MappedRegionSize/4, alignment, std::nothrow); - std::size_t offset2 = static_cast - (static_cast(mem2) - static_cast(mapping.get_address())); - if(offset2 & (alignment -1)) - return false; - - //Deallocate them - seg_mgr->deallocate(mem); - seg_mgr->deallocate(mem2); - if(!seg_mgr->all_memory_deallocated()) - return false; - if(seg_mgr->get_free_memory() != free_mem_before) - return false; - - //Try an imposible size to test error is signalled - bool allocate_aligned_throws = false; - BOOST_INTERPROCESS_TRY{ seg_mgr->allocate_aligned(MappedRegionSize*2, alignment); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&){ allocate_aligned_throws = true; } - BOOST_INTERPROCESS_CATCH_END - if (!allocate_aligned_throws) - return false; - - if (seg_mgr->allocate_aligned(MappedRegionSize*2, alignment, std::nothrow)) - return false; - - if(seg_mgr->get_free_memory() != free_mem_before) - return false; - if(seg_mgr->allocate_aligned(MappedRegionSize*2, alignment, std::nothrow)) - return false; - if(seg_mgr->get_free_memory() != free_mem_before) - return false; - } - return true; -} - -template -bool test_shrink_to_fit(SegmentManager* seg_mgr, mapped_region &) -{ - typedef typename SegmentManager::size_type size_type; - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - std::size_t size_before = seg_mgr->get_size(); - seg_mgr->shrink_to_fit(); - if (!seg_mgr->all_memory_deallocated()) - return false; - std::size_t empty_shrunk_size = seg_mgr->get_size(); - std::size_t empty_shrunk_free_mem = seg_mgr->get_free_memory(); - if (empty_shrunk_size >= size_before) - return false; - if (empty_shrunk_free_mem >= size_before) - return false; - seg_mgr->grow(size_type(size_before - empty_shrunk_size)); - if (seg_mgr->get_size() != size_before) - return false; - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - if (!seg_mgr->all_memory_deallocated()) - return false; - - return true; -} - -template -bool test_zero_free_memory(SegmentManager* seg_mgr, mapped_region &mapping) -{ - typedef typename SegmentManager::size_type size_type; - const std::size_t MappedRegionSize = mapping.get_size(); - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - const size_type Size(MappedRegionSize / 2 + 1), Size2(MappedRegionSize / 8); - void* mem = seg_mgr->allocate(Size); - void* mem2 = seg_mgr->allocate(Size2); - //Mark memory to non-zero - std::memset(mem, 0xFF, Size); - std::memset(mem2, 0xFF, Size2); - //Deallocate and check still non-zero - seg_mgr->deallocate(mem); - seg_mgr->deallocate(mem2); - { //Use byte per byte comparison as "static unsigned char zerobuf[Size]" - //seems to be problematic in some compilers - unsigned char* const mem_uch_ptr = static_cast(mem); - unsigned char* const mem2_uch_ptr = static_cast(mem2); - size_type zeroes = 0; - for (size_type i = 0; i != Size; ++i) { - if (!mem_uch_ptr[i]) - ++zeroes; - } - if (zeroes == Size) - return false; - - zeroes = 0; - for (size_type i = 0; i != Size2; ++i) { - if (!mem2_uch_ptr[i]) - ++zeroes; - } - if (zeroes == Size2) - return false; - } - //zero_free_memory and check it's zeroed - seg_mgr->zero_free_memory(); - //TODO: some parts are not zeroed because they are used - //as internal metadata, find a way to test this - //if(std::memcmp(mem, zerobuf, Size)) - //return false; - //if(std::memcmp(mem2, zerobuf, Size2)) - //return false; - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - if (!seg_mgr->all_memory_deallocated()) - return false; - return true; -} - -template -bool test_anoymous_object(SegmentManager* seg_mgr, mapped_region& mapping) -{ - const std::size_t MappedRegionSize = mapping.get_size(); - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - - int* int_object = seg_mgr->template construct(anonymous_instance)(); - if (1 != seg_mgr->get_instance_length(int_object)) - return false; - if (anonymous_type != seg_mgr->get_instance_type(int_object)) - return false; - if (seg_mgr->get_instance_name(int_object)) - return false; - seg_mgr->destroy_ptr(int_object); - int const int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - int* int_array = seg_mgr->template construct_it(anonymous_instance, std::nothrow)[10](&int_array_values[0]); - if (10 != seg_mgr->get_instance_length(int_object)) - return false; - if (anonymous_type != seg_mgr->get_instance_type(int_array)) - return false; - if (seg_mgr->get_instance_name(int_array)) - return false; - seg_mgr->destroy_ptr(int_array); - BOOST_INTERPROCESS_TRY{ seg_mgr->template construct(anonymous_instance)[MappedRegionSize](); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->template construct(anonymous_instance, std::nothrow)[MappedRegionSize]()) - BOOST_INTERPROCESS_TRY{ seg_mgr->template construct_it(anonymous_instance)[MappedRegionSize](&int_array_values[0]); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->template construct_it(anonymous_instance, std::nothrow)[MappedRegionSize](&int_array_values[0])) - return false; - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - if (!seg_mgr->all_memory_deallocated()) - return false; - return true; -} - -template -bool test_named_object(SegmentManager* seg_mgr, mapped_region& mapping) -{ - const std::size_t MappedRegionSize = mapping.get_size(); - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - - const char* const object1_name = "object1"; - const char* const object2_name = "object2"; - int const int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - for (std::size_t i = 0; i != 4; ++i) { - if (seg_mgr->template find(object1_name).first) - return false; - //Single element construction - unsigned int* uint_object = 0; - switch (i) { - case 0: - uint_object = seg_mgr->template construct(object1_name)(); - break; - case 1: - uint_object = seg_mgr->template construct(object1_name, std::nothrow)(); - break; - case 2: - uint_object = seg_mgr->template find_or_construct(object1_name)(); - break; - case 3: - uint_object = seg_mgr->template find_or_construct(object1_name, std::nothrow)(); - break; - } - std::pair find_ret = seg_mgr->template find(object1_name); - if (uint_object != find_ret.first) - return false; - if (1 != find_ret.second) - return false; - if (1 != seg_mgr->get_instance_length(uint_object)) - return false; - if (named_type != seg_mgr->get_instance_type(uint_object)) - return false; - if (std::strcmp(object1_name, seg_mgr->get_instance_name(uint_object))) - return false; - //Array construction - if (seg_mgr->template find(object2_name).first) - return false; - int* int_array = 0; - switch (i) { - case 0: - int_array = seg_mgr->template construct_it(object2_name)[10](&int_array_values[0]); - break; - case 1: - int_array = seg_mgr->template construct_it(object2_name, std::nothrow)[10](&int_array_values[0]); - break; - case 2: - int_array = seg_mgr->template find_or_construct_it(object2_name)[10](&int_array_values[0]); - break; - case 3: - int_array = seg_mgr->template find_or_construct_it(object2_name, std::nothrow)[10](&int_array_values[0]); - break; - } - std::pair find_ret2 = seg_mgr->template find(object2_name); - if (int_array != find_ret2.first) - return false; - if (10 != find_ret2.second) - return false; - if (10 != seg_mgr->get_instance_length(int_array)) - return false; - if (named_type != seg_mgr->get_instance_type(int_array)) - return false; - if (std::strcmp(object2_name, seg_mgr->get_instance_name(int_array))) - return false; - if (seg_mgr->get_num_named_objects() != 2) - return false; - typename SegmentManager::const_named_iterator nb(seg_mgr->named_begin()); - typename SegmentManager::const_named_iterator ne(seg_mgr->named_end()); - for (std::size_t j = 0, imax = seg_mgr->get_num_named_objects(); j != imax; ++j) { ++nb; } - if (nb != ne) - return false; - seg_mgr->destroy_ptr(uint_object); - seg_mgr->template destroy(object2_name); - } - BOOST_INTERPROCESS_TRY{ seg_mgr->template construct(object1_name)[MappedRegionSize](); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->template construct(object2_name, std::nothrow)[MappedRegionSize]()) - BOOST_INTERPROCESS_TRY{ seg_mgr->template construct_it(object1_name)[MappedRegionSize](&int_array_values[0]); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->template construct_it(object2_name, std::nothrow)[MappedRegionSize](&int_array_values[0])) - return false; - seg_mgr->shrink_to_fit_indexes(); - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - if (!seg_mgr->all_memory_deallocated()) - return false; - seg_mgr->reserve_named_objects(1); - //In indexes with no capacity() memory won't be allocated so don't check anything was allocated. - //if(seg_mgr->all_memory_deallocated()) return false; - seg_mgr->shrink_to_fit_indexes(); - if (!seg_mgr->all_memory_deallocated()) - return false; - return true; -} - -template -bool test_unique_object(SegmentManager* seg_mgr, mapped_region& mapping) -{ - const std::size_t MappedRegionSize = mapping.get_size(); - const std::size_t free_mem_before = seg_mgr->get_free_memory(); - - int const int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - for (std::size_t i = 0; i != 4; ++i) { - if (seg_mgr->template find(unique_instance).first) - return false; - //Single element construction - unsigned int* uint_object = 0; - switch (i) { - case 0: - uint_object = seg_mgr->template construct(unique_instance)(); - break; - case 1: - uint_object = seg_mgr->template construct(unique_instance, std::nothrow)(); - break; - case 2: - uint_object = seg_mgr->template find_or_construct(unique_instance)(); - break; - case 3: - uint_object = seg_mgr->template find_or_construct(unique_instance, std::nothrow)(); - break; - } - std::pair find_ret = seg_mgr->template find(unique_instance); - if (uint_object != find_ret.first) - return false; - if (1 != find_ret.second) - return false; - if (1 != seg_mgr->get_instance_length(uint_object)) - return false; - if (unique_type != seg_mgr->get_instance_type(uint_object)) - return false; - if (std::strcmp(typeid(unsigned int).name(), seg_mgr->get_instance_name(uint_object))) - return false; - //Array construction - if (seg_mgr->template find(unique_instance).first) - return false; - int* int_array = 0; - switch (i) { - case 0: - int_array = seg_mgr->template construct_it(unique_instance)[10](&int_array_values[0]); - break; - case 1: - int_array = seg_mgr->template construct_it(unique_instance, std::nothrow)[10](&int_array_values[0]); - break; - case 2: - int_array = seg_mgr->template find_or_construct_it(unique_instance)[10](&int_array_values[0]); - break; - case 3: - int_array = seg_mgr->template find_or_construct_it(unique_instance, std::nothrow)[10](&int_array_values[0]); - break; - } - std::pair find_ret2 = seg_mgr->template find(unique_instance); - if (int_array != find_ret2.first) - return false; - if (10 != find_ret2.second) - return false; - if (10 != seg_mgr->get_instance_length(int_array)) - return false; - if (unique_type != seg_mgr->get_instance_type(int_array)) - return false; - if (std::strcmp(typeid(int).name(), seg_mgr->get_instance_name(int_array))) - return false; - if (seg_mgr->get_num_unique_objects() != 2) - return false; - typename SegmentManager::const_unique_iterator nb(seg_mgr->unique_begin()); - typename SegmentManager::const_unique_iterator ne(seg_mgr->unique_end()); - for (std::size_t j = 0, imax = seg_mgr->get_num_unique_objects(); j != imax; ++j) { ++nb; } - if (nb != ne) - return false; - seg_mgr->destroy_ptr(uint_object); - seg_mgr->template destroy(unique_instance); - } - BOOST_INTERPROCESS_TRY{ seg_mgr->template construct(unique_instance)[MappedRegionSize](); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->template construct(unique_instance, std::nothrow)[MappedRegionSize]()) - BOOST_INTERPROCESS_TRY{ seg_mgr->template construct_it(unique_instance)[MappedRegionSize](&int_array_values[0]); } - BOOST_INTERPROCESS_CATCH(interprocess_exception&) {} - BOOST_INTERPROCESS_CATCH_END - if (seg_mgr->template construct_it(unique_instance, std::nothrow)[MappedRegionSize](&int_array_values[0])) - return false; - seg_mgr->shrink_to_fit_indexes(); - if (seg_mgr->get_free_memory() != free_mem_before) - return false; - if (!seg_mgr->all_memory_deallocated()) - return false; - seg_mgr->reserve_unique_objects(1); - //In indexes with no capacity() memory won't be allocated so don't check anything was allocated. - //if(seg_mgr->all_memory_deallocated()) return false; - seg_mgr->shrink_to_fit_indexes(); - if (!seg_mgr->all_memory_deallocated()) - return false; - return true; -} -template -bool test_atomic_func(SegmentManager* seg_mgr, mapped_region& ) -{ - if (!seg_mgr->all_memory_deallocated()) - return false; - int* int_object = seg_mgr->template construct("atomic_func_find_object")(); - atomic_func_test func(*seg_mgr); - seg_mgr->atomic_func(func); - if (int_object != func.object) - return 1; - seg_mgr->destroy_ptr(int_object); - seg_mgr->shrink_to_fit_indexes(); - if (!seg_mgr->all_memory_deallocated()) - return false; - return true; -} - -template -bool test_allocator_deleter(SegmentManager* seg_mgr, mapped_region&) -{//test allocator/deleter - if (!seg_mgr->all_memory_deallocated()) - return false; - typedef typename SegmentManager::template allocator::type allocator_t; - - allocator_t alloc(seg_mgr->template get_allocator()); - - if (!seg_mgr->all_memory_deallocated()) - return false; - offset_ptr f = alloc.allocate(50); - if (seg_mgr->all_memory_deallocated()) - return false; - alloc.deallocate(f, 50); - if (!seg_mgr->all_memory_deallocated()) - return false; - typedef typename SegmentManager::template deleter::type deleter_t; - deleter_t delet(seg_mgr->template get_deleter()); - delet(seg_mgr->template construct(anonymous_instance)()); - if (!seg_mgr->all_memory_deallocated()) - return false; - return true; -} - -template -bool test_get_memory_algorithm(SegmentManager* seg_mgr, mapped_region&) -{ - { - typename SegmentManager::memory_algorithm& mem_algo = - seg_mgr->get_memory_algorithm(); - const typename SegmentManager::memory_algorithm& const_mem_algo = - const_cast(seg_mgr)->get_memory_algorithm(); - if (&mem_algo != &const_mem_algo) - return false; - } - return true; -} - - -template -bool test_segment_manager() -{ - const unsigned int MappedRegionSize = 1024*64u; - std::string shmname(test::get_process_id_name()); - - shared_memory_object::remove(shmname.c_str()); - shared_memory_object sh_mem( create_only, shmname.c_str(), read_write ); - sh_mem.truncate( MappedRegionSize ); - mapped_region mapping( sh_mem, read_write ); - - //Remove shared memory to minimize risk of garbage on crash - shared_memory_object::remove(shmname.c_str()); - - SegmentManager* seg_mgr = new( mapping.get_address() ) SegmentManager( MappedRegionSize ); - std::size_t size_before = seg_mgr->get_size(); - - if(size_before != MappedRegionSize) - return false; - if(!seg_mgr->all_memory_deallocated()) - return false; - if(seg_mgr->get_min_size() >= MappedRegionSize) - return false; - - if (!test_allocate_deallocate(seg_mgr, mapping)) - return false; - - if (!test_allocate_aligned(seg_mgr, mapping)) - return false; - - if (!test_shrink_to_fit(seg_mgr, mapping)) - return false; - - if (!test_zero_free_memory(seg_mgr, mapping)) - return false; - - if (!test_anoymous_object(seg_mgr, mapping)) - return false; - - if (!test_named_object(seg_mgr, mapping)) - return false; - - if (!test_unique_object(seg_mgr, mapping)) - return false; - - if (!test_allocator_deleter(seg_mgr, mapping)) - return false; - - if (!test_atomic_func(seg_mgr, mapping)) - return false; - - if (!test_allocator_deleter(seg_mgr, mapping)) - return false; - - if (!test_get_memory_algorithm(seg_mgr, mapping)) - return false; - - return true; -} - -template -bool test_each_algo() -{ - { - typedef segment_manager< char, MemoryAlgorithm, flat_map_index > segment_manager_t; - if(!test_segment_manager()) - return false; - } - { - typedef segment_manager< char, MemoryAlgorithm, map_index > segment_manager_t; - if(!test_segment_manager()) - return false; - } - { - typedef segment_manager< char, MemoryAlgorithm, iset_index > segment_manager_t; - if(!test_segment_manager()) - return false; - } - { - typedef segment_manager< char, MemoryAlgorithm, iunordered_set_index > segment_manager_t; - if(!test_segment_manager()) - return false; - } - return true; -} - -int main() -{ - if(!test_each_algo< simple_seq_fit< null_mutex_family > >()) - return 1; - if(!test_each_algo< rbtree_best_fit< null_mutex_family > >()) - return 1; - - return 0; -} +#define BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI 2 +#include "segment_manager_test.hpp" diff --git a/test/segment_manager_test.hpp b/test/segment_manager_test.hpp new file mode 100644 index 0000000..87713af --- /dev/null +++ b/test/segment_manager_test.hpp @@ -0,0 +1,860 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2004-2019. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/interprocess for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "get_process_id_name.hpp" +#include +#include +#include +#include +#include + +using namespace boost::interprocess; + +template +struct IntLike; + +#define BOOST_INTERPROCESS_ALIGNED_INTLIKE(A)\ +template<>\ +struct IntLike\ +{\ + IntLike(){}\ +\ + IntLike(int i) : data(i) {}\ +\ + BOOST_ALIGNMENT(A) int data;\ +\ + operator int() const { return data; }\ +};\ +// + +//Up to 4K alignment (typical page size) +BOOST_INTERPROCESS_ALIGNED_INTLIKE(16) +BOOST_INTERPROCESS_ALIGNED_INTLIKE(32) +BOOST_INTERPROCESS_ALIGNED_INTLIKE(64) +BOOST_INTERPROCESS_ALIGNED_INTLIKE(128) +BOOST_INTERPROCESS_ALIGNED_INTLIKE(256) + +#undef BOOST_INTERPROCESS_ALIGNED_INTLIKE + +template +struct atomic_func_test +{ + SegmentManager &rsm; + int *object; + + atomic_func_test(SegmentManager &sm) + : rsm(sm), object() + {} + + void operator()() + { + object = rsm.template find("atomic_func_find_object").first; + } + private: + atomic_func_test operator=(const atomic_func_test&); + atomic_func_test(const atomic_func_test&); +}; + + +template +bool test_allocate_deallocate(SegmentManager* seg_mgr, mapped_region& mapping) +{ + typedef typename SegmentManager::size_type size_type; + const std::size_t MappedRegionSize = mapping.get_size(); + + for (std::size_t size = 1; size <= (MappedRegionSize / 2); size <<= 1 ) { + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + + //Allocate memory + void* mem = seg_mgr->allocate(size + 1); + const size_type free_mem = seg_mgr->get_free_memory(); + if (free_mem >= (free_mem_before-size)) + return false; + if (seg_mgr->all_memory_deallocated()) + return false; + //Allocate half of the rest + const size_type Size2 = free_mem / 2; + void* mem2 = seg_mgr->allocate(size_type(Size2 + 1), std::nothrow); + + //Sanity checks + if (seg_mgr->get_free_memory() >= Size2) + return false; + if (seg_mgr->size(mem) < (size + 1)) + return false; + if (seg_mgr->size(mem2) < (Size2 + 1)) + return false; + + //Deallocate both + seg_mgr->deallocate(mem); + seg_mgr->deallocate(mem2); + + //Sanity checks again + if (!seg_mgr->all_memory_deallocated()) + return false; + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + + //Try an imposible size to test error is signalled + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->allocate(MappedRegionSize * 2); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&) { operation_throws = true; } + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + + if (seg_mgr->allocate(MappedRegionSize*2u, std::nothrow)) + return false; + + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + } + return true; +} + +template +bool test_allocate_aligned(SegmentManager* seg_mgr, mapped_region& mapping) +{ + const std::size_t MappedRegionSize = mapping.get_size(); + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + const std::size_t InitialAlignment = SegmentManager::memory_algorithm::Alignment; + const std::size_t RegionAlignment = mapped_region::get_page_size(); + + for( std::size_t alignment = InitialAlignment + ; (alignment <= MappedRegionSize/8 && alignment <= RegionAlignment/4) + ; alignment <<= 1u) { + + //Allocate two buffers and test the alignment inside the mapped region + void *mem = seg_mgr->allocate_aligned(MappedRegionSize/8, alignment); + if(seg_mgr->all_memory_deallocated()) + return false; + + std::size_t offset = static_cast + (static_cast(mem) - static_cast(mapping.get_address())); + if(offset & (alignment -1)) + return false; + void *mem2 = seg_mgr->allocate_aligned(MappedRegionSize/4, alignment, std::nothrow); + std::size_t offset2 = static_cast + (static_cast(mem2) - static_cast(mapping.get_address())); + if(offset2 & (alignment -1)) + return false; + + //Deallocate them + seg_mgr->deallocate(mem); + seg_mgr->deallocate(mem2); + if(!seg_mgr->all_memory_deallocated()) + return false; + if(seg_mgr->get_free_memory() != free_mem_before) + return false; + + //Try an imposible size to test error is signalled + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->allocate_aligned(MappedRegionSize*2, alignment); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&){ operation_throws = true; } + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + + if (seg_mgr->allocate_aligned(MappedRegionSize*2, alignment, std::nothrow)) + return false; + if(seg_mgr->get_free_memory() != free_mem_before) + return false; + if(seg_mgr->allocate_aligned(MappedRegionSize*2, alignment, std::nothrow)) + return false; + if(seg_mgr->get_free_memory() != free_mem_before) + return false; + } + return true; +} + +template +bool test_shrink_to_fit(SegmentManager* seg_mgr, mapped_region &) +{ + typedef typename SegmentManager::size_type size_type; + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + std::size_t size_before = seg_mgr->get_size(); + seg_mgr->shrink_to_fit(); + if (!seg_mgr->all_memory_deallocated()) + return false; + std::size_t empty_shrunk_size = seg_mgr->get_size(); + std::size_t empty_shrunk_free_mem = seg_mgr->get_free_memory(); + if (empty_shrunk_size >= size_before) + return false; + if (empty_shrunk_free_mem >= size_before) + return false; + seg_mgr->grow(size_type(size_before - empty_shrunk_size)); + if (seg_mgr->get_size() != size_before) + return false; + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + if (!seg_mgr->all_memory_deallocated()) + return false; + + return true; +} + +template +bool test_zero_free_memory(SegmentManager* seg_mgr, mapped_region &mapping) +{ + typedef typename SegmentManager::size_type size_type; + const std::size_t MappedRegionSize = mapping.get_size(); + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + const size_type Size(MappedRegionSize / 2 + 1), Size2(MappedRegionSize / 8); + void* mem = seg_mgr->allocate(Size); + void* mem2 = seg_mgr->allocate(Size2); + //Mark memory to non-zero + std::memset(mem, 0xFF, Size); + std::memset(mem2, 0xFF, Size2); + //Deallocate and check still non-zero + seg_mgr->deallocate(mem); + seg_mgr->deallocate(mem2); + { //Use byte per byte comparison as "static unsigned char zerobuf[Size]" + //seems to be problematic in some compilers + unsigned char* const mem_uch_ptr = static_cast(mem); + unsigned char* const mem2_uch_ptr = static_cast(mem2); + size_type zeroes = 0; + for (size_type i = 0; i != Size; ++i) { + if (!mem_uch_ptr[i]) + ++zeroes; + } + if (zeroes == Size) + return false; + + zeroes = 0; + for (size_type i = 0; i != Size2; ++i) { + if (!mem2_uch_ptr[i]) + ++zeroes; + } + if (zeroes == Size2) + return false; + } + //zero_free_memory and check it's zeroed + seg_mgr->zero_free_memory(); + //TODO: some parts are not zeroed because they are used + //as internal metadata, find a way to test this + //if(std::memcmp(mem, zerobuf, Size)) + //return false; + //if(std::memcmp(mem2, zerobuf, Size2)) + //return false; + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + if (!seg_mgr->all_memory_deallocated()) + return false; + return true; +} + + +template +bool test_anonymous_object_type(SegmentManager* seg_mgr, mapped_region& mapping) +{ + const std::size_t MappedRegionSize = mapping.get_size(); + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + + //Construct single object + { + IntLike* int_object = seg_mgr->template construct(anonymous_instance)(); + BOOST_ASSERT(is_ptr_aligned(int_object, boost::move_detail::alignment_of::value)); + if (!is_ptr_aligned(int_object, boost::move_detail::alignment_of::value)) + return false; + if (1 != seg_mgr->get_instance_length(int_object)) + return false; + if (anonymous_type != seg_mgr->get_instance_type(int_object)) + return false; + if (seg_mgr->get_instance_name(int_object)) + return false; + seg_mgr->destroy_ptr(int_object); + } + { + //Construct array object + IntLike* int_array = seg_mgr->template construct_it(anonymous_instance, std::nothrow)[5](); + BOOST_ASSERT(is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)); + if (!is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)) + return false; + if (5 != seg_mgr->get_instance_length(int_array)) + return false; + if (anonymous_type != seg_mgr->get_instance_type(int_array)) + return false; + if (seg_mgr->get_instance_name(int_array)) + return false; + seg_mgr->destroy_ptr(int_array); + } + { + //Construct array object from it + const signed char int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + IntLike* int_array = seg_mgr->template construct_it(anonymous_instance, std::nothrow)[10](&int_array_values[0]); + BOOST_ASSERT(is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)); + if (!is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)) + return false; + if (10 != seg_mgr->get_instance_length(int_array)) + return false; + if (anonymous_type != seg_mgr->get_instance_type(int_array)) + return false; + if (seg_mgr->get_instance_name(int_array)) + return false; + seg_mgr->destroy_ptr(int_array); + } + + //Try an imposible size to test error is signalled + { + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->template construct(anonymous_instance)[MappedRegionSize](); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&) { operation_throws = true; } + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + } + { + if (seg_mgr->template construct(anonymous_instance, std::nothrow)[MappedRegionSize]()) + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + } + { + const signed char int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + if (seg_mgr->template construct_it(anonymous_instance, std::nothrow)[MappedRegionSize](&int_array_values[0])) + return false; + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + } + + if (!seg_mgr->all_memory_deallocated()) + return false; + return true; +} + +template +bool test_anonymous_object(SegmentManager* seg_mgr, mapped_region& mapping) +{ + if (!test_anonymous_object_type(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type(seg_mgr, mapping)) + return false; + #if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI >= 2) + if (!test_anonymous_object_type(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type >(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type >(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type >(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object_type >(seg_mgr, mapping)) + return false; + #endif //#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI >= 2) + return true; +} + +template +bool test_named_object_type(SegmentManager* seg_mgr, mapped_region& mapping) +{ + const std::size_t MappedRegionSize = mapping.get_size(); + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + + const char* const object1_name = "object1"; + const char* const object2_name = "object2"; + const signed char int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + for (std::size_t i = 0; i != 4; ++i) { + if (seg_mgr->template find(object1_name).first) + return false; + //Single element construction + IntLike* int_object = 0; + switch (i) { + case 0: + int_object = seg_mgr->template construct(object1_name)(); + break; + case 1: + int_object = seg_mgr->template construct(object1_name, std::nothrow)(); + break; + case 2: + int_object = seg_mgr->template find_or_construct(object1_name)(); + break; + case 3: + int_object = seg_mgr->template find_or_construct(object1_name, std::nothrow)(); + break; + } + + + if (!is_ptr_aligned(int_object, boost::move_detail::alignment_of::value)){ + std::cout << "\ntype/alignment: " << typeid(IntLike).name() << "/" << boost::move_detail::alignment_of::value << "\n segment_manager: " << typeid(SegmentManager).name() + << "\nmem alignment: " << SegmentManager::memory_algorithm::Alignment < find_ret = seg_mgr->template find(object1_name); + if (int_object != find_ret.first) + return false; + if (1 != find_ret.second) + return false; + if (1 != seg_mgr->get_instance_length(int_object)) + return false; + if (named_type != seg_mgr->get_instance_type(int_object)) + return false; + if (std::strcmp(object1_name, seg_mgr->get_instance_name(int_object))) + return false; + + //Array construction + if (seg_mgr->template find(object2_name).first) + return false; + IntLike* int_array = 0; + switch (i) { + case 0: + int_array = seg_mgr->template construct_it(object2_name)[10](&int_array_values[0]); + break; + case 1: + int_array = seg_mgr->template construct_it(object2_name, std::nothrow)[10](&int_array_values[0]); + break; + case 2: + int_array = seg_mgr->template find_or_construct_it(object2_name)[10](&int_array_values[0]); + break; + case 3: + int_array = seg_mgr->template find_or_construct_it(object2_name, std::nothrow)[10](&int_array_values[0]); + break; + } + + BOOST_ASSERT(is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)); + if (!is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)) + return false; + + std::pair find_ret2 = seg_mgr->template find(object2_name); + if (int_array != find_ret2.first) + return false; + if (10 != find_ret2.second) + return false; + if (10 != seg_mgr->get_instance_length(int_array)) + return false; + if (named_type != seg_mgr->get_instance_type(int_array)) + return false; + if (std::strcmp(object2_name, seg_mgr->get_instance_name(int_array))) + return false; + if (seg_mgr->get_num_named_objects() != 2) + return false; + typename SegmentManager::const_named_iterator nb(seg_mgr->named_begin()); + typename SegmentManager::const_named_iterator ne(seg_mgr->named_end()); + for (std::size_t j = 0, imax = seg_mgr->get_num_named_objects(); j != imax; ++j) { ++nb; } + if (nb != ne) + return false; + seg_mgr->destroy_ptr(int_object); + seg_mgr->template destroy(object2_name); + } + + //Try an imposible size to test error is signalled + { + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->template construct(object1_name)[MappedRegionSize](); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&) { operation_throws = true;} + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + + if (seg_mgr->template construct(object2_name, std::nothrow)[MappedRegionSize]()) + return false; + } + { + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->template construct_it(object1_name)[MappedRegionSize](&int_array_values[0]); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&) { operation_throws = true; } + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + + if (seg_mgr->template construct_it(object2_name, std::nothrow)[MappedRegionSize](&int_array_values[0])) + return false; + } + + seg_mgr->shrink_to_fit_indexes(); + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + if (!seg_mgr->all_memory_deallocated()) + return false; + seg_mgr->reserve_named_objects(1); + + //In indexes with no capacity() memory won't be allocated so don't check anything was allocated. + //if(seg_mgr->all_memory_deallocated()) return false; + seg_mgr->shrink_to_fit_indexes(); + if (!seg_mgr->all_memory_deallocated()) + return false; + return true; +} + +template +bool test_named_object(SegmentManager* seg_mgr, mapped_region& mapping) +{ + if (!test_named_object_type(seg_mgr, mapping)) + return false; + + if (!test_named_object_type(seg_mgr, mapping)) + return false; + + if (!test_named_object_type(seg_mgr, mapping)) + return false; + + if (!test_named_object_type(seg_mgr, mapping)) + return false; + #if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI >= 2) + if (!test_named_object_type(seg_mgr, mapping)) + return false; + + if (!test_named_object_type >(seg_mgr, mapping)) + return false; + + if (!test_named_object_type >(seg_mgr, mapping)) + return false; + + if (!test_named_object_type >(seg_mgr, mapping)) + return false; + + if (!test_named_object_type >(seg_mgr, mapping)) + return false; + #endif //#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI >= 2) + return true; +} + +template +bool test_unique_object_type(SegmentManager* seg_mgr, mapped_region& mapping) +{ + const std::size_t MappedRegionSize = mapping.get_size(); + const std::size_t free_mem_before = seg_mgr->get_free_memory(); + + const signed char int_array_values[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + for (std::size_t i = 0; i != 4; ++i) { + if (seg_mgr->template find(unique_instance).first) + return false; + //Single element construction + IntLike* int_object = 0; + switch (i) { + case 0: + int_object = seg_mgr->template construct(unique_instance)(); + break; + case 1: + int_object = seg_mgr->template construct(unique_instance, std::nothrow)(); + break; + case 2: + int_object = seg_mgr->template find_or_construct(unique_instance)(); + break; + case 3: + int_object = seg_mgr->template find_or_construct(unique_instance, std::nothrow)(); + break; + } + + BOOST_ASSERT(is_ptr_aligned(int_object, boost::move_detail::alignment_of::value)); + if (!is_ptr_aligned(int_object, boost::move_detail::alignment_of::value)) + return false; + + std::pair find_ret = seg_mgr->template find(unique_instance); + if (int_object != find_ret.first) + return false; + if (1 != find_ret.second) + return false; + if (1 != seg_mgr->get_instance_length(int_object)) + return false; + if (unique_type != seg_mgr->get_instance_type(int_object)) + return false; + if (std::strcmp(typeid(IntLike).name(), seg_mgr->get_instance_name(int_object))) + return false; + //Array construction + if (!seg_mgr->template find(unique_instance).first) + return false; + + seg_mgr->destroy_ptr(int_object); + + IntLike* int_array = 0; + switch (i) { + case 0: + int_array = seg_mgr->template construct_it(unique_instance)[10](&int_array_values[0]); + break; + case 1: + int_array = seg_mgr->template construct_it(unique_instance, std::nothrow)[10](&int_array_values[0]); + break; + case 2: + int_array = seg_mgr->template find_or_construct_it(unique_instance)[10](&int_array_values[0]); + break; + case 3: + int_array = seg_mgr->template find_or_construct_it(unique_instance, std::nothrow)[10](&int_array_values[0]); + break; + } + + BOOST_ASSERT(is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)); + if (!is_ptr_aligned(int_array, boost::move_detail::alignment_of::value)) + return false; + + std::pair find_ret2 = seg_mgr->template find(unique_instance); + if (int_array != find_ret2.first) + return false; + if (10 != find_ret2.second) + return false; + if (10 != seg_mgr->get_instance_length(int_array)) + return false; + if (unique_type != seg_mgr->get_instance_type(int_array)) + return false; + if (std::strcmp(typeid(IntLike).name(), seg_mgr->get_instance_name(int_array))) + return false; + if (seg_mgr->get_num_unique_objects() != 1) + return false; + typename SegmentManager::const_unique_iterator nb(seg_mgr->unique_begin()); + typename SegmentManager::const_unique_iterator ne(seg_mgr->unique_end()); + for (std::size_t j = 0, imax = seg_mgr->get_num_unique_objects(); j != imax; ++j) { ++nb; } + if (nb != ne) + return false; + seg_mgr->template destroy(unique_instance); + } + //Try an imposible size to test error is signalled + { + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->template construct(unique_instance)[MappedRegionSize](); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&) { operation_throws = true; } + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + if (seg_mgr->template construct(unique_instance, std::nothrow)[MappedRegionSize]()) + return false; + } + { + bool operation_throws = false; + BOOST_INTERPROCESS_TRY{ seg_mgr->template construct_it(unique_instance)[MappedRegionSize](&int_array_values[0]); } + BOOST_INTERPROCESS_CATCH(interprocess_exception&) { operation_throws = true; } + BOOST_INTERPROCESS_CATCH_END + if (!operation_throws) + return false; + if (seg_mgr->template construct_it(unique_instance, std::nothrow)[MappedRegionSize](&int_array_values[0])) + return false; + } + + seg_mgr->shrink_to_fit_indexes(); + + if (seg_mgr->get_free_memory() != free_mem_before) + return false; + if (!seg_mgr->all_memory_deallocated()) + return false; + + seg_mgr->reserve_unique_objects(1); + + //In indexes with no capacity() memory won't be allocated so don't check anything was allocated. + //if(seg_mgr->all_memory_deallocated()) return false; + seg_mgr->shrink_to_fit_indexes(); + if (!seg_mgr->all_memory_deallocated()) + return false; + return true; +} + +template +bool test_unique_object(SegmentManager* seg_mgr, mapped_region& mapping) +{ + if (!test_unique_object_type(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type(seg_mgr, mapping)) + return false; + #if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI >= 2) + if (!test_unique_object_type(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type >(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type >(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type >(seg_mgr, mapping)) + return false; + + if (!test_unique_object_type >(seg_mgr, mapping)) + return false; + #endif //#if (BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI >= 2) + return true; +} + +template +bool test_atomic_func(SegmentManager* seg_mgr, mapped_region& ) +{ + if (!seg_mgr->all_memory_deallocated()) + return false; + int* int_object = seg_mgr->template construct("atomic_func_find_object")(); + atomic_func_test func(*seg_mgr); + seg_mgr->atomic_func(func); + if (int_object != func.object) + return 1; + seg_mgr->destroy_ptr(int_object); + seg_mgr->shrink_to_fit_indexes(); + if (!seg_mgr->all_memory_deallocated()) + return false; + return true; +} + +template +bool test_allocator_deleter(SegmentManager* seg_mgr, mapped_region&) +{//test allocator/deleter + if (!seg_mgr->all_memory_deallocated()) + return false; + typedef typename SegmentManager::template allocator::type allocator_t; + + allocator_t alloc(seg_mgr->template get_allocator()); + + if (!seg_mgr->all_memory_deallocated()) + return false; + offset_ptr f = alloc.allocate(50); + if (seg_mgr->all_memory_deallocated()) + return false; + alloc.deallocate(f, 50); + if (!seg_mgr->all_memory_deallocated()) + return false; + typedef typename SegmentManager::template deleter::type deleter_t; + deleter_t delet(seg_mgr->template get_deleter()); + delet(seg_mgr->template construct(anonymous_instance)()); + if (!seg_mgr->all_memory_deallocated()) + return false; + return true; +} + +template +bool test_get_memory_algorithm(SegmentManager* seg_mgr, mapped_region&) +{ + { + typename SegmentManager::memory_algorithm& mem_algo = + seg_mgr->get_memory_algorithm(); + const typename SegmentManager::memory_algorithm& const_mem_algo = + const_cast(seg_mgr)->get_memory_algorithm(); + if (&mem_algo != &const_mem_algo) + return false; + } + return true; +} + + +template +bool test_segment_manager() +{ + const unsigned int MappedRegionSize = 1024*64u; + std::string shmname(test::get_process_id_name()); + + shared_memory_object::remove(shmname.c_str()); + shared_memory_object sh_mem( create_only, shmname.c_str(), read_write ); + sh_mem.truncate( MappedRegionSize ); + mapped_region mapping( sh_mem, read_write ); + + //Remove shared memory to minimize risk of garbage on crash + shared_memory_object::remove(shmname.c_str()); + + SegmentManager* seg_mgr = new( mapping.get_address() ) SegmentManager( MappedRegionSize ); + std::size_t size_before = seg_mgr->get_size(); + + if(size_before != MappedRegionSize) + return false; + if(!seg_mgr->all_memory_deallocated()) + return false; + if(seg_mgr->get_min_size() >= MappedRegionSize) + return false; + + if (!test_allocate_deallocate(seg_mgr, mapping)) + return false; + + if (!test_allocate_aligned(seg_mgr, mapping)) + return false; + + if (!test_shrink_to_fit(seg_mgr, mapping)) + return false; + + if (!test_zero_free_memory(seg_mgr, mapping)) + return false; + + if (!test_anonymous_object(seg_mgr, mapping)) + return false; + + if (!test_named_object(seg_mgr, mapping)) + return false; + + if (!test_unique_object(seg_mgr, mapping)) + return false; + + if (!test_allocator_deleter(seg_mgr, mapping)) + return false; + + if (!test_atomic_func(seg_mgr, mapping)) + return false; + + if (!test_allocator_deleter(seg_mgr, mapping)) + return false; + + if (!test_get_memory_algorithm(seg_mgr, mapping)) + return false; + + return true; +} + +template +bool test_each_algo() +{ + { + typedef segment_manager< char, MemoryAlgorithm, flat_map_index > segment_manager_t; + if(!test_segment_manager()) + return false; + } + { + typedef segment_manager< char, MemoryAlgorithm, map_index > segment_manager_t; + if(!test_segment_manager()) + return false; + } + { + typedef segment_manager< char, MemoryAlgorithm, iset_index > segment_manager_t; + if(!test_segment_manager()) + return false; + } + { + typedef segment_manager< char, MemoryAlgorithm, iunordered_set_index > segment_manager_t; + if(!test_segment_manager()) + return false; + } + return true; +} + +int main() +{ + if(!test_each_algo< simple_seq_fit< null_mutex_family > >()) + return 1; + if(!test_each_algo< rbtree_best_fit< null_mutex_family > >()) + return 1; + + return 0; +} diff --git a/test/segment_manager_v1_test.cpp b/test/segment_manager_v1_test.cpp new file mode 100644 index 0000000..9092a98 --- /dev/null +++ b/test/segment_manager_v1_test.cpp @@ -0,0 +1,12 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2004-2019. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/interprocess for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#define BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI 1 +#include "segment_manager_test.hpp"