2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #ifndef CDSLIB_MEMORY_VYUKOV_QUEUE_ALLOCATOR_H
32 #define CDSLIB_MEMORY_VYUKOV_QUEUE_ALLOCATOR_H
34 #include <cds/details/allocator.h>
35 #include <cds/intrusive/vyukov_mpmc_cycle_queue.h>
37 namespace cds { namespace memory {
39 /// \p vyukov_queue_pool traits
40 /** @ingroup cds_memory_pool
42 struct vyukov_queue_pool_traits : public cds::intrusive::vyukov_queue::traits
45 typedef CDS_DEFAULT_ALLOCATOR allocator;
48 /// Free-list based on bounded lock-free queue \p cds::intrusive::VyukovMPMCCycleQueue
49 /** @ingroup cds_memory_pool
51 - \p T - the type of object maintaining by free-list. \p T must be default constructible.
52 - \p Traits - traits for \p cds::intrusive::VyukovMPMCCycleQueue class plus
53 \p cds::opt::allocator option, defaul is \p vyukov_queue_pool_traits
57 This free-list is very simple.
58 At construction time, the free-list allocates the array of N items
59 and stores them into queue, where N is the queue capacity.
60 When allocating the free-list tries to pop an object from
61 internal queue i.e. from preallocated pool. If success the popped object is returned.
62 Otherwise a new one is allocated. When deallocating, the free-list checks whether
63 the object is from preallocated pool. If so, the object is pushed into queue, otherwise
64 it is deallocated by using the allocator provided.
65 The pool can manage more than \p N items but only \p N items is contained in the free-list.
69 \p %vyukov_queue_pool should be used together with \ref pool_allocator.
70 You should declare an static object of type \p %vyukov_queue_pool, provide
71 an accessor to that object and use \p pool_allocator as an allocator:
73 #include <cds/memory/vyukov_queue_pool.h>
74 #include <cds/memory/pool_allocator.h>
76 // Pool of Foo object of size 1024.
77 struct pool_traits: public cds::memory::vyukov_queue_pool_traits
79 typedef cds::opt::v::uninitialized_static_buffer< Foo, 1024 > buffer;
81 typedef cds::memory::vyukov_queue_pool< Foo, pool_traits > pool_type;
82 static pool_type thePool;
84 struct pool_accessor {
85 typedef typename pool_type::value_type value_type;
87 pool_type& operator()() const
93 // Declare pool allocator
94 typedef cds::memory::pool_allocator< Foo, pool_accessor > pool_allocator;
98 Foo * p = pool_allocator().allocate( 1 );
109 pool_allocator().deallocate( p , 1 );
112 template <typename T, typename Traits = vyukov_queue_pool_traits >
113 class vyukov_queue_pool
116 typedef cds::intrusive::VyukovMPMCCycleQueue< T, Traits > queue_type ; ///< Queue type
119 typedef T value_type ; ///< Value type
120 typedef Traits traits; ///< Traits type
121 typedef typename traits::allocator::template rebind<value_type>::other allocator_type ; ///< allocator type
122 typedef typename traits::back_off back_off; ///< back-off strategy
126 typedef cds::details::Allocator< value_type, allocator_type > cxx_allocator;
127 typedef typename cxx_allocator::allocator_type std_allocator;
130 value_type * m_pFirst;
131 value_type * m_pLast;
136 void preallocate_pool()
138 m_pFirst = std_allocator().allocate( m_Queue.capacity() );
139 m_pLast = m_pFirst + m_Queue.capacity();
141 for ( value_type * p = m_pFirst; p < m_pLast; ++p ) {
142 CDS_VERIFY( m_Queue.push( *p )) ; // must be true
146 bool from_pool( value_type * p ) const
148 return m_pFirst <= p && p < m_pLast;
153 /// Preallocates the pool of object
155 \p nCapacity argument is the queue capacity. It should be passed
156 if the queue is based on dynamically-allocated buffer.
157 See \p cds::intrusive::VyukovMPMCCycleQueue for explanation.
159 vyukov_queue_pool( size_t nCapacity = 0 )
160 : m_Queue( nCapacity )
165 /// Deallocates the pool.
169 std_allocator().deallocate( m_pFirst, m_Queue.capacity());
172 /// Allocates an object from pool
174 The pool supports allocation only single object (\p n = 1).
175 If \p n > 1 the behavior is undefined.
177 If the queue is not empty, the popped value is returned.
178 Otherwise, a new value allocated.
180 value_type * allocate( size_t n )
185 value_type * p = m_Queue.pop();
187 assert( from_pool(p) );
188 return new( p ) value_type;
190 // The pool is empty - allocate new from the heap
191 return cxx_allocator().New();
194 /// Deallocated the object \p p
196 The pool supports allocation only single object (\p n = 1).
197 If \p n > 1 the behavior is undefined.
199 If \p p is from preallocated pool, it pushes into the queue.
200 Otherwise, \p p is deallocated by allocator provided.
202 void deallocate( value_type * p, size_t n )
208 if ( from_pool(p) ) {
210 // The queue can notify about false fullness state
211 // so we push in loop
213 while ( !m_Queue.push( *p ))
217 cxx_allocator().Delete( p );
223 /// Lazy free-list based on bounded lock-free queue \p cds::intrusive::VyukovMPMCCycleQueue
224 /** @ingroup cds_memory_pool
226 - \p T - the type of object maintaining by free-list. \p T must be default constructible
227 - \p Traits - traits for \p cds::intrusive::VyukovMPMCCycleQueue class plus
228 \p cds::opt::allocator option, default is \p vyukov_queue_pool_traits
232 This free-list is very simple.
233 At construction time the pool is empty.
234 When allocating the free-list tries to pop an object from
235 internal queue. If success the popped object is returned.
236 Otherwise a new one is allocated.
237 When deallocating, the free-list tries to push the object into the pool.
238 If internal queue is full, the object is deallocated by using the allocator provided.
239 The pool can manage more than \p N items but only \p N items is placed in the free-list.
243 \p %lazy_vyukov_queue_pool should be used together with \ref pool_allocator.
244 You should declare an static object of type \p %lazy_vyukov_queue_pool, provide
245 an accessor functor to this object and use \p pool_allocator as an allocator:
247 #include <cds/memory/vyukov_queue_pool.h>
248 #include <cds/memory/pool_allocator.h>
250 // Pool of Foo object of size 1024.
251 typedef cds::memory::lazy_vyukov_queue_pool< Foo > pool_type;
252 static pool_type thePool( 1024 );
254 struct pool_accessor {
255 typedef typename pool_type::value_type value_type;
257 pool_type& operator()() const
263 // Declare pool allocator
264 typedef cds::memory::pool_allocator< Foo, pool_accessor > pool_allocator;
266 // Use pool_allocator
267 // Allocate an object
268 Foo * p = pool_allocator().allocate( 1 );
279 pool_allocator().deallocate( p , 1 );
283 template <typename T, typename Traits = vyukov_queue_pool_traits>
284 class lazy_vyukov_queue_pool
287 typedef cds::intrusive::VyukovMPMCCycleQueue< T, Traits > queue_type ; ///< Queue type
290 typedef T value_type ; ///< Value type
291 typedef Traits traits; ///< Pool traits
292 typedef typename traits::allocator::template rebind<value_type>::other allocator_type ; ///< allocator type
296 typedef cds::details::Allocator< value_type, allocator_type > cxx_allocator;
297 typedef typename cxx_allocator::allocator_type std_allocator;
303 /// Constructs empty pool
304 lazy_vyukov_queue_pool( size_t nCapacity = 0 )
305 : m_Queue( nCapacity )
308 /// Deallocates all objects from the pool
309 ~lazy_vyukov_queue_pool()
312 while ( !m_Queue.empty() )
313 a.deallocate( m_Queue.pop(), 1 );
316 /// Allocates an object from pool
318 The pool supports allocation only single object (\p n = 1).
319 If \p n > 1 the behavior is undefined.
321 If the queue is not empty, the popped value is returned.
322 Otherwise, a new value allocated.
324 value_type * allocate( size_t n )
329 value_type * p = m_Queue.pop();
331 return new( p ) value_type;
333 return cxx_allocator().New();
336 /// Deallocates the object \p p
338 The pool supports allocation only single object (\p n = 1).
339 If \p n > 1 the behaviour is undefined.
341 If the queue is not full, \p p is pushed into the queue.
342 Otherwise, \p p is deallocated by allocator provided.
344 void deallocate( value_type * p, size_t n )
351 // Here we ignore false fullness state of the queue
352 if ( !m_Queue.push( *p ))
353 std_allocator().deallocate( p, 1 );
359 /// Bounded free-list based on bounded lock-free queue \p cds::intrusive::VyukovMPMCCycleQueue
360 /** @ingroup cds_memory_pool
362 - \p T - the type of object maintaining by free-list. \p T must be default-constructible
363 - \p Traits - traits for \p cds::intrusive::VyukovMPMCCycleQueue class plus
364 \p cds::opt::allocator option, defaul is \p vyukov_queue_pool_traits
368 At construction time, the free-list allocates the array of N items
369 and stores them into the queue, where N is the queue capacity.
370 When allocating the free-list tries to pop an object from
371 internal queue i.e. from preallocated pool. If success the popped object is returned.
372 Otherwise a \p std::bad_alloc exception is raised.
373 So, the pool can contain up to \p N items.
374 When deallocating, the object is pushed into the queue.
375 In debug mode \p deallocate() member function asserts
376 that the pointer is from preallocated pool.
380 \p %bounded_vyukov_queue_pool should be used together with \ref pool_allocator.
381 You should declare an static object of type \p %bounded_vyukov_queue_pool, provide
382 an accessor functor to this object and use \p pool_allocator as an allocator:
384 #include <cds/memory/vyukov_queue_pool.h>
385 #include <cds/memory/pool_allocator.h>
387 // Pool of Foo object of size 1024.
388 struct pool_traits: public cds::memory::vyukov_queue_pool_traits
390 typedef cds::opt::v::uninitialized_static_buffer< Foo, 1024 > buffer;
392 typedef cds::memory::bounded_vyukov_queue_pool< Foo, pool_traits > pool_type;
393 static pool_type thePool;
395 struct pool_accessor {
396 typedef typename pool_type::value_type value_type;
398 pool_type& operator()() const
404 // Declare pool allocator
405 typedef cds::memory::pool_allocator< Foo, pool_accessor > pool_allocator;
407 // Use pool_allocator
408 // Allocate an object
409 Foo * p = pool_allocator().allocate( 1 );
420 pool_allocator().deallocate( p , 1 );
423 template <typename T, typename Traits = vyukov_queue_pool_traits >
424 class bounded_vyukov_queue_pool
427 struct internal_traits : public Traits {
428 typedef cds::atomicity::item_counter item_counter;
432 typedef cds::intrusive::VyukovMPMCCycleQueue< T, internal_traits > queue_type ; ///< Queue type
435 typedef T value_type; ///< Value type
436 typedef Traits traits; ///< Pool traits
437 typedef typename traits::allocator::template rebind<value_type>::other allocator_type ; ///< allocator type
438 typedef typename traits::back_off back_off; ///< back-off strategy
442 typedef cds::details::Allocator< value_type, allocator_type > cxx_allocator;
443 typedef typename cxx_allocator::allocator_type std_allocator;
446 value_type * m_pFirst;
447 value_type * m_pLast;
452 void preallocate_pool()
454 size_t const nCount = m_Queue.capacity();
455 m_pFirst = std_allocator().allocate( nCount );
456 m_pLast = m_pFirst + nCount;
458 for ( value_type * p = m_pFirst; p < m_pLast; ++p )
459 CDS_VERIFY( m_Queue.push( *p )) ; // must be true
462 bool from_pool( value_type * p ) const
464 return m_pFirst <= p && p < m_pLast;
469 /// Preallocates the pool of object
471 \p nCapacity argument is the queue capacity. It should be passed
472 if the queue is based on dynamically-allocated buffer.
473 See \p cds::intrusive::VyukovMPMCCycleQueue for explanation.
475 bounded_vyukov_queue_pool( size_t nCapacity = 0 )
476 : m_Queue( nCapacity )
481 /// Deallocates the pool.
482 ~bounded_vyukov_queue_pool()
485 std_allocator().deallocate( m_pFirst, m_Queue.capacity());
488 /// Allocates an object from pool
490 The pool supports allocation only single object (\p n = 1).
491 If \p n > 1 the behaviour is undefined.
493 If the queue is not empty, the popped value is returned.
494 Otherwise, a \p std::bad_alloc exception is raised.
496 value_type * allocate( size_t n )
501 value_type * p = m_Queue.pop();
505 while ( m_Queue.size() ) {
513 throw std::bad_alloc();
517 assert( from_pool(p) );
521 /// Deallocates the object \p p
523 The pool supports allocation only single object (\p n = 1).
524 If \p n > 1 the behaviour is undefined.
526 \p p should be from preallocated pool.
528 void deallocate( value_type * p, size_t n )
534 assert( from_pool( p ));
536 // The queue can notify it is full but that is false fullness state
537 // So, we push in loop
538 while ( !m_Queue.push(*p) )
545 }} // namespace cds::memory
548 #endif // #ifndef CDSLIB_MEMORY_VYUKOV_QUEUE_ALLOCATOR_H