Chris@16: // Copyright (C) 2000, 2001 Stephen Cleary Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See Chris@16: // accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: // See http://www.boost.org for updates, documentation, and revision history. Chris@16: Chris@16: #ifndef BOOST_POOL_HPP Chris@16: #define BOOST_POOL_HPP Chris@16: Chris@16: #include // for workarounds Chris@16: Chris@16: // std::less, std::less_equal, std::greater Chris@16: #include Chris@16: // new[], delete[], std::nothrow Chris@16: #include Chris@16: // std::size_t, std::ptrdiff_t Chris@16: #include Chris@16: // std::malloc, std::free Chris@16: #include Chris@16: // std::invalid_argument Chris@16: #include Chris@16: // std::max Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@101: // boost::integer::static_lcm Chris@101: #include Chris@16: // boost::simple_segregated_storage Chris@16: #include Chris@16: // boost::alignment_of Chris@16: #include Chris@16: // BOOST_ASSERT Chris@16: #include Chris@16: Chris@16: #ifdef BOOST_POOL_INSTRUMENT Chris@16: #include Chris@16: #include Chris@16: #endif Chris@16: #ifdef BOOST_POOL_VALGRIND Chris@16: #include Chris@16: #include Chris@16: #endif Chris@16: Chris@16: #ifdef BOOST_NO_STDC_NAMESPACE Chris@16: namespace std { using ::malloc; using ::free; } Chris@16: #endif Chris@16: Chris@16: // There are a few places in this file where the expression "this->m" is used. Chris@16: // This expression is used to force instantiation-time name lookup, which I am Chris@16: // informed is required for strict Standard compliance. It's only necessary Chris@16: // if "m" is a member of a base class that is dependent on a template Chris@16: // parameter. Chris@16: // Thanks to Jens Maurer for pointing this out! Chris@16: Chris@16: /*! Chris@16: \file Chris@16: \brief Provides class \ref pool: a fast memory allocator that guarantees proper alignment of all allocated chunks, Chris@16: and which extends and generalizes the framework provided by the simple segregated storage solution. Chris@16: Also provides two UserAllocator classes which can be used in conjuction with \ref pool. Chris@16: */ Chris@16: Chris@16: /*! Chris@16: \mainpage Boost.Pool Memory Allocation Scheme Chris@16: Chris@16: \section intro_sec Introduction Chris@16: Chris@16: Pool allocation is a memory allocation scheme that is very fast, but limited in its usage. Chris@16: Chris@16: This Doxygen-style documentation is complementary to the Chris@16: full Quickbook-generated html and pdf documentation at www.boost.org. Chris@16: Chris@16: This page generated from file pool.hpp. Chris@16: Chris@16: */ Chris@16: Chris@16: #ifdef BOOST_MSVC Chris@16: #pragma warning(push) Chris@16: #pragma warning(disable:4127) // Conditional expression is constant Chris@16: #endif Chris@16: Chris@16: namespace boost Chris@16: { Chris@16: Chris@16: //! \brief Allocator used as the default template parameter for Chris@16: //! a UserAllocator Chris@16: //! template parameter. Uses new and delete. Chris@16: struct default_user_allocator_new_delete Chris@16: { Chris@16: typedef std::size_t size_type; //!< An unsigned integral type that can represent the size of the largest object to be allocated. Chris@16: typedef std::ptrdiff_t difference_type; //!< A signed integral type that can represent the difference of any two pointers. Chris@16: Chris@16: static char * malloc BOOST_PREVENT_MACRO_SUBSTITUTION(const size_type bytes) Chris@16: { //! Attempts to allocate n bytes from the system. Returns 0 if out-of-memory Chris@16: return new (std::nothrow) char[bytes]; Chris@16: } Chris@16: static void free BOOST_PREVENT_MACRO_SUBSTITUTION(char * const block) Chris@16: { //! Attempts to de-allocate block. Chris@16: //! \pre Block must have been previously returned from a call to UserAllocator::malloc. Chris@16: delete [] block; Chris@16: } Chris@16: }; Chris@16: Chris@16: //! \brief UserAllocator Chris@16: //! used as template parameter for \ref pool and \ref object_pool. Chris@16: //! Uses malloc and free internally. Chris@16: struct default_user_allocator_malloc_free Chris@16: { Chris@16: typedef std::size_t size_type; //!< An unsigned integral type that can represent the size of the largest object to be allocated. Chris@16: typedef std::ptrdiff_t difference_type; //!< A signed integral type that can represent the difference of any two pointers. Chris@16: Chris@16: static char * malloc BOOST_PREVENT_MACRO_SUBSTITUTION(const size_type bytes) Chris@16: { return static_cast((std::malloc)(bytes)); } Chris@16: static void free BOOST_PREVENT_MACRO_SUBSTITUTION(char * const block) Chris@16: { (std::free)(block); } Chris@16: }; Chris@16: Chris@16: namespace details Chris@16: { //! Implemention only. Chris@16: Chris@16: template Chris@16: class PODptr Chris@16: { //! PODptr is a class that pretends to be a "pointer" to different class types Chris@16: //! that don't really exist. It provides member functions to access the "data" Chris@16: //! of the "object" it points to. Since these "class" types are of variable Chris@16: //! size, and contains some information at the *end* of its memory Chris@16: //! (for alignment reasons), Chris@16: //! PODptr must contain the size of this "class" as well as the pointer to this "object". Chris@16: Chris@16: /*! \details A PODptr holds the location and size of a memory block allocated from the system. Chris@16: Each memory block is split logically into three sections: Chris@16: Chris@16: Chunk area. This section may be different sizes. PODptr does not care what the size of the chunks is, Chris@16: but it does care (and keep track of) the total size of the chunk area. Chris@16: Chris@16: Next pointer. This section is always the same size for a given SizeType. It holds a pointer Chris@16: to the location of the next memory block in the memory block list, or 0 if there is no such block. Chris@16: Chris@16: Next size. This section is always the same size for a given SizeType. It holds the size of the Chris@16: next memory block in the memory block list. Chris@16: Chris@16: The PODptr class just provides cleaner ways of dealing with raw memory blocks. Chris@16: Chris@16: A PODptr object is either valid or invalid. An invalid PODptr is analogous to a null pointer. Chris@16: The default constructor for PODptr will result in an invalid object. Chris@16: Calling the member function invalidate will result in that object becoming invalid. Chris@16: The member function valid can be used to test for validity. Chris@16: */ Chris@16: public: Chris@16: typedef SizeType size_type; Chris@16: Chris@16: private: Chris@16: char * ptr; Chris@16: size_type sz; Chris@16: Chris@16: char * ptr_next_size() const Chris@16: { Chris@16: return (ptr + sz - sizeof(size_type)); Chris@16: } Chris@16: char * ptr_next_ptr() const Chris@16: { Chris@16: return (ptr_next_size() - Chris@101: integer::static_lcm::value); Chris@16: } Chris@16: Chris@16: public: Chris@16: PODptr(char * const nptr, const size_type nsize) Chris@16: :ptr(nptr), sz(nsize) Chris@16: { Chris@16: //! A PODptr may be created to point to a memory block by passing Chris@16: //! the address and size of that memory block into the constructor. Chris@16: //! A PODptr constructed in this way is valid. Chris@16: } Chris@16: PODptr() Chris@16: : ptr(0), sz(0) Chris@16: { //! default constructor for PODptr will result in an invalid object. Chris@16: } Chris@16: Chris@16: bool valid() const Chris@16: { //! A PODptr object is either valid or invalid. Chris@16: //! An invalid PODptr is analogous to a null pointer. Chris@16: //! \returns true if PODptr is valid, false if invalid. Chris@16: return (begin() != 0); Chris@16: } Chris@16: void invalidate() Chris@16: { //! Make object invalid. Chris@16: begin() = 0; Chris@16: } Chris@16: char * & begin() Chris@16: { //! Each PODptr keeps the address and size of its memory block. Chris@16: //! \returns The address of its memory block. Chris@16: return ptr; Chris@16: } Chris@16: char * begin() const Chris@16: { //! Each PODptr keeps the address and size of its memory block. Chris@16: //! \return The address of its memory block. Chris@16: return ptr; Chris@16: } Chris@16: char * end() const Chris@16: { //! \returns begin() plus element_size (a 'past the end' value). Chris@16: return ptr_next_ptr(); Chris@16: } Chris@16: size_type total_size() const Chris@16: { //! Each PODptr keeps the address and size of its memory block. Chris@16: //! The address may be read or written by the member functions begin. Chris@16: //! The size of the memory block may only be read, Chris@16: //! \returns size of the memory block. Chris@16: return sz; Chris@16: } Chris@16: size_type element_size() const Chris@16: { //! \returns size of element pointer area. Chris@16: return static_cast(sz - sizeof(size_type) - Chris@101: integer::static_lcm::value); Chris@16: } Chris@16: Chris@16: size_type & next_size() const Chris@16: { //! Chris@16: //! \returns next_size. Chris@16: return *(static_cast(static_cast((ptr_next_size())))); Chris@16: } Chris@16: char * & next_ptr() const Chris@16: { //! \returns pointer to next pointer area. Chris@16: return *(static_cast(static_cast(ptr_next_ptr()))); Chris@16: } Chris@16: Chris@16: PODptr next() const Chris@16: { //! \returns next PODptr. Chris@16: return PODptr(next_ptr(), next_size()); Chris@16: } Chris@16: void next(const PODptr & arg) const Chris@16: { //! Sets next PODptr. Chris@16: next_ptr() = arg.begin(); Chris@16: next_size() = arg.total_size(); Chris@16: } Chris@16: }; // class PODptr Chris@16: } // namespace details Chris@16: Chris@16: #ifndef BOOST_POOL_VALGRIND Chris@16: /*! Chris@16: \brief A fast memory allocator that guarantees proper alignment of all allocated chunks. Chris@16: Chris@16: \details Whenever an object of type pool needs memory from the system, Chris@16: it will request it from its UserAllocator template parameter. Chris@16: The amount requested is determined using a doubling algorithm; Chris@16: that is, each time more system memory is allocated, Chris@16: the amount of system memory requested is doubled. Chris@16: Chris@16: Users may control the doubling algorithm by using the following extensions: Chris@16: Chris@16: Users may pass an additional constructor parameter to pool. Chris@16: This parameter is of type size_type, Chris@16: and is the number of chunks to request from the system Chris@16: the first time that object needs to allocate system memory. Chris@16: The default is 32. This parameter may not be 0. Chris@16: Chris@16: Users may also pass an optional third parameter to pool's Chris@16: constructor. This parameter is of type size_type, Chris@16: and sets a maximum size for allocated chunks. When this Chris@16: parameter takes the default value of 0, then there is no upper Chris@16: limit on chunk size. Chris@16: Chris@16: Finally, if the doubling algorithm results in no memory Chris@16: being allocated, the pool will backtrack just once, halving Chris@16: the chunk size and trying again. Chris@16: Chris@16: UserAllocator type - the method that the Pool will use to allocate memory from the system. Chris@16: Chris@16: There are essentially two ways to use class pool: the client can call \ref malloc() and \ref free() to allocate Chris@16: and free single chunks of memory, this is the most efficient way to use a pool, but does not allow for Chris@16: the efficient allocation of arrays of chunks. Alternatively, the client may call \ref ordered_malloc() and \ref Chris@16: ordered_free(), in which case the free list is maintained in an ordered state, and efficient allocation of arrays Chris@16: of chunks are possible. However, this latter option can suffer from poor performance when large numbers of Chris@16: allocations are performed. Chris@16: Chris@16: */ Chris@16: template Chris@16: class pool: protected simple_segregated_storage < typename UserAllocator::size_type > Chris@16: { Chris@16: public: Chris@16: typedef UserAllocator user_allocator; //!< User allocator. Chris@16: typedef typename UserAllocator::size_type size_type; //!< An unsigned integral type that can represent the size of the largest object to be allocated. Chris@16: typedef typename UserAllocator::difference_type difference_type; //!< A signed integral type that can represent the difference of any two pointers. Chris@16: Chris@16: private: Chris@16: BOOST_STATIC_CONSTANT(size_type, min_alloc_size = Chris@101: (::boost::integer::static_lcm::value) ); Chris@16: BOOST_STATIC_CONSTANT(size_type, min_align = Chris@101: (::boost::integer::static_lcm< ::boost::alignment_of::value, ::boost::alignment_of::value>::value) ); Chris@16: Chris@16: //! \returns 0 if out-of-memory. Chris@16: //! Called if malloc/ordered_malloc needs to resize the free list. Chris@16: void * malloc_need_resize(); //! Called if malloc needs to resize the free list. Chris@16: void * ordered_malloc_need_resize(); //! Called if ordered_malloc needs to resize the free list. Chris@16: Chris@16: protected: Chris@16: details::PODptr list; //!< List structure holding ordered blocks. Chris@16: Chris@16: simple_segregated_storage & store() Chris@16: { //! \returns pointer to store. Chris@16: return *this; Chris@16: } Chris@16: const simple_segregated_storage & store() const Chris@16: { //! \returns pointer to store. Chris@16: return *this; Chris@16: } Chris@16: const size_type requested_size; Chris@16: size_type next_size; Chris@16: size_type start_size; Chris@16: size_type max_size; Chris@16: Chris@16: //! finds which POD in the list 'chunk' was allocated from. Chris@16: details::PODptr find_POD(void * const chunk) const; Chris@16: Chris@16: // is_from() tests a chunk to determine if it belongs in a block. Chris@16: static bool is_from(void * const chunk, char * const i, Chris@16: const size_type sizeof_i) Chris@16: { //! \param chunk chunk to check if is from this pool. Chris@16: //! \param i memory chunk at i with element sizeof_i. Chris@16: //! \param sizeof_i element size (size of the chunk area of that block, not the total size of that block). Chris@16: //! \returns true if chunk was allocated or may be returned. Chris@16: //! as the result of a future allocation. Chris@16: //! Chris@16: //! Returns false if chunk was allocated from some other pool, Chris@16: //! or may be returned as the result of a future allocation from some other pool. Chris@16: //! Otherwise, the return value is meaningless. Chris@16: //! Chris@16: //! Note that this function may not be used to reliably test random pointer values. Chris@16: Chris@16: // We use std::less_equal and std::less to test 'chunk' Chris@16: // against the array bounds because standard operators Chris@16: // may return unspecified results. Chris@16: // This is to ensure portability. The operators < <= > >= are only Chris@16: // defined for pointers to objects that are 1) in the same array, or Chris@16: // 2) subobjects of the same object [5.9/2]. Chris@16: // The functor objects guarantee a total order for any pointer [20.3.3/8] Chris@16: std::less_equal lt_eq; Chris@16: std::less lt; Chris@16: return (lt_eq(i, chunk) && lt(chunk, i + sizeof_i)); Chris@16: } Chris@16: Chris@16: size_type alloc_size() const Chris@16: { //! Calculated size of the memory chunks that will be allocated by this Pool. Chris@16: //! \returns allocated size. Chris@16: // For alignment reasons, this used to be defined to be lcm(requested_size, sizeof(void *), sizeof(size_type)), Chris@16: // but is now more parsimonious: just rounding up to the minimum required alignment of our housekeeping data Chris@16: // when required. This works provided all alignments are powers of two. Chris@16: size_type s = (std::max)(requested_size, min_alloc_size); Chris@16: size_type rem = s % min_align; Chris@16: if(rem) Chris@16: s += min_align - rem; Chris@16: BOOST_ASSERT(s >= min_alloc_size); Chris@16: BOOST_ASSERT(s % min_align == 0); Chris@16: return s; Chris@16: } Chris@16: Chris@16: static void * & nextof(void * const ptr) Chris@16: { //! \returns Pointer dereferenced. Chris@16: //! (Provided and used for the sake of code readability :) Chris@16: return *(static_cast(ptr)); Chris@16: } Chris@16: Chris@16: public: Chris@16: // pre: npartition_size != 0 && nnext_size != 0 Chris@16: explicit pool(const size_type nrequested_size, Chris@16: const size_type nnext_size = 32, Chris@16: const size_type nmax_size = 0) Chris@16: : Chris@16: list(0, 0), requested_size(nrequested_size), next_size(nnext_size), start_size(nnext_size),max_size(nmax_size) Chris@16: { //! Constructs a new empty Pool that can be used to allocate chunks of size RequestedSize. Chris@16: //! \param nrequested_size Requested chunk size Chris@16: //! \param nnext_size parameter is of type size_type, Chris@16: //! is the number of chunks to request from the system Chris@16: //! the first time that object needs to allocate system memory. Chris@16: //! The default is 32. This parameter may not be 0. Chris@16: //! \param nmax_size is the maximum number of chunks to allocate in one block. Chris@16: } Chris@16: Chris@16: ~pool() Chris@16: { //! Destructs the Pool, freeing its list of memory blocks. Chris@16: purge_memory(); Chris@16: } Chris@16: Chris@16: // Releases memory blocks that don't have chunks allocated Chris@16: // pre: lists are ordered Chris@16: // Returns true if memory was actually deallocated Chris@16: bool release_memory(); Chris@16: Chris@16: // Releases *all* memory blocks, even if chunks are still allocated Chris@16: // Returns true if memory was actually deallocated Chris@16: bool purge_memory(); Chris@16: Chris@16: size_type get_next_size() const Chris@16: { //! Number of chunks to request from the system the next time that object needs to allocate system memory. This value should never be 0. Chris@16: //! \returns next_size; Chris@16: return next_size; Chris@16: } Chris@16: void set_next_size(const size_type nnext_size) Chris@16: { //! Set number of chunks to request from the system the next time that object needs to allocate system memory. This value should never be set to 0. Chris@16: //! \returns nnext_size. Chris@16: next_size = start_size = nnext_size; Chris@16: } Chris@16: size_type get_max_size() const Chris@16: { //! \returns max_size. Chris@16: return max_size; Chris@16: } Chris@16: void set_max_size(const size_type nmax_size) Chris@16: { //! Set max_size. Chris@16: max_size = nmax_size; Chris@16: } Chris@16: size_type get_requested_size() const Chris@16: { //! \returns the requested size passed into the constructor. Chris@16: //! (This value will not change during the lifetime of a Pool object). Chris@16: return requested_size; Chris@16: } Chris@16: Chris@16: // Both malloc and ordered_malloc do a quick inlined check first for any Chris@16: // free chunks. Only if we need to get another memory block do we call Chris@16: // the non-inlined *_need_resize() functions. Chris@16: // Returns 0 if out-of-memory Chris@16: void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() Chris@16: { //! Allocates a chunk of memory. Searches in the list of memory blocks Chris@16: //! for a block that has a free chunk, and returns that free chunk if found. Chris@16: //! Otherwise, creates a new memory block, adds its free list to pool's free list, Chris@16: //! \returns a free chunk from that block. Chris@16: //! If a new memory block cannot be allocated, returns 0. Amortized O(1). Chris@16: // Look for a non-empty storage Chris@16: if (!store().empty()) Chris@16: return (store().malloc)(); Chris@16: return malloc_need_resize(); Chris@16: } Chris@16: Chris@16: void * ordered_malloc() Chris@16: { //! Same as malloc, only merges the free lists, to preserve order. Amortized O(1). Chris@16: //! \returns a free chunk from that block. Chris@16: //! If a new memory block cannot be allocated, returns 0. Amortized O(1). Chris@16: // Look for a non-empty storage Chris@16: if (!store().empty()) Chris@16: return (store().malloc)(); Chris@16: return ordered_malloc_need_resize(); Chris@16: } Chris@16: Chris@16: // Returns 0 if out-of-memory Chris@16: // Allocate a contiguous section of n chunks Chris@16: void * ordered_malloc(size_type n); Chris@16: //! Same as malloc, only allocates enough contiguous chunks to cover n * requested_size bytes. Amortized O(n). Chris@16: //! \returns a free chunk from that block. Chris@16: //! If a new memory block cannot be allocated, returns 0. Amortized O(1). Chris@16: Chris@16: // pre: 'chunk' must have been previously Chris@16: // returned by *this.malloc(). Chris@16: void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk) Chris@16: { //! Deallocates a chunk of memory. Note that chunk may not be 0. O(1). Chris@16: //! Chris@16: //! Chunk must have been previously returned by t.malloc() or t.ordered_malloc(). Chris@16: //! Assumes that chunk actually refers to a block of chunks Chris@16: //! spanning n * partition_sz bytes. Chris@16: //! deallocates each chunk in that block. Chris@16: //! Note that chunk may not be 0. O(n). Chris@16: (store().free)(chunk); Chris@16: } Chris@16: Chris@16: // pre: 'chunk' must have been previously Chris@16: // returned by *this.malloc(). Chris@16: void ordered_free(void * const chunk) Chris@16: { //! Same as above, but is order-preserving. Chris@16: //! Chris@16: //! Note that chunk may not be 0. O(N) with respect to the size of the free list. Chris@16: //! chunk must have been previously returned by t.malloc() or t.ordered_malloc(). Chris@16: store().ordered_free(chunk); Chris@16: } Chris@16: Chris@16: // pre: 'chunk' must have been previously Chris@16: // returned by *this.malloc(n). Chris@16: void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunks, const size_type n) Chris@16: { //! Assumes that chunk actually refers to a block of chunks. Chris@16: //! Chris@16: //! chunk must have been previously returned by t.ordered_malloc(n) Chris@16: //! spanning n * partition_sz bytes. Chris@16: //! Deallocates each chunk in that block. Chris@16: //! Note that chunk may not be 0. O(n). Chris@16: const size_type partition_size = alloc_size(); Chris@16: const size_type total_req_size = n * requested_size; Chris@16: const size_type num_chunks = total_req_size / partition_size + Chris@16: ((total_req_size % partition_size) ? true : false); Chris@16: Chris@16: store().free_n(chunks, num_chunks, partition_size); Chris@16: } Chris@16: Chris@16: // pre: 'chunk' must have been previously Chris@16: // returned by *this.malloc(n). Chris@16: void ordered_free(void * const chunks, const size_type n) Chris@16: { //! Assumes that chunk actually refers to a block of chunks spanning n * partition_sz bytes; Chris@16: //! deallocates each chunk in that block. Chris@16: //! Chris@16: //! Note that chunk may not be 0. Order-preserving. O(N + n) where N is the size of the free list. Chris@16: //! chunk must have been previously returned by t.malloc() or t.ordered_malloc(). Chris@16: Chris@16: const size_type partition_size = alloc_size(); Chris@16: const size_type total_req_size = n * requested_size; Chris@16: const size_type num_chunks = total_req_size / partition_size + Chris@16: ((total_req_size % partition_size) ? true : false); Chris@16: Chris@16: store().ordered_free_n(chunks, num_chunks, partition_size); Chris@16: } Chris@16: Chris@16: // is_from() tests a chunk to determine if it was allocated from *this Chris@16: bool is_from(void * const chunk) const Chris@16: { //! \returns Returns true if chunk was allocated from u or Chris@16: //! may be returned as the result of a future allocation from u. Chris@16: //! Returns false if chunk was allocated from some other pool or Chris@16: //! may be returned as the result of a future allocation from some other pool. Chris@16: //! Otherwise, the return value is meaningless. Chris@16: //! Note that this function may not be used to reliably test random pointer values. Chris@16: return (find_POD(chunk).valid()); Chris@16: } Chris@16: }; Chris@16: Chris@16: #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION Chris@16: template Chris@16: typename pool::size_type const pool::min_alloc_size; Chris@16: template Chris@16: typename pool::size_type const pool::min_align; Chris@16: #endif Chris@16: Chris@16: template Chris@16: bool pool::release_memory() Chris@16: { //! pool must be ordered. Frees every memory block that doesn't have any allocated chunks. Chris@16: //! \returns true if at least one memory block was freed. Chris@16: Chris@16: // ret is the return value: it will be set to true when we actually call Chris@16: // UserAllocator::free(..) Chris@16: bool ret = false; Chris@16: Chris@16: // This is a current & previous iterator pair over the memory block list Chris@16: details::PODptr ptr = list; Chris@16: details::PODptr prev; Chris@16: Chris@16: // This is a current & previous iterator pair over the free memory chunk list Chris@16: // Note that "prev_free" in this case does NOT point to the previous memory Chris@16: // chunk in the free list, but rather the last free memory chunk before the Chris@16: // current block. Chris@16: void * free_p = this->first; Chris@16: void * prev_free_p = 0; Chris@16: Chris@16: const size_type partition_size = alloc_size(); Chris@16: Chris@16: // Search through all the all the allocated memory blocks Chris@16: while (ptr.valid()) Chris@16: { Chris@16: // At this point: Chris@16: // ptr points to a valid memory block Chris@16: // free_p points to either: Chris@16: // 0 if there are no more free chunks Chris@16: // the first free chunk in this or some next memory block Chris@16: // prev_free_p points to either: Chris@16: // the last free chunk in some previous memory block Chris@16: // 0 if there is no such free chunk Chris@16: // prev is either: Chris@16: // the PODptr whose next() is ptr Chris@16: // !valid() if there is no such PODptr Chris@16: Chris@16: // If there are no more free memory chunks, then every remaining Chris@16: // block is allocated out to its fullest capacity, and we can't Chris@16: // release any more memory Chris@16: if (free_p == 0) Chris@16: break; Chris@16: Chris@16: // We have to check all the chunks. If they are *all* free (i.e., present Chris@16: // in the free list), then we can free the block. Chris@16: bool all_chunks_free = true; Chris@16: Chris@16: // Iterate 'i' through all chunks in the memory block Chris@16: // if free starts in the memory block, be careful to keep it there Chris@16: void * saved_free = free_p; Chris@16: for (char * i = ptr.begin(); i != ptr.end(); i += partition_size) Chris@16: { Chris@16: // If this chunk is not free Chris@16: if (i != free_p) Chris@16: { Chris@16: // We won't be able to free this block Chris@16: all_chunks_free = false; Chris@16: Chris@16: // free_p might have travelled outside ptr Chris@16: free_p = saved_free; Chris@16: // Abort searching the chunks; we won't be able to free this Chris@16: // block because a chunk is not free. Chris@16: break; Chris@16: } Chris@16: Chris@16: // We do not increment prev_free_p because we are in the same block Chris@16: free_p = nextof(free_p); Chris@16: } Chris@16: Chris@16: // post: if the memory block has any chunks, free_p points to one of them Chris@16: // otherwise, our assertions above are still valid Chris@16: Chris@16: const details::PODptr next = ptr.next(); Chris@16: Chris@16: if (!all_chunks_free) Chris@16: { Chris@16: if (is_from(free_p, ptr.begin(), ptr.element_size())) Chris@16: { Chris@16: std::less lt; Chris@16: void * const end = ptr.end(); Chris@16: do Chris@16: { Chris@16: prev_free_p = free_p; Chris@16: free_p = nextof(free_p); Chris@16: } while (free_p && lt(free_p, end)); Chris@16: } Chris@16: // This invariant is now restored: Chris@16: // free_p points to the first free chunk in some next memory block, or Chris@16: // 0 if there is no such chunk. Chris@16: // prev_free_p points to the last free chunk in this memory block. Chris@16: Chris@16: // We are just about to advance ptr. Maintain the invariant: Chris@16: // prev is the PODptr whose next() is ptr, or !valid() Chris@16: // if there is no such PODptr Chris@16: prev = ptr; Chris@16: } Chris@16: else Chris@16: { Chris@16: // All chunks from this block are free Chris@16: Chris@16: // Remove block from list Chris@16: if (prev.valid()) Chris@16: prev.next(next); Chris@16: else Chris@16: list = next; Chris@16: Chris@16: // Remove all entries in the free list from this block Chris@16: if (prev_free_p != 0) Chris@16: nextof(prev_free_p) = free_p; Chris@16: else Chris@16: this->first = free_p; Chris@16: Chris@16: // And release memory Chris@16: (UserAllocator::free)(ptr.begin()); Chris@16: ret = true; Chris@16: } Chris@16: Chris@16: // Increment ptr Chris@16: ptr = next; Chris@16: } Chris@16: Chris@16: next_size = start_size; Chris@16: return ret; Chris@16: } Chris@16: Chris@16: template Chris@16: bool pool::purge_memory() Chris@16: { //! pool must be ordered. Chris@16: //! Frees every memory block. Chris@16: //! Chris@16: //! This function invalidates any pointers previously returned Chris@16: //! by allocation functions of t. Chris@16: //! \returns true if at least one memory block was freed. Chris@16: Chris@16: details::PODptr iter = list; Chris@16: Chris@16: if (!iter.valid()) Chris@16: return false; Chris@16: Chris@16: do Chris@16: { Chris@16: // hold "next" pointer Chris@16: const details::PODptr next = iter.next(); Chris@16: Chris@16: // delete the storage Chris@16: (UserAllocator::free)(iter.begin()); Chris@16: Chris@16: // increment iter Chris@16: iter = next; Chris@16: } while (iter.valid()); Chris@16: Chris@16: list.invalidate(); Chris@16: this->first = 0; Chris@16: next_size = start_size; Chris@16: Chris@16: return true; Chris@16: } Chris@16: Chris@16: template Chris@16: void * pool::malloc_need_resize() Chris@16: { //! No memory in any of our storages; make a new storage, Chris@16: //! Allocates chunk in newly malloc aftert resize. Chris@16: //! \returns pointer to chunk. Chris@16: size_type partition_size = alloc_size(); Chris@16: size_type POD_size = static_cast(next_size * partition_size + Chris@101: integer::static_lcm::value + sizeof(size_type)); Chris@16: char * ptr = (UserAllocator::malloc)(POD_size); Chris@16: if (ptr == 0) Chris@16: { Chris@16: if(next_size > 4) Chris@16: { Chris@16: next_size >>= 1; Chris@16: partition_size = alloc_size(); Chris@16: POD_size = static_cast(next_size * partition_size + Chris@101: integer::static_lcm::value + sizeof(size_type)); Chris@16: ptr = (UserAllocator::malloc)(POD_size); Chris@16: } Chris@16: if(ptr == 0) Chris@16: return 0; Chris@16: } Chris@16: const details::PODptr node(ptr, POD_size); Chris@16: Chris@16: BOOST_USING_STD_MIN(); Chris@16: if(!max_size) Chris@16: next_size <<= 1; Chris@16: else if( next_size*partition_size/requested_size < max_size) Chris@16: next_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size*requested_size/ partition_size); Chris@16: Chris@16: // initialize it, Chris@16: store().add_block(node.begin(), node.element_size(), partition_size); Chris@16: Chris@16: // insert it into the list, Chris@16: node.next(list); Chris@16: list = node; Chris@16: Chris@16: // and return a chunk from it. Chris@16: return (store().malloc)(); Chris@16: } Chris@16: Chris@16: template Chris@16: void * pool::ordered_malloc_need_resize() Chris@16: { //! No memory in any of our storages; make a new storage, Chris@16: //! \returns pointer to new chunk. Chris@16: size_type partition_size = alloc_size(); Chris@16: size_type POD_size = static_cast(next_size * partition_size + Chris@101: integer::static_lcm::value + sizeof(size_type)); Chris@16: char * ptr = (UserAllocator::malloc)(POD_size); Chris@16: if (ptr == 0) Chris@16: { Chris@16: if(next_size > 4) Chris@16: { Chris@16: next_size >>= 1; Chris@16: partition_size = alloc_size(); Chris@16: POD_size = static_cast(next_size * partition_size + Chris@101: integer::static_lcm::value + sizeof(size_type)); Chris@16: ptr = (UserAllocator::malloc)(POD_size); Chris@16: } Chris@16: if(ptr == 0) Chris@16: return 0; Chris@16: } Chris@16: const details::PODptr node(ptr, POD_size); Chris@16: Chris@16: BOOST_USING_STD_MIN(); Chris@16: if(!max_size) Chris@16: next_size <<= 1; Chris@16: else if( next_size*partition_size/requested_size < max_size) Chris@16: next_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size*requested_size/ partition_size); Chris@16: Chris@16: // initialize it, Chris@16: // (we can use "add_block" here because we know that Chris@16: // the free list is empty, so we don't have to use Chris@16: // the slower ordered version) Chris@16: store().add_ordered_block(node.begin(), node.element_size(), partition_size); Chris@16: Chris@16: // insert it into the list, Chris@16: // handle border case Chris@16: if (!list.valid() || std::greater()(list.begin(), node.begin())) Chris@16: { Chris@16: node.next(list); Chris@16: list = node; Chris@16: } Chris@16: else Chris@16: { Chris@16: details::PODptr prev = list; Chris@16: Chris@16: while (true) Chris@16: { Chris@16: // if we're about to hit the end or Chris@16: // if we've found where "node" goes Chris@16: if (prev.next_ptr() == 0 Chris@16: || std::greater()(prev.next_ptr(), node.begin())) Chris@16: break; Chris@16: Chris@16: prev = prev.next(); Chris@16: } Chris@16: Chris@16: node.next(prev.next()); Chris@16: prev.next(node); Chris@16: } Chris@16: // and return a chunk from it. Chris@16: return (store().malloc)(); Chris@16: } Chris@16: Chris@16: template Chris@16: void * pool::ordered_malloc(const size_type n) Chris@16: { //! Gets address of a chunk n, allocating new memory if not already available. Chris@16: //! \returns Address of chunk n if allocated ok. Chris@16: //! \returns 0 if not enough memory for n chunks. Chris@16: Chris@16: const size_type partition_size = alloc_size(); Chris@16: const size_type total_req_size = n * requested_size; Chris@16: const size_type num_chunks = total_req_size / partition_size + Chris@16: ((total_req_size % partition_size) ? true : false); Chris@16: Chris@16: void * ret = store().malloc_n(num_chunks, partition_size); Chris@16: Chris@16: #ifdef BOOST_POOL_INSTRUMENT Chris@16: std::cout << "Allocating " << n << " chunks from pool of size " << partition_size << std::endl; Chris@16: #endif Chris@16: if ((ret != 0) || (n == 0)) Chris@16: return ret; Chris@16: Chris@16: #ifdef BOOST_POOL_INSTRUMENT Chris@16: std::cout << "Cache miss, allocating another chunk...\n"; Chris@16: #endif Chris@16: Chris@16: // Not enough memory in our storages; make a new storage, Chris@16: BOOST_USING_STD_MAX(); Chris@16: next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks); Chris@16: size_type POD_size = static_cast(next_size * partition_size + Chris@101: integer::static_lcm::value + sizeof(size_type)); Chris@16: char * ptr = (UserAllocator::malloc)(POD_size); Chris@16: if (ptr == 0) Chris@16: { Chris@16: if(num_chunks < next_size) Chris@16: { Chris@16: // Try again with just enough memory to do the job, or at least whatever we Chris@16: // allocated last time: Chris@16: next_size >>= 1; Chris@16: next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks); Chris@16: POD_size = static_cast(next_size * partition_size + Chris@101: integer::static_lcm::value + sizeof(size_type)); Chris@16: ptr = (UserAllocator::malloc)(POD_size); Chris@16: } Chris@16: if(ptr == 0) Chris@16: return 0; Chris@16: } Chris@16: const details::PODptr node(ptr, POD_size); Chris@16: Chris@16: // Split up block so we can use what wasn't requested. Chris@16: if (next_size > num_chunks) Chris@16: store().add_ordered_block(node.begin() + num_chunks * partition_size, Chris@16: node.element_size() - num_chunks * partition_size, partition_size); Chris@16: Chris@16: BOOST_USING_STD_MIN(); Chris@16: if(!max_size) Chris@16: next_size <<= 1; Chris@16: else if( next_size*partition_size/requested_size < max_size) Chris@16: next_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size*requested_size/ partition_size); Chris@16: Chris@16: // insert it into the list, Chris@16: // handle border case. Chris@16: if (!list.valid() || std::greater()(list.begin(), node.begin())) Chris@16: { Chris@16: node.next(list); Chris@16: list = node; Chris@16: } Chris@16: else Chris@16: { Chris@16: details::PODptr prev = list; Chris@16: Chris@16: while (true) Chris@16: { Chris@16: // if we're about to hit the end, or if we've found where "node" goes. Chris@16: if (prev.next_ptr() == 0 Chris@16: || std::greater()(prev.next_ptr(), node.begin())) Chris@16: break; Chris@16: Chris@16: prev = prev.next(); Chris@16: } Chris@16: Chris@16: node.next(prev.next()); Chris@16: prev.next(node); Chris@16: } Chris@16: Chris@16: // and return it. Chris@16: return node.begin(); Chris@16: } Chris@16: Chris@16: template Chris@16: details::PODptr::size_type> Chris@16: pool::find_POD(void * const chunk) const Chris@16: { //! find which PODptr storage memory that this chunk is from. Chris@16: //! \returns the PODptr that holds this chunk. Chris@16: // Iterate down list to find which storage this chunk is from. Chris@16: details::PODptr iter = list; Chris@16: while (iter.valid()) Chris@16: { Chris@16: if (is_from(chunk, iter.begin(), iter.element_size())) Chris@16: return iter; Chris@16: iter = iter.next(); Chris@16: } Chris@16: Chris@16: return iter; Chris@16: } Chris@16: Chris@16: #else // BOOST_POOL_VALGRIND Chris@16: Chris@16: template Chris@16: class pool Chris@16: { Chris@16: public: Chris@16: // types Chris@16: typedef UserAllocator user_allocator; // User allocator. Chris@16: typedef typename UserAllocator::size_type size_type; // An unsigned integral type that can represent the size of the largest object to be allocated. Chris@16: typedef typename UserAllocator::difference_type difference_type; // A signed integral type that can represent the difference of any two pointers. Chris@16: Chris@16: // construct/copy/destruct Chris@16: explicit pool(const size_type s, const size_type = 32, const size_type m = 0) : chunk_size(s), max_alloc_size(m) {} Chris@16: ~pool() Chris@16: { Chris@16: purge_memory(); Chris@16: } Chris@16: Chris@16: bool release_memory() Chris@16: { Chris@16: bool ret = free_list.empty() ? false : true; Chris@16: for(std::set::iterator pos = free_list.begin(); pos != free_list.end(); ++pos) Chris@16: { Chris@16: (user_allocator::free)(static_cast(*pos)); Chris@16: } Chris@16: free_list.clear(); Chris@16: return ret; Chris@16: } Chris@16: bool purge_memory() Chris@16: { Chris@16: bool ret = free_list.empty() && used_list.empty() ? false : true; Chris@16: for(std::set::iterator pos = free_list.begin(); pos != free_list.end(); ++pos) Chris@16: { Chris@16: (user_allocator::free)(static_cast(*pos)); Chris@16: } Chris@16: free_list.clear(); Chris@16: for(std::set::iterator pos = used_list.begin(); pos != used_list.end(); ++pos) Chris@16: { Chris@16: (user_allocator::free)(static_cast(*pos)); Chris@16: } Chris@16: used_list.clear(); Chris@16: return ret; Chris@16: } Chris@16: size_type get_next_size() const Chris@16: { Chris@16: return 1; Chris@16: } Chris@16: void set_next_size(const size_type){} Chris@16: size_type get_max_size() const Chris@16: { Chris@16: return max_alloc_size; Chris@16: } Chris@16: void set_max_size(const size_type s) Chris@16: { Chris@16: max_alloc_size = s; Chris@16: } Chris@16: size_type get_requested_size() const Chris@16: { Chris@16: return chunk_size; Chris@16: } Chris@16: void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() Chris@16: { Chris@16: void* ret; Chris@16: if(free_list.empty()) Chris@16: { Chris@16: ret = (user_allocator::malloc)(chunk_size); Chris@16: VALGRIND_MAKE_MEM_UNDEFINED(ret, chunk_size); Chris@16: } Chris@16: else Chris@16: { Chris@16: ret = *free_list.begin(); Chris@16: free_list.erase(free_list.begin()); Chris@16: VALGRIND_MAKE_MEM_UNDEFINED(ret, chunk_size); Chris@16: } Chris@16: used_list.insert(ret); Chris@16: return ret; Chris@16: } Chris@16: void * ordered_malloc() Chris@16: { Chris@16: return (this->malloc)(); Chris@16: } Chris@16: void * ordered_malloc(size_type n) Chris@16: { Chris@16: if(max_alloc_size && (n > max_alloc_size)) Chris@16: return 0; Chris@16: void* ret = (user_allocator::malloc)(chunk_size * n); Chris@16: used_list.insert(ret); Chris@16: return ret; Chris@16: } Chris@16: void free BOOST_PREVENT_MACRO_SUBSTITUTION(void *const chunk) Chris@16: { Chris@16: BOOST_ASSERT(used_list.count(chunk) == 1); Chris@16: BOOST_ASSERT(free_list.count(chunk) == 0); Chris@16: used_list.erase(chunk); Chris@16: free_list.insert(chunk); Chris@16: VALGRIND_MAKE_MEM_NOACCESS(chunk, chunk_size); Chris@16: } Chris@16: void ordered_free(void *const chunk) Chris@16: { Chris@16: return (this->free)(chunk); Chris@16: } Chris@16: void free BOOST_PREVENT_MACRO_SUBSTITUTION(void *const chunk, const size_type) Chris@16: { Chris@16: BOOST_ASSERT(used_list.count(chunk) == 1); Chris@16: BOOST_ASSERT(free_list.count(chunk) == 0); Chris@16: used_list.erase(chunk); Chris@16: (user_allocator::free)(static_cast(chunk)); Chris@16: } Chris@16: void ordered_free(void *const chunk, const size_type n) Chris@16: { Chris@16: (this->free)(chunk, n); Chris@16: } Chris@16: bool is_from(void *const chunk) const Chris@16: { Chris@16: return used_list.count(chunk) || free_list.count(chunk); Chris@16: } Chris@16: Chris@16: protected: Chris@16: size_type chunk_size, max_alloc_size; Chris@16: std::set free_list, used_list; Chris@16: }; Chris@16: Chris@16: #endif Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: #ifdef BOOST_MSVC Chris@16: #pragma warning(pop) Chris@16: #endif Chris@16: Chris@16: #endif // #ifdef BOOST_POOL_HPP Chris@16: