Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/atomic/atomic fetch add"

From cppreference.com
< cpp‎ | atomic
m (Integral => T)
(P0558)
Line 3: Line 3:
 
{{dcl begin}}
 
{{dcl begin}}
 
{{dcl header | atomic }}
 
{{dcl header | atomic }}
{{dcl h | implemented only for {{tt|atomic<''Integral''>}}{{mark c++11}} and  {{tt|atomic<''Floating''>}}{{mark c++20}} template specializations }}
 
 
{{dcl rev begin | num=1 }}
 
{{dcl rev begin | num=1 }}
 
{{dcl |  
 
{{dcl |  
 
template< class T >
 
template< class T >
T atomic_fetch_add( std::atomic<T>* obj, T arg ) noexcept;
+
T atomic_fetch_add( std::atomic<T>* obj,
 +
                    typename std::atomic<T>::difference_type arg ) noexcept;
 
}}
 
}}
 
{{dcl |
 
{{dcl |
 
template< class T >
 
template< class T >
T atomic_fetch_add( volatile std::atomic<T>* obj, T arg ) noexcept;
+
T atomic_fetch_add( volatile std::atomic<T>* obj,
 +
                    typename std::atomic<T>::difference_type arg ) noexcept;
 
}}
 
}}
 
{{dcl rev end}}
 
{{dcl rev end}}
{{dcl h | implemented only for {{tt|atomic<''Integral''>}}{{mark c++11}} and  {{tt|atomic<''Floating''>}}{{mark c++20}} template specializations }}
 
 
{{dcl rev begin | num=2 }}
 
{{dcl rev begin | num=2 }}
 
{{dcl |  
 
{{dcl |  
 
template< class T >
 
template< class T >
T atomic_fetch_add_explicit( std::atomic<T>* obj, T arg,  
+
T atomic_fetch_add_explicit( std::atomic<T>* obj,  
 +
                            typename std::atomic<T>::difference_type arg,  
 
                             std::memory_order order ) noexcept;
 
                             std::memory_order order ) noexcept;
 
}}
 
}}
 
{{dcl |  
 
{{dcl |  
 
template< class T >
 
template< class T >
T atomic_fetch_add_explicit( volatile std::atomic<T>* obj, T arg,  
+
T atomic_fetch_add_explicit( volatile std::atomic<T>* obj,
 +
                            typename std::atomic<T>::difference_type arg,  
 
                             std::memory_order order ) noexcept;
 
                             std::memory_order order ) noexcept;
}}
 
{{dcl rev end}}
 
{{dcl rev begin | num=3 | since=c++11}}
 
{{dcl |
 
template< class T >
 
T* atomic_fetch_add( std::atomic<T*>* obj, std::ptrdiff_t arg ) noexcept;
 
}}
 
{{dcl |
 
template< class T >
 
T* atomic_fetch_add( volatile std::atomic<T*>* obj, std::ptrdiff_t arg ) noexcept;
 
}}
 
{{dcl rev end}}
 
{{dcl rev begin | num=4 | since=c++11}}
 
{{dcl |
 
template< class T >
 
T* atomic_fetch_add_explicit( std::atomic<T*>* obj, std::ptrdiff_t arg,
 
                              std::memory_order order ) noexcept;
 
}}
 
{{dcl |
 
template< class T >
 
T* atomic_fetch_add_explicit( volatile std::atomic<T*>* obj, std::ptrdiff_t arg,
 
                              std::memory_order order ) noexcept;
 
 
}}
 
}}
 
{{dcl rev end}}
 
{{dcl rev end}}
 
{{dcl end}}
 
{{dcl end}}
  
{{todo|apply P0558 (change the second parameter to be non-deduced context)}}
+
Performs atomic addition. Atomically adds {{tt|arg}} to the value pointed to by {{tt|obj}} and returns the value {{tt|obj}} held previously. The operation is performed as if the following was executed:
 
+
Performs atomic addition.
+
 
+
@1-2@ Atomically adds {{tt|arg}} to the value pointed to by {{tt|obj}} and returns the value {{tt|obj}} held previously. The operation is performed as if the following was executed:
+
  
 
:@1@ {{c|obj->fetch_add(arg)}}
 
:@1@ {{c|obj->fetch_add(arg)}}
  
 
:@2@ {{c|obj->fetch_add(arg, order)}}
 
:@2@ {{c|obj->fetch_add(arg, order)}}
 
@3-4@ Atomically increments the pointer value, pointed to by {{tt|obj}}, by {{tt|arg}}, and returns the value {{tt|obj}} held previously. The operation is performed as if the following was executed:
 
 
:@3@ {{c|obj->fetch_add(arg)}}
 
 
:@4@ {{c|obj->fetch_add(arg, order)}}
 
  
 
===Parameters===
 
===Parameters===
Line 71: Line 41:
 
{{par | obj | pointer to the atomic object to modify}}
 
{{par | obj | pointer to the atomic object to modify}}
 
{{par | arg | the value to add to the value stored in the atomic object}}
 
{{par | arg | the value to add to the value stored in the atomic object}}
{{par | order | the memory sycnhronization ordering for this operation: all values are permitted.}}
+
{{par | order | the memory synchronization ordering for this operation: all values are permitted.}}
 
{{par end}}  
 
