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