////////////////////////////////////////////////////////////////////////////// // // (C) Copyright Ion Gaztanaga 2005-2012. 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. // ////////////////////////////////////////////////////////////////////////////// #ifndef BOOST_INTERPROCESS_DETAIL_MANAGED_MEMORY_IMPL_HPP #define BOOST_INTERPROCESS_DETAIL_MANAGED_MEMORY_IMPL_HPP #ifndef BOOST_CONFIG_HPP # include #endif # #if defined(BOOST_HAS_PRAGMA_ONCE) # pragma once #endif #include #include #include #include #include #include #include #include #include #include #include // #include // #include #include //!\file //!Describes a named shared memory allocation user class. //! namespace boost { namespace interprocess { namespace ipcdetail { template class create_open_func; template< class CharType, class MemoryAlgorithm, template class IndexType > struct segment_manager_type { typedef segment_manager type; }; //!This class is designed to be a base class to classes that manage //!creation of objects in a fixed size memory buffer. Apart //!from allocating raw memory, the user can construct named objects. To //!achieve this, this class uses the reserved space provided by the allocation //!algorithm to place a named_allocator_algo, who takes care of name mappings. //!The class can be customized with the char type used for object names //!and the memory allocation algorithm to be used.*/ template < class CharType , class MemoryAlgorithm , template class IndexType , std::size_t Offset = 0 > class basic_managed_memory_impl { //Non-copyable basic_managed_memory_impl(const basic_managed_memory_impl &); basic_managed_memory_impl &operator=(const basic_managed_memory_impl &); template friend class create_open_func; public: typedef typename segment_manager_type ::type segment_manager; typedef CharType char_type; typedef MemoryAlgorithm memory_algorithm; typedef typename MemoryAlgorithm::mutex_family mutex_family; typedef CharType char_t; typedef typename MemoryAlgorithm::size_type size_type; typedef typename MemoryAlgorithm::difference_type difference_type; typedef difference_type handle_t; typedef typename segment_manager:: const_named_iterator const_named_iterator; typedef typename segment_manager:: const_unique_iterator const_unique_iterator; #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) typedef typename segment_manager::char_ptr_holder_t char_ptr_holder_t; //Experimental. Don't use. typedef typename segment_manager::multiallocation_chain multiallocation_chain; #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED static const size_type PayloadPerAllocation = segment_manager::PayloadPerAllocation; private: typedef basic_managed_memory_impl self_t; protected: template static bool grow(const char *filename, size_type extra_bytes) { typedef typename ManagedMemory::device_type device_type; //Increase file size try{ offset_t old_size; { device_type f(open_or_create, filename, read_write); if(!f.get_size(old_size)) return false; f.truncate(old_size + extra_bytes); } ManagedMemory managed_memory(open_only, filename); //Grow always works managed_memory.self_t::grow(extra_bytes); } catch(...){ return false; } return true; } template static bool shrink_to_fit(const char *filename) { typedef typename ManagedMemory::device_type device_type; size_type new_size; try{ ManagedMemory managed_memory(open_only, filename); managed_memory.get_size(); managed_memory.self_t::shrink_to_fit(); new_size = managed_memory.get_size(); } catch(...){ return false; } //Decrease file size { device_type f(open_or_create, filename, read_write); f.truncate(new_size); } return true; } //!Constructor. Allocates basic resources. Never throws. basic_managed_memory_impl() : mp_header(0){} //!Destructor. Calls close. Never throws. ~basic_managed_memory_impl() { this->close_impl(); } //!Places segment manager in the reserved space. This can throw. bool create_impl (void *addr, size_type size) { if(mp_header) return false; //Check if there is enough space if(size < segment_manager::get_min_size()) return false; //This function should not throw. The index construction can //throw if constructor allocates memory. So we must catch it. BOOST_TRY{ //Let's construct the allocator in memory mp_header = ::new(addr, boost_container_new_t()) segment_manager(size); } BOOST_CATCH(...){ return false; } BOOST_CATCH_END return true; } //!Connects to a segment manager in the reserved buffer. Never throws. bool open_impl (void *addr, size_type) { if(mp_header) return false; mp_header = static_cast(addr); return true; } //!Frees resources. Never throws. bool close_impl() { bool ret = mp_header != 0; mp_header = 0; return ret; } //!Frees resources and destroys common resources. Never throws. bool destroy_impl() { if(mp_header == 0) return false; mp_header->~segment_manager(); this->close_impl(); return true; } //! void grow(size_type extra_bytes) { mp_header->grow(extra_bytes); } void shrink_to_fit() { mp_header->shrink_to_fit(); } public: //!Returns segment manager. Never throws. segment_manager *get_segment_manager() const { return mp_header; } //!Returns the base address of the memory in this process. Never throws. void * get_address () const { return reinterpret_cast(mp_header) - Offset; } //!Returns the size of memory segment. Never throws. size_type get_size () const { return mp_header->get_size() + Offset; } //!Returns the number of free bytes of the memory //!segment size_type get_free_memory() const { return mp_header->get_free_memory(); } //!Returns the result of "all_memory_deallocated()" function //!of the used memory algorithm bool all_memory_deallocated() { return mp_header->all_memory_deallocated(); } //!Returns the result of "check_sanity()" function //!of the used memory algorithm bool check_sanity() { return mp_header->check_sanity(); } //!Writes to zero free memory (memory not yet allocated) of //!the memory algorithm void zero_free_memory() { mp_header->zero_free_memory(); } //!Transforms an absolute address into an offset from base address. //!The address must belong to the memory segment. Never throws. handle_t get_handle_from_address (const void *ptr) const { return (handle_t)(reinterpret_cast(ptr) - reinterpret_cast(this->get_address())); } //!Returns true if the address belongs to the managed memory segment bool belongs_to_segment (const void *ptr) const { return ptr >= this->get_address() && ptr < (reinterpret_cast(this->get_address()) + this->get_size()); } //!Transforms previously obtained offset into an absolute address in the //!process space of the current process. Never throws.*/ void * get_address_from_handle (handle_t offset) const { return reinterpret_cast(this->get_address()) + offset; } //!Searches for nbytes of free memory in the segment, marks the //!memory as used and return the pointer to the memory. If no //!memory is available throws a boost::interprocess::bad_alloc exception void* allocate (size_type nbytes) { return mp_header->allocate(nbytes); } //!Searches for nbytes of free memory in the segment, marks the //!memory as used and return the pointer to the memory. If no memory //!is available returns 0. Never throws. void* allocate (size_type nbytes, const std::nothrow_t &tag) { return mp_header->allocate(nbytes, tag); } //!Allocates nbytes bytes aligned to "alignment" bytes. "alignment" //!must be power of two. If no memory //!is available returns 0. Never throws. void * allocate_aligned (size_type nbytes, size_type alignment, const std::nothrow_t &tag) { return mp_header->allocate_aligned(nbytes, alignment, tag); } template T * allocation_command (boost::interprocess::allocation_type command, size_type limit_size, size_type &prefer_in_recvd_out_size, T *&reuse) { return mp_header->allocation_command(command, limit_size, prefer_in_recvd_out_size, reuse); } //!Allocates nbytes bytes aligned to "alignment" bytes. "alignment" //!must be power of two. If no //!memory is available throws a boost::interprocess::bad_alloc exception void * allocate_aligned(size_type nbytes, size_type alignment) { return mp_header->allocate_aligned(nbytes, alignment); } #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) //Experimental. Don't use. //!Allocates n_elements of elem_bytes bytes. //!Throws bad_alloc on failure. chain.size() is not increased on failure. void allocate_many(size_type elem_bytes, size_type n_elements, multiallocation_chain &chain) { mp_header->allocate_many(elem_bytes, n_elements, chain); } //!Allocates n_elements, each one of element_lengths[i]*sizeof_element bytes. //!Throws bad_alloc on failure. chain.size() is not increased on failure. void allocate_many(const size_type *element_lengths, size_type n_elements, size_type sizeof_element, multiallocation_chain &chain) { mp_header->allocate_many(element_lengths, n_elements, sizeof_element, chain); } //!Allocates n_elements of elem_bytes bytes. //!Non-throwing version. chain.size() is not increased on failure. void allocate_many(const std::nothrow_t &tag, size_type elem_bytes, size_type n_elements, multiallocation_chain &chain) { mp_header->allocate_many(tag, elem_bytes, n_elements, chain); } //!Allocates n_elements, each one of //!element_lengths[i]*sizeof_element bytes. //!Non-throwing version. chain.size() is not increased on failure. void allocate_many(const std::nothrow_t &tag, const size_type *elem_sizes, size_type n_elements, size_type sizeof_element, multiallocation_chain &chain) { mp_header->allocate_many(tag, elem_sizes, n_elements, sizeof_element, chain); } //!Deallocates all elements contained in chain. //!Never throws. void deallocate_many(multiallocation_chain &chain) { mp_header->deallocate_many(chain); } #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED //!Marks previously allocated memory as free. Never throws. void deallocate (void *addr) { if (mp_header) mp_header->deallocate(addr); } //!Tries to find a previous named allocation address. Returns a memory //!buffer and the object count. If not found returned pointer is 0. //!Never throws. template std::pair find (char_ptr_holder_t name) { return mp_header->template find(name); } //!Creates a named object or array in memory //! //!Allocates and constructs a T object or an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. If an array is being constructed all objects are //!created using the same parameters given to this function. //! //!-> If the name was previously used, returns 0. //! //!-> Throws boost::interprocess::bad_alloc if there is no available memory //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and if an //!array was being constructed, destructors of created objects are called //!before freeing the memory. template typename segment_manager::template construct_proxy::type construct(char_ptr_holder_t name) { return mp_header->template construct(name); } //!Finds or creates a named object or array in memory //! //!Tries to find an object with the given name in memory. If //!found, returns the pointer to this pointer. If the object is not found, //!allocates and constructs a T object or an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. If an array is being constructed all objects are //!created using the same parameters given to this function. //! //!-> Throws boost::interprocess::bad_alloc if there is no available memory //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and if an //!array was being constructed, destructors of created objects are called //!before freeing the memory. template typename segment_manager::template construct_proxy::type find_or_construct(char_ptr_holder_t name) { return mp_header->template find_or_construct(name); } //!Creates a named object or array in memory //! //!Allocates and constructs a T object or an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. If an array is being constructed all objects are //!created using the same parameters given to this function. //! //!-> If the name was previously used, returns 0. //! //!-> Returns 0 if there is no available memory //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and if an //!array was being constructed, destructors of created objects are called //!before freeing the memory. template typename segment_manager::template construct_proxy::type construct(char_ptr_holder_t name, const std::nothrow_t &tag) { return mp_header->template construct(name, tag); } //!Finds or creates a named object or array in memory //! //!Tries to find an object with the given name in memory. If //!found, returns the pointer to this pointer. If the object is not found, //!allocates and constructs a T object or an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. If an array is being constructed all objects are //!created using the same parameters given to this function. //! //!-> Returns 0 if there is no available memory //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and if an //!array was being constructed, destructors of created objects are called //!before freeing the memory. template typename segment_manager::template construct_proxy::type find_or_construct(char_ptr_holder_t name, const std::nothrow_t &tag) { return mp_header->template find_or_construct(name, tag); } //!Creates a named array from iterators in memory //! //!Allocates and constructs an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. Each element in the array is created using the //!objects returned when dereferencing iterators as parameters //!and incrementing all iterators for each element. //! //!-> If the name was previously used, returns 0. //! //!-> Throws boost::interprocess::bad_alloc if there is no available memory //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and //!destructors of created objects are called before freeing the memory. template typename segment_manager::template construct_iter_proxy::type construct_it(char_ptr_holder_t name) { return mp_header->template construct_it(name); } //!Finds or creates a named array from iterators in memory //! //!Tries to find an object with the given name in memory. If //!found, returns the pointer to this pointer. If the object is not found, //!allocates and constructs an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. Each element in the array is created using the //!objects returned when dereferencing iterators as parameters //!and incrementing all iterators for each element. //! //!-> If the name was previously used, returns 0. //! //!-> Throws boost::interprocess::bad_alloc if there is no available memory //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and //!destructors of created objects are called before freeing the memory. template typename segment_manager::template construct_iter_proxy::type find_or_construct_it(char_ptr_holder_t name) { return mp_header->template find_or_construct_it(name); } //!Creates a named array from iterators in memory //! //!Allocates and constructs an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. Each element in the array is created using the //!objects returned when dereferencing iterators as parameters //!and incrementing all iterators for each element. //! //!-> If the name was previously used, returns 0. //! //!-> If there is no available memory, returns 0. //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and //!destructors of created objects are called before freeing the memory.*/ template typename segment_manager::template construct_iter_proxy::type construct_it(char_ptr_holder_t name, const std::nothrow_t &tag) { return mp_header->template construct_it(name, tag); } //!Finds or creates a named array from iterators in memory //! //!Tries to find an object with the given name in memory. If //!found, returns the pointer to this pointer. If the object is not found, //!allocates and constructs an array of T in memory, //!associates this with the given name and returns a pointer to the //!created object. Each element in the array is created using the //!objects returned when dereferencing iterators as parameters //!and incrementing all iterators for each element. //! //!-> If the name was previously used, returns 0. //! //!-> If there is no available memory, returns 0. //! //!-> If T's constructor throws, the function throws that exception. //! //!Memory is freed automatically if T's constructor throws and //!destructors of created objects are called before freeing the memory.*/ template typename segment_manager::template construct_iter_proxy::type find_or_construct_it(char_ptr_holder_t name, const std::nothrow_t &tag) { return mp_header->template find_or_construct_it(name, tag); } //!Calls a functor and guarantees that no new construction, search or //!destruction will be executed by any process while executing the object //!function call. If the functor throws, this function throws. template void atomic_func(Func &f) { mp_header->atomic_func(f); } //!Tries to call a functor guaranteeing that no new construction, search or //!destruction will be executed by any process while executing the object //!function call. If the atomic function can't be immediatelly executed //!because the internal mutex is already locked, returns false. //!If the functor throws, this function throws. template bool try_atomic_func(Func &f) { return mp_header->try_atomic_func(f); } //!Destroys a named memory object or array. //! //!Finds the object with the given name, calls its destructors, //!frees used memory and returns true. //! //!-> If the object is not found, it returns false. //! //!Exception Handling: //! //!When deleting a dynamically object or array, the Standard //!does not guarantee that dynamically allocated memory, will be released. //!Also, when deleting arrays, the Standard doesn't require calling //!destructors for the rest of the objects if for one of them the destructor //!terminated with an exception. //! //!Destroying an object: //! //!If the destructor throws, the memory will be freed and that exception //!will be thrown. //! //!Destroying an array: //! //!When destroying an array, if a destructor throws, the rest of //!destructors are called. If any of these throws, the exceptions are //!ignored. The name association will be erased, memory will be freed and //!the first exception will be thrown. This guarantees the unlocking of //!mutexes and other resources. //! //!For all theses reasons, classes with throwing destructors are not //!recommended. template bool destroy(const CharType *name) { return mp_header->template destroy(name); } //!Destroys the unique instance of type T //! //!Calls the destructor, frees used memory and returns true. //! //!Exception Handling: //! //!When deleting a dynamically object, the Standard does not //!guarantee that dynamically allocated memory will be released. //! //!Destroying an object: //! //!If the destructor throws, the memory will be freed and that exception //!will be thrown. //! //!For all theses reasons, classes with throwing destructors are not //!recommended for memory. template bool destroy(const unique_instance_t *const ) { return mp_header->template destroy(unique_instance); } //!Destroys the object (named, unique, or anonymous) //! //!Calls the destructor, frees used memory and returns true. //! //!Exception Handling: //! //!When deleting a dynamically object, the Standard does not //!guarantee that dynamically allocated memory will be released. //! //!Destroying an object: //! //!If the destructor throws, the memory will be freed and that exception //!will be thrown. //! //!For all theses reasons, classes with throwing destructors are not //!recommended for memory. template void destroy_ptr(const T *ptr) { mp_header->template destroy_ptr(ptr); } //!Returns the name of an object created with construct/find_or_construct //!functions. If ptr points to an unique instance typeid(T).name() is returned. template static const char_type *get_instance_name(const T *ptr) { return segment_manager::get_instance_name(ptr); } //!Returns is the type an object created with construct/find_or_construct //!functions. Does not throw. template static instance_type get_instance_type(const T *ptr) { return segment_manager::get_instance_type(ptr); } //!Returns the length of an object created with construct/find_or_construct //!functions (1 if is a single element, >=1 if it's an array). Does not throw. template static size_type get_instance_length(const T *ptr) { return segment_manager::get_instance_length(ptr); } //!Preallocates needed index resources to optimize the //!creation of "num" named objects in the memory segment. //!Can throw boost::interprocess::bad_alloc if there is no enough memory. void reserve_named_objects(size_type num) { mp_header->reserve_named_objects(num); } //!Preallocates needed index resources to optimize the //!creation of "num" unique objects in the memory segment. //!Can throw boost::interprocess::bad_alloc if there is no enough memory. void reserve_unique_objects(size_type num) { mp_header->reserve_unique_objects(num); } //!Calls shrink_to_fit in both named and unique object indexes //to try to free unused memory from those indexes. void shrink_to_fit_indexes() { mp_header->shrink_to_fit_indexes(); } //!Returns the number of named objects stored //!in the managed segment. size_type get_num_named_objects() { return mp_header->get_num_named_objects(); } //!Returns the number of unique objects stored //!in the managed segment. size_type get_num_unique_objects() { return mp_header->get_num_unique_objects(); } //!Returns a constant iterator to the index storing the //!named allocations. NOT thread-safe. Never throws. const_named_iterator named_begin() const { return mp_header->named_begin(); } //!Returns a constant iterator to the end of the index //!storing the named allocations. NOT thread-safe. Never throws. const_named_iterator named_end() const { return mp_header->named_end(); } //!Returns a constant iterator to the index storing the //!unique allocations. NOT thread-safe. Never throws. const_unique_iterator unique_begin() const { return mp_header->unique_begin(); } //!Returns a constant iterator to the end of the index //!storing the unique allocations. NOT thread-safe. Never throws. const_unique_iterator unique_end() const { return mp_header->unique_end(); } //!This is the default allocator to allocate types T //!from this managed segment template struct allocator { typedef typename segment_manager::template allocator::type type; }; //!Returns an instance of the default allocator for type T //!initialized that allocates memory from this segment manager. template typename allocator::type get_allocator() { return mp_header->template get_allocator(); } //!This is the default deleter to delete types T //!from this managed segment. template struct deleter { typedef typename segment_manager::template deleter::type type; }; //!Returns an instance of the default allocator for type T //!initialized that allocates memory from this segment manager. template typename deleter::type get_deleter() { return mp_header->template get_deleter(); } #if !defined(BOOST_INTERPROCESS_DOXYGEN_INVOKED) //!Tries to find a previous named allocation address. Returns a memory //!buffer and the object count. If not found returned pointer is 0. //!Never throws. template std::pair find_no_lock (char_ptr_holder_t name) { return mp_header->template find_no_lock(name); } #endif //#ifndef BOOST_INTERPROCESS_DOXYGEN_INVOKED protected: //!Swaps the segment manager's managed by this managed memory segment. //!NOT thread-safe. Never throws. void swap(basic_managed_memory_impl &other) { (simple_swap)(mp_header, other.mp_header); } private: segment_manager *mp_header; }; template class create_open_func { typedef typename BasicManagedMemoryImpl::size_type size_type; public: create_open_func(BasicManagedMemoryImpl * const frontend, create_enum_t type) : m_frontend(frontend), m_type(type){} bool operator()(void *addr, std::size_t size, bool created) const { if( ((m_type == DoOpen) && created) || ((m_type == DoCreate) && !created) || //Check for overflow size_type(-1) < size ){ return false; } else if(created){ return m_frontend->create_impl(addr, static_cast(size)); } else{ return m_frontend->open_impl (addr, static_cast(size)); } } static std::size_t get_min_size() { const size_type sz = BasicManagedMemoryImpl::segment_manager::get_min_size(); if(sz > std::size_t(-1)){ //The minimum size is not representable by std::size_t BOOST_ASSERT(false); return std::size_t(-1); } else{ return static_cast(sz); } } private: BasicManagedMemoryImpl *m_frontend; create_enum_t m_type; }; } //namespace ipcdetail { } //namespace interprocess { } //namespace boost { #include #endif //BOOST_INTERPROCESS_DETAIL_MANAGED_MEMORY_IMPL_HPP