{{par end}}  
  
 
===Return value===
 
===Return value===
 
The value immediately preceding the effects of this function in the [[cpp/atomic/memory_order#Modification_order|modification order]] of {{tt|*obj}}.
 
The value immediately preceding the effects of this function in the [[cpp/atomic/memory_order#Modification_order|modification order]] of {{tt|*obj}}.
 
 
  
 
===Possible implementation===
 
===Possible implementation===
Line 83: Line 51:
 
  | 1=
 
  | 1=
 
template< class T >
 
template< class T >
typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, T>::type
+
T atomic_fetch_add( std::atomic<T>* obj, typename std::atomic<T>::difference_type arg );
atomic_fetch_add( std::atomic<T>* obj, T arg );
+
{
+
    return obj->fetch_add(arg);
+
}
+
| 2=
+
template< class T >
+
T* atomic_fetch_add( std::atomic<T*>* obj, std::ptrdiff_t arg)
+
 
{
 
{
 
     return obj->fetch_add(arg);
 
     return obj->fetch_add(arg);
Line 175: Line 136:
 
reader 1 sees 24
 
reader 1 sees 24
 
}}
 
}}
 +
 +
===Defect reports===
 +
{{dr list begin}}
 +
{{dr list item|std=C++11|paper=P0558R1|before=exact type match required because {{tt|T}} is deduced from multiple arguments|after={{tt|T}} is deduced from the {{tt|atomic}} argument only}}
 +
{{dr list end}}
  
 
===See also===
 
===See also===

Revision as of 16:03, 14 February 2018

 
 
 
Defined in header <atomic>
(1)
template< class T >

T atomic_fetch_add( std::atomic<T>* obj,

                    typename std::atomic<T>::difference_type arg ) noexcept;
template< class T >

T atomic_fetch_add( volatile std::atomic<T>* obj,

                    typename std::atomic<T>::difference_type arg ) noexcept;
(2)
template< class T >

T atomic_fetch_add_explicit( std::atomic<T>* obj,
                             typename std::atomic<T>::difference_type arg,

                             std::memory_order order ) noexcept;
template< class T >

T atomic_fetch_add_explicit( volatile std::atomic<T>* obj,
                             typename std::atomic<T>::difference_type arg,

                             std::memory_order order ) noexcept;

Performs atomic addition. Atomically adds arg to the value pointed to by obj and returns the value obj held previously. The operation is performed as if the following was executed:

1) obj->fetch_add(arg)
2) obj->fetch_add(arg, order)

Contents

Parameters

obj - pointer to the atomic object to modify
arg - the value to add to the value stored in the atomic object
order - the memory synchronization ordering for this operation: all values are permitted.

Return value

The value immediately preceding the effects of this function in the modification order of *obj.

Possible implementation

template< class T >
T atomic_fetch_add( std::atomic<T>* obj, typename std::atomic<T>::difference_type arg );
{
    return obj->fetch_add(arg);
}

Example

Single-writer/multiple-reader lock can be made with fetch_add. Note that this simplistic implementation is not lockout-free

#include <string>
#include <thread>
#include <vector>
#include <iostream>
#include <atomic>
#include <chrono>
 
// meaning of cnt:
// 5: there are no active readers or writers.
// 1...4: there are 4...1 readers active, The writer is blocked
// 0: temporary value between fetch_sub and fetch_add in reader lock
// -1: there is a writer active. The readers are blocked.
const int N = 5; // four concurrent readers are allowed
std::atomic<int> cnt(N);
 
std::vector<int> data;
 
void reader(int id)
{
    for(;;)
    {
        // lock
        while(std::atomic_fetch_sub(&cnt, 1) <= 0)
            std::atomic_fetch_add(&cnt, 1);
        // read
        if(!data.empty())
            std::cout << (  "reader " + std::to_string(id)
                          + " sees " + std::to_string(*data.rbegin()) + '\n');
        if(data.size() == 25)
            break;
        // unlock
        std::atomic_fetch_add(&cnt, 1);
        // pause
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}
 
void writer()
{
    for(int n = 0; n < 25; ++n)
    {
        // lock
        while(std::atomic_fetch_sub(&cnt, N+1) != N)
            std::atomic_fetch_add(&cnt, N+1);
        // write
        data.push_back(n);
        std::cout << "writer pushed back " << n << '\n';
        // unlock
        std::atomic_fetch_add(&cnt, N+1);
        // pause
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}
 
int main()
{
    std::vector<std::thread> v;
    for (int n = 0; n < N; ++n) {
        v.emplace_back(reader, n);
    }
    v.emplace_back(writer);
    for (auto& t : v) {
        t.join();
    }
}

Output:

writer pushed back 0
reader 2 sees 0
reader 3 sees 0
reader 1 sees 0
<...>
reader 2 sees 24
reader 4 sees 24
reader 1 sees 24

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
P0558R1 C++11 exact type match required because T is deduced from multiple arguments T is deduced from the atomic argument only

See also

atomically adds the argument to the value stored in the atomic object and obtains the value held previously
(public member function of std::atomic<T>) [edit]
subtracts a non-atomic value from an atomic object and obtains the previous value of the atomic
(function template) [edit]
C documentation for atomic_fetch_add, atomic_fetch_add_explicit