Uses different pass count for different parallel queue test cases
[libcds.git] / cds / opt / buffer.h
index 7c04ff007cfe83f8a6f10db22796dcea27003a9a..2623069eca3d967a69a33e0fcbd82422e8d22f4e 100644 (file)
@@ -1,11 +1,11 @@
 /*
     This file is a part of libcds - Concurrent Data Structures library
 
-    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
 
     Source code repo: http://github.com/khizmax/libcds/
     Download: http://sourceforge.net/projects/libcds/files/
-    
+
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions are met:
 
     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #ifndef CDSLIB_OPT_BUFFER_H
 #define CDSLIB_OPT_BUFFER_H
 
+#include <memory.h>
 #include <cds/details/defs.h>
 #include <cds/user_setup/allocator.h>
 #include <cds/details/allocator.h>
@@ -47,8 +48,19 @@ namespace cds { namespace opt {
         The template parameter \p Type should be rebindable.
 
         Implementations:
-            - \p opt::v::static_buffer
-            - \p opt::v::dynamic_buffer
+            - \p opt::v::initialized_static_buffer
+            - \p opt::v::uninitialized_static_buffer
+            - \p opt::v::initialized_dynamic_buffer
+            - \p opt::v::uninitialized_dynamic_buffer
+
+        Uninitialized buffer is just an array of uninitialized elements.
+        Each element should be manually constructed, for example with a placement new operator.
+        When the uninitialized buffer is destroyed the destructor of its element is not called.
+
+        Initialized buffer contains default-constructed elements. Element destructor is called automatically
+        when the buffer is destroyed.
+
+        @note Usually, initialized and uninitialized buffers are not interchangeable.
     */
     template <typename Type>
     struct buffer {
@@ -62,11 +74,13 @@ namespace cds { namespace opt {
 
     namespace v {
 
-        /// Static buffer
+        /// Static uninitialized buffer
         /**
             One of available type for \p opt::buffer option.
 
-            This buffer maintains static array. No dynamic memory allocation performed.
+            This buffer maintains static array of uninitialized elements.
+            You should manually construct each element when needed.
+            No dynamic memory allocation performed.
 
             \par Template parameters:
                 - \p T - item type the buffer stores
@@ -76,17 +90,141 @@ namespace cds { namespace opt {
                     size of a power of two.
         */
         template <typename T, size_t Capacity, bool Exp2 = true>
-        class static_buffer
+        class uninitialized_static_buffer
         {
         public:
-            typedef T   value_type  ;   ///< value type
-            static CDS_CONSTEXPR const size_t c_nCapacity = Capacity ;    ///< Capacity
+            typedef T   value_type;   ///< value type
+            static CDS_CONSTEXPR const size_t c_nCapacity = Capacity;    ///< Capacity
             static CDS_CONSTEXPR const bool c_bExp2 = Exp2; ///< \p Exp2 flag
 
             /// Rebind buffer for other template parameters
             template <typename Q, size_t Capacity2 = c_nCapacity, bool Exp22 = c_bExp2>
             struct rebind {
-                typedef static_buffer<Q, Capacity2, Exp22> other   ;   ///< Rebind result type
+                typedef uninitialized_static_buffer<Q, Capacity2, Exp22> other;   ///< Rebind result type
+            };
+
+            // Capacity must be power of 2
+            static_assert(!c_bExp2 || (c_nCapacity & (c_nCapacity - 1)) == 0, "Capacity must be power of two");
+
+        private:
+            //@cond
+            union element {
+                value_type v;
+                char       c;
+
+                element()
+                {}
+            };
+
+            element  m_buffer[c_nCapacity];
+            //@endcond
+        public:
+            /// Construct static buffer
+            uninitialized_static_buffer() CDS_NOEXCEPT
+            {}
+
+            /// Construct buffer of given capacity
+            /**
+                This ctor ignores \p nCapacity argument. The capacity of static buffer
+                is defined by template argument \p Capacity
+            */
+            uninitialized_static_buffer( size_t nCapacity ) CDS_NOEXCEPT
+            {
+                CDS_UNUSED( nCapacity );
+            }
+
+            uninitialized_static_buffer( const uninitialized_static_buffer& ) = delete;
+            uninitialized_static_buffer& operator =( const uninitialized_static_buffer& ) = delete;
+
+            /// Get item \p i
+            value_type& operator []( size_t i )
+            {
+                assert( i < capacity());
+                return m_buffer[i].v;
+            }
+
+            /// Get item \p i, const version
+            const value_type& operator []( size_t i ) const
+            {
+                assert( i < capacity());
+                return m_buffer[i].v;
+            }
+
+            /// Returns buffer capacity
+            CDS_CONSTEXPR size_t capacity() const CDS_NOEXCEPT
+            {
+                return c_nCapacity;
+            }
+
+            /// Zeroize the buffer
+            void zeroize()
+            {
+                memset( m_buffer, 0, capacity() * sizeof(m_buffer[0]));
+            }
+
+            /// Returns pointer to buffer array
+            value_type * buffer() CDS_NOEXCEPT
+            {
+                return &( m_buffer[0].v );
+            }
+
+            /// Returns pointer to buffer array
+            value_type * buffer() const CDS_NOEXCEPT
+            {
+                return &( m_buffer[0].v );
+            }
+
+            /// Returns <tt> idx % capacity() </tt>
+            /**
+                If the buffer size is a power of two, binary arithmethics is used
+                instead of modulo arithmetics
+            */
+            size_t mod( size_t idx )
+            {
+                static_if( c_bExp2 )
+                    return idx & ( capacity() - 1 );
+                else
+                    return idx % capacity();
+            }
+
+            //@cond
+            template <typename I>
+            typename std::enable_if< sizeof(I) != sizeof(size_t), size_t >::type mod( I idx )
+            {
+                static_if( c_bExp2 )
+                    return static_cast<size_t>( idx & static_cast<I>( capacity() - 1 ));
+                else
+                    return static_cast<size_t>( idx % capacity());
+            }
+            //@endcond
+        };
+
+        /// Static initialized buffer
+        /**
+            One of available type for \p opt::buffer option.
+
+            This buffer maintains static array of default-constructed elements.
+            No dynamic memory allocation performed.
+
+            \par Template parameters:
+                - \p T - item type the buffer stores
+                - \p Capacity - the capacity of buffer. The value must be power of two if \p Exp2 is \p true
+                - \p Exp2 - a boolean flag. If it is \p true the buffer capacity must be power of two.
+                    Otherwise it can be any positive number. Usually, it is required that the buffer has
+                    size of a power of two.
+        */
+        template <typename T, size_t Capacity, bool Exp2 = true>
+        class initialized_static_buffer
+        {
+        public:
+            typedef T   value_type;   ///< value type
+            static CDS_CONSTEXPR const size_t c_nCapacity = Capacity;    ///< Capacity
+            static CDS_CONSTEXPR const bool c_bExp2 = Exp2; ///< \p Exp2 flag
+
+            /// Rebind buffer for other template parameters
+            template <typename Q, size_t Capacity2 = c_nCapacity, bool Exp22 = c_bExp2>
+            struct rebind {
+                typedef initialized_static_buffer<Q, Capacity2, Exp22> other;   ///< Rebind result type
             };
 
             // Capacity must be power of 2
@@ -98,32 +236,33 @@ namespace cds { namespace opt {
             //@endcond
         public:
             /// Construct static buffer
-            static_buffer() CDS_NOEXCEPT
+            initialized_static_buffer() CDS_NOEXCEPT
             {}
+
             /// Construct buffer of given capacity
             /**
                 This ctor ignores \p nCapacity argument. The capacity of static buffer
                 is defined by template argument \p Capacity
             */
-            static_buffer( size_t nCapacity ) CDS_NOEXCEPT
+            initialized_static_buffer( size_t nCapacity ) CDS_NOEXCEPT
             {
                 CDS_UNUSED( nCapacity );
             }
 
-            static_buffer( const static_buffer& ) = delete;
-            static_buffer& operator =( const static_buffer& ) = delete;
+            initialized_static_buffer( const initialized_static_buffer& ) = delete;
+            initialized_static_buffer& operator =( const initialized_static_buffer& ) = delete;
 
             /// Get item \p i
             value_type& operator []( size_t i )
             {
-                assert( i < capacity() );
+                assert( i < capacity());
                 return m_buffer[i];
             }
 
             /// Get item \p i, const version
             const value_type& operator []( size_t i ) const
             {
-                assert( i < capacity() );
+                assert( i < capacity());
                 return m_buffer[i];
             }
 
@@ -136,7 +275,7 @@ namespace cds { namespace opt {
             /// Zeroize the buffer
             void zeroize()
             {
-                memset( m_buffer, 0, capacity() * sizeof(m_buffer[0]) );
+                memset( m_buffer, 0, capacity() * sizeof(m_buffer[0]));
             }
 
             /// Returns pointer to buffer array
@@ -150,14 +289,164 @@ namespace cds { namespace opt {
             {
                 return m_buffer;
             }
+
+            /// Returns <tt> idx % capacity() </tt>
+            /**
+            If the buffer size is a power of two, binary arithmethics is used
+            instead of modulo arithmetics
+            */
+            size_t mod( size_t idx )
+            {
+                static_if( c_bExp2 )
+                    return idx & ( capacity() - 1 );
+                else
+                    return idx % capacity();
+            }
+
+            //@cond
+            template <typename I>
+            typename std::enable_if< sizeof( I ) != sizeof( size_t ), size_t >::type mod( I idx )
+            {
+                static_if( c_bExp2 )
+                    return static_cast<size_t>( idx & static_cast<I>( capacity() - 1 ));
+                else
+                    return static_cast<size_t>( idx % capacity());
+            }
+            //@endcond
         };
 
+        /// Dynamically allocated uninitialized buffer
+        /**
+            One of available type for \p opt::buffer option.
 
-        /// Dynamically allocated buffer
+            This buffer maintains dynamically allocated array of uninitialized elements.
+            You should manually construct each element when needed.
+            Allocation is performed at construction time.
+
+            \par Template parameters:
+                - \p T - item type storing in the buffer
+                - \p Alloc - an allocator used for allocating internal buffer (\p std::allocator interface)
+                - \p Exp2 - a boolean flag. If it is \p true the buffer capacity must be power of two.
+                    Otherwise it can be any positive number. Usually, it is required that the buffer has
+                    size of a power of two.
+        */
+        template <typename T, class Alloc = CDS_DEFAULT_ALLOCATOR, bool Exp2 = true>
+        class uninitialized_dynamic_buffer
+        {
+        public:
+            typedef T     value_type;   ///< Value type
+            typedef Alloc allocator;    ///< Allocator type;
+            static CDS_CONSTEXPR const bool c_bExp2 = Exp2; ///< \p Exp2 flag
+
+            /// Rebind buffer for other template parameters
+            template <typename Q, typename Alloc2= allocator, bool Exp22 = c_bExp2>
+            struct rebind {
+                typedef uninitialized_dynamic_buffer<Q, Alloc2, Exp22> other;  ///< Rebinding result type
+            };
+
+            //@cond
+            typedef typename allocator::template rebind<value_type>::other allocator_type;
+            //@endcond
+
+        private:
+            //@cond
+            value_type *    m_buffer;
+            size_t const    m_nCapacity;
+            //@endcond
+        public:
+            /// Allocates dynamic buffer of given \p nCapacity
+            /**
+                If \p Exp2 class template parameter is \p true then actual capacity
+                of allocating buffer is nearest upper to \p nCapacity power of two.
+            */
+            uninitialized_dynamic_buffer( size_t nCapacity )
+                : m_nCapacity( c_bExp2 ? beans::ceil2(nCapacity) : nCapacity )
+            {
+                assert( m_nCapacity >= 2 );
+                // Capacity must be power of 2
+                assert( !c_bExp2 || (m_nCapacity & (m_nCapacity - 1)) == 0 );
+
+                m_buffer = allocator_type().allocate( m_nCapacity );
+            }
+
+            /// Destroys dynamically allocated buffer
+            ~uninitialized_dynamic_buffer()
+            {
+                allocator_type().deallocate( m_buffer, m_nCapacity );
+            }
+
+            uninitialized_dynamic_buffer( const uninitialized_dynamic_buffer& ) = delete;
+            uninitialized_dynamic_buffer& operator =( const uninitialized_dynamic_buffer& ) = delete;
+
+            /// Get item \p i
+            value_type& operator []( size_t i )
+            {
+                assert( i < capacity());
+                return m_buffer[i];
+            }
+
+            /// Get item \p i, const version
+            const value_type& operator []( size_t i ) const
+            {
+                assert( i < capacity());
+                return m_buffer[i];
+            }
+
+            /// Returns buffer capacity
+            size_t capacity() const CDS_NOEXCEPT
+            {
+                return m_nCapacity;
+            }
+
+            /// Zeroize the buffer
+            void zeroize()
+            {
+                memset( m_buffer, 0, capacity() * sizeof(m_buffer[0]));
+            }
+
+            /// Returns pointer to buffer array
+            value_type * buffer() CDS_NOEXCEPT
+            {
+                return m_buffer;
+            }
+
+            /// Returns pointer to buffer array
+            value_type * buffer() const CDS_NOEXCEPT
+            {
+                return m_buffer;
+            }
+
+            /// Returns <tt> idx % capacity() </tt>
+            /**
+                If the buffer size is a power of two, binary arithmethics is used
+                instead of modulo arithmetics
+            */
+            size_t mod( size_t idx )
+            {
+                static_if ( c_bExp2 )
+                    return idx & ( capacity() - 1 );
+                else
+                    return idx % capacity();
+            }
+
+            //@cond
+            template <typename I>
+            typename std::enable_if< sizeof( I ) != sizeof( size_t ), size_t >::type mod( I idx )
+            {
+                static_if ( c_bExp2 )
+                    return static_cast<size_t>( idx & static_cast<I>( capacity() - 1 ));
+                else
+                    return static_cast<size_t>( idx % capacity());
+            }
+            //@endcond
+        };
+
+
+        /// Dynamically allocated initialized buffer
         /**
             One of available type for \p opt::buffer option.
 
-            This buffer maintains dynamically allocated array.
+            This buffer maintains dynamically allocated array of initialized default-constructed elements.
             Allocation is performed at construction time.
 
             \par Template parameters:
@@ -168,20 +457,21 @@ namespace cds { namespace opt {
                     size of a power of two.
         */
         template <typename T, class Alloc = CDS_DEFAULT_ALLOCATOR, bool Exp2 = true>
-        class dynamic_buffer
+        class initialized_dynamic_buffer
         {
         public:
-            typedef T   value_type  ;   ///< Value type
+            typedef T     value_type;   ///< Value type
+            typedef Alloc allocator;    ///< Allocator type
             static CDS_CONSTEXPR const bool c_bExp2 = Exp2; ///< \p Exp2 flag
 
             /// Rebind buffer for other template parameters
-            template <typename Q, typename Alloc2=Alloc, bool Exp22 = c_bExp2>
+            template <typename Q, typename Alloc2= allocator, bool Exp22 = c_bExp2>
             struct rebind {
-                typedef dynamic_buffer<Q, Alloc2, Exp22> other   ;  ///< Rebinding result type
+                typedef initialized_dynamic_buffer<Q, Alloc2, Exp22> other;  ///< Rebinding result type
             };
 
             //@cond
-            typedef cds::details::Allocator<value_type, Alloc>   allocator_type;
+            typedef cds::details::Allocator<value_type, allocator>   allocator_type;
             //@endcond
 
         private:
@@ -195,7 +485,7 @@ namespace cds { namespace opt {
                 If \p Exp2 class template parameter is \p true then actual capacity
                 of allocating buffer is nearest upper to \p nCapacity power of two.
             */
-            dynamic_buffer( size_t nCapacity )
+            initialized_dynamic_buffer( size_t nCapacity )
                 : m_nCapacity( c_bExp2 ? beans::ceil2(nCapacity) : nCapacity )
             {
                 assert( m_nCapacity >= 2 );
@@ -207,26 +497,26 @@ namespace cds { namespace opt {
             }
 
             /// Destroys dynamically allocated buffer
-            ~dynamic_buffer()
+            ~initialized_dynamic_buffer()
             {
                 allocator_type a;
                 a.Delete( m_buffer, m_nCapacity );
             }
 
-            dynamic_buffer( const dynamic_buffer& ) = delete;
-            dynamic_buffer& operator =( const dynamic_buffer& ) = delete;
+            initialized_dynamic_buffer( const initialized_dynamic_buffer& ) = delete;
+            initialized_dynamic_buffer& operator =( const initialized_dynamic_buffer& ) = delete;
 
             /// Get item \p i
             value_type& operator []( size_t i )
             {
-                assert( i < capacity() );
+                assert( i < capacity());
                 return m_buffer[i];
             }
 
             /// Get item \p i, const version
             const value_type& operator []( size_t i ) const
             {
-                assert( i < capacity() );
+                assert( i < capacity());
                 return m_buffer[i];
             }
 
@@ -239,7 +529,7 @@ namespace cds { namespace opt {
             /// Zeroize the buffer
             void zeroize()
             {
-                memset( m_buffer, 0, capacity() * sizeof(m_buffer[0]) );
+                memset( m_buffer, 0, capacity() * sizeof(m_buffer[0]));
             }
 
             /// Returns pointer to buffer array
@@ -253,6 +543,30 @@ namespace cds { namespace opt {
             {
                 return m_buffer;
             }
+
+            /// Returns <tt> idx % capacity() </tt>
+            /**
+            If the buffer size is a power of two, binary arithmethics is used
+            instead of modulo arithmetics
+            */
+            size_t mod( size_t idx )
+            {
+                static_if( c_bExp2 )
+                    return idx & ( capacity() - 1 );
+                else
+                    return idx % capacity();
+            }
+
+            //@cond
+            template <typename I>
+            typename std::enable_if< sizeof( I ) != sizeof( size_t ), size_t >::type mod( I idx )
+            {
+                static_if( c_bExp2 )
+                    return static_cast<size_t>( idx & static_cast<I>( capacity() - 1 ));
+                else
+                    return static_cast<size_t>( idx % capacity());
+            }
+            //@endcond
         };
 
     }   // namespace v