Uses different pass count for different parallel queue test cases
[libcds.git] / cds / algo / atomic.h
index 51d26f83f57ff8a5e3a98fa6a0a561f1e2a09f29..987b6e0452ed7c09c895496cfb1e1bfa9f2f6626 100644 (file)
@@ -1,9 +1,38 @@
-//$$CDS-header$$
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (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:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    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.
+*/
 
 #ifndef CDSLIB_CXX11_ATOMIC_H
 #define CDSLIB_CXX11_ATOMIC_H
 
 #include <cds/details/defs.h>
+#include <cds/user_setup/cache_line.h>
 
 namespace cds {
 
@@ -93,7 +122,7 @@ namespace cds {
                 Returns \p n.
             */
             value_type operator =(
-                value_type n    //< new value of the counter
+                value_type n    ///< new value of the counter
             ) CDS_NOEXCEPT
             {
                 m_counter.exchange( n, atomics::memory_order_relaxed );
@@ -161,23 +190,24 @@ namespace cds {
             {
                 m_counter.store( 0, atomics::memory_order_release );
             }
-
         };
 
         /// Atomic item counter
         /**
-            This class is simplified interface around <tt>std::atomic_size_t</tt>.
-            The class supports getting of current value of the counter and increment/decrement its value.
+            This class is simplified interface around \p std::atomic_size_t.
+            The class supports getting current value of the counter and increment/decrement its value.
+
+            See also: improved version that eliminates false sharing - \p cache_friendly_item_counter.
         */
         class item_counter
         {
         public:
-            typedef atomics::atomic_size_t   atomic_type ;   ///< atomic type used
-            typedef size_t counter_type    ;   ///< Integral item counter type (size_t)
+            typedef atomics::atomic_size_t   atomic_type;   ///< atomic type used
+            typedef size_t counter_type;                    ///< Integral item counter type (size_t)
 
         private:
             //@cond
-            atomic_type                         m_Counter   ;   ///< Atomic item counter
+            atomic_type     m_Counter;   ///< Atomic item counter
             //@endcond
 
         public:
@@ -216,12 +246,143 @@ namespace cds {
                 return m_Counter.fetch_add( 1, order );
             }
 
+            /// Increments the counter. Semantics: postincrement
+            counter_type inc( counter_type count, atomics::memory_order order = atomics::memory_order_relaxed )
+            {
+                return m_Counter.fetch_add( count, order );
+            }
+
+            /// Decrements the counter. Semantics: postdecrement
+            counter_type dec(atomics::memory_order order = atomics::memory_order_relaxed)
+            {
+                return m_Counter.fetch_sub( 1, order );
+            }
+
+            /// Decrements the counter. Semantics: postdecrement
+            counter_type dec( counter_type count, atomics::memory_order order = atomics::memory_order_relaxed )
+            {
+                return m_Counter.fetch_sub( count, order );
+            }
+
+            /// Preincrement
+            counter_type operator ++()
+            {
+                return inc() + 1;
+            }
+            /// Postincrement
+            counter_type operator ++(int)
+            {
+                return inc();
+            }
+
+            /// Predecrement
+            counter_type operator --()
+            {
+                return dec() - 1;
+            }
+            /// Postdecrement
+            counter_type operator --(int)
+            {
+                return dec();
+            }
+
+            /// Increment by \p count
+            counter_type operator +=( counter_type count )
+            {
+                return inc( count ) + count;
+            }
+
+            /// Decrement by \p count
+            counter_type operator -=( counter_type count )
+            {
+                return dec( count ) - count;
+            }
+
+            /// Resets count to 0
+            void reset(atomics::memory_order order = atomics::memory_order_relaxed)
+            {
+                m_Counter.store( 0, order );
+            }
+        };
+
+#if CDS_COMPILER == CDS_COMPILER_CLANG
+    // CLang unhappy: pad1_ and pad2_ - unused private field warning
+#   pragma GCC diagnostic push
+#   pragma GCC diagnostic ignored "-Wunused-private-field"
+#endif
+        /// Atomic cache-friendly item counter
+        /**
+            Atomic item counter with cache-line padding to avoid false sharing.
+            Adding cache-line padding before and after atomic counter eliminates the contention
+            in read path of many containers and can notably improve search operations in sets/maps.
+        */
+        class cache_friendly_item_counter
+        {
+        public:
+            typedef atomics::atomic_size_t   atomic_type;   ///< atomic type used
+            typedef size_t counter_type;                    ///< Integral item counter type (size_t)
+
+        private:
+            //@cond
+            char            pad1_[cds::c_nCacheLineSize];
+            atomic_type     m_Counter;   ///< Atomic item counter
+            char            pad2_[cds::c_nCacheLineSize - sizeof( atomic_type )];
+            //@endcond
+
+        public:
+            /// Default ctor initializes the counter to zero.
+            cache_friendly_item_counter()
+                : m_Counter(counter_type(0))
+            {}
+
+            /// Returns current value of the counter
+            counter_type value(atomics::memory_order order = atomics::memory_order_relaxed) const
+            {
+                return m_Counter.load( order );
+            }
+
+            /// Same as \ref value() with relaxed memory ordering
+            operator counter_type() const
+            {
+                return value();
+            }
+
+            /// Returns underlying atomic interface
+            atomic_type& getAtomic()
+            {
+                return m_Counter;
+            }
+
+            /// Returns underlying atomic interface (const)
+            const atomic_type& getAtomic() const
+            {
+                return m_Counter;
+            }
+
+            /// Increments the counter. Semantics: postincrement
+            counter_type inc(atomics::memory_order order = atomics::memory_order_relaxed )
+            {
+                return m_Counter.fetch_add( 1, order );
+            }
+
+            /// Increments the counter. Semantics: postincrement
+            counter_type inc( counter_type count, atomics::memory_order order = atomics::memory_order_relaxed )
+            {
+                return m_Counter.fetch_add( count, order );
+            }
+
             /// Decrements the counter. Semantics: postdecrement
             counter_type dec(atomics::memory_order order = atomics::memory_order_relaxed)
             {
                 return m_Counter.fetch_sub( 1, order );
             }
 
+            /// Decrements the counter. Semantics: postdecrement
+            counter_type dec( counter_type count, atomics::memory_order order = atomics::memory_order_relaxed )
+            {
+                return m_Counter.fetch_sub( count, order );
+            }
+
             /// Preincrement
             counter_type operator ++()
             {
@@ -244,12 +405,27 @@ namespace cds {
                 return dec();
             }
 
+            /// Increment by \p count
+            counter_type operator +=( counter_type count )
+            {
+                return inc( count ) + count;
+            }
+
+            /// Decrement by \p count
+            counter_type operator -=( counter_type count )
+            {
+                return dec( count ) - count;
+            }
+
             /// Resets count to 0
             void reset(atomics::memory_order order = atomics::memory_order_relaxed)
             {
                 m_Counter.store( 0, order );
             }
         };
+#if CDS_COMPILER == CDS_COMPILER_CLANG
+#   pragma GCC diagnostic pop
+#endif
 
         /// Empty item counter
         /**
@@ -276,41 +452,67 @@ namespace cds {
             }
 
             /// Dummy increment. Always returns 0
-            static size_t inc(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
+            static counter_type inc(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
+            {
+                return 0;
+            }
+
+            /// Dummy increment. Always returns 0
+            static counter_type inc( counter_type /*count*/, atomics::memory_order /*order*/ = atomics::memory_order_relaxed )
+            {
+                return 0;
+            }
+
+            /// Dummy increment. Always returns 0
+            static counter_type dec(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
             {
                 return 0;
             }
 
             /// Dummy increment. Always returns 0
-            static size_t dec(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
+            static counter_type dec( counter_type /*count*/, atomics::memory_order /*order*/ = atomics::memory_order_relaxed )
             {
                 return 0;
             }
 
             /// Dummy pre-increment. Always returns 0
-            size_t operator ++() const
+            counter_type operator ++() const
             {
                 return 0;
             }
             /// Dummy post-increment. Always returns 0
-            size_t operator ++(int) const
+            counter_type operator ++(int) const
             {
                 return 0;
             }
 
             /// Dummy pre-decrement. Always returns 0
-            size_t operator --() const
+            counter_type operator --() const
             {
                 return 0;
             }
             /// Dummy post-decrement. Always returns 0
-            size_t operator --(int) const
+            counter_type operator --(int) const
+            {
+                return 0;
+            }
+
+            /// Dummy increment by \p count, always returns 0
+            counter_type operator +=( counter_type count )
+            {
+                CDS_UNUSED( count );
+                return 0;
+            }
+
+            /// Dummy decrement by \p count, always returns 0
+            counter_type operator -=( counter_type count )
             {
+                CDS_UNUSED( count );
                 return 0;
             }
 
             /// Dummy function
-            void reset(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
+            static void reset(atomics::memory_order /*order*/ = atomics::memory_order_relaxed)
             {}
         };
     }   // namespace atomicity