GraphLab: Distributed Graph-Parallel API  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
atomic.hpp
1 /**
2  * Copyright (c) 2009 Carnegie Mellon University.
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an "AS
13  * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language
15  * governing permissions and limitations under the License.
16  *
17  * For more about this software visit:
18  *
19  * http://www.graphlab.ml.cmu.edu
20  *
21  */
22 
23 
24 #ifndef GRAPHLAB_ATOMIC_HPP
25 #define GRAPHLAB_ATOMIC_HPP
26 
27 #include <stdint.h>
28 
29 #include <boost/type_traits/is_integral.hpp>
30 
31 #include <graphlab/serialization/serialization_includes.hpp>
32 #include <graphlab/parallel/atomic_ops.hpp>
33 
34 namespace graphlab {
35 namespace graphlab_impl {
36  template<typename T, bool IsIntegral>
37  class atomic_impl {};
38  /**
39  * \internal
40  * \brief atomic object
41  * A templated class for creating atomic numbers.
42  */
43  template<typename T>
44  class atomic_impl <T, true>: public IS_POD_TYPE {
45  public:
46  //! The current value of the atomic number
47  volatile T value;
48 
49  //! Creates an atomic number with value "value"
50  atomic_impl(const T& value = T()) : value(value) { }
51 
52  //! Performs an atomic increment by 1, returning the new value
53  T inc() { return __sync_add_and_fetch(&value, 1); }
54 
55  //! Performs an atomic decrement by 1, returning the new value
56  T dec() { return __sync_sub_and_fetch(&value, 1); }
57 
58  //! Lvalue implicit cast
59  operator T() const { return value; }
60 
61  //! Performs an atomic increment by 1, returning the new value
62  T operator++() { return inc(); }
63 
64  //! Performs an atomic decrement by 1, returning the new value
65  T operator--() { return dec(); }
66 
67  //! Performs an atomic increment by 'val', returning the new value
68  T inc(const T val) { return __sync_add_and_fetch(&value, val); }
69 
70  //! Performs an atomic decrement by 'val', returning the new value
71  T dec(const T val) { return __sync_sub_and_fetch(&value, val); }
72 
73  //! Performs an atomic increment by 'val', returning the new value
74  T operator+=(const T val) { return inc(val); }
75 
76  //! Performs an atomic decrement by 'val', returning the new value
77  T operator-=(const T val) { return dec(val); }
78 
79  //! Performs an atomic increment by 1, returning the old value
80  T inc_ret_last() { return __sync_fetch_and_add(&value, 1); }
81 
82  //! Performs an atomic decrement by 1, returning the old value
83  T dec_ret_last() { return __sync_fetch_and_sub(&value, 1); }
84 
85  //! Performs an atomic increment by 1, returning the old value
86  T operator++(int) { return inc_ret_last(); }
87 
88  //! Performs an atomic decrement by 1, returning the old value
89  T operator--(int) { return dec_ret_last(); }
90 
91  //! Performs an atomic increment by 'val', returning the old value
92  T inc_ret_last(const T val) { return __sync_fetch_and_add(&value, val); }
93 
94  //! Performs an atomic decrement by 'val', returning the new value
95  T dec_ret_last(const T val) { return __sync_fetch_and_sub(&value, val); }
96 
97  //! Performs an atomic exchange with 'val', returning the previous value
98  T exchange(const T val) { return __sync_lock_test_and_set(&value, val); }
99  };
100 
101  // specialization for floats and doubles
102  template<typename T>
103  class atomic_impl <T, false>: public IS_POD_TYPE {
104  public:
105  //! The current value of the atomic number
106  volatile T value;
107 
108  //! Creates an atomic number with value "value"
109  atomic_impl(const T& value = T()) : value(value) { }
110 
111  //! Performs an atomic increment by 1, returning the new value
112  T inc() { return inc(1); }
113 
114  //! Performs an atomic decrement by 1, returning the new value
115  T dec() { return dec(1); }
116 
117  //! Lvalue implicit cast
118  operator T() const { return value; }
119 
120  //! Performs an atomic increment by 1, returning the new value
121  T operator++() { return inc(); }
122 
123  //! Performs an atomic decrement by 1, returning the new value
124  T operator--() { return dec(); }
125 
126  //! Performs an atomic increment by 'val', returning the new value
127  T inc(const T val) {
128  T prev_value;
129  T new_value;
130  do {
131  prev_value = value;
132  new_value = prev_value + val;
133  } while(!atomic_compare_and_swap(value, prev_value, new_value));
134  return new_value;
135  }
136 
137  //! Performs an atomic decrement by 'val', returning the new value
138  T dec(const T val) {
139  T prev_value;
140  T new_value;
141  do {
142  prev_value = value;
143  new_value = prev_value - val;
144  } while(!atomic_compare_and_swap(value, prev_value, new_value));
145  return new_value;
146  }
147 
148  //! Performs an atomic increment by 'val', returning the new value
149  T operator+=(const T val) { return inc(val); }
150 
151  //! Performs an atomic decrement by 'val', returning the new value
152  T operator-=(const T val) { return dec(val); }
153 
154  //! Performs an atomic increment by 1, returning the old value
155  T inc_ret_last() { return inc_ret_last(1); }
156 
157  //! Performs an atomic decrement by 1, returning the old value
158  T dec_ret_last() { return dec_ret_last(1); }
159 
160  //! Performs an atomic increment by 1, returning the old value
161  T operator++(int) { return inc_ret_last(); }
162 
163  //! Performs an atomic decrement by 1, returning the old value
164  T operator--(int) { return dec_ret_last(); }
165 
166  //! Performs an atomic increment by 'val', returning the old value
167  T inc_ret_last(const T val) {
168  T prev_value;
169  T new_value;
170  do {
171  prev_value = value;
172  new_value = prev_value + val;
173  } while(!atomic_compare_and_swap(value, prev_value, new_value));
174  return prev_value;
175  }
176 
177  //! Performs an atomic decrement by 'val', returning the new value
178  T dec_ret_last(const T val) {
179  T prev_value;
180  T new_value;
181  do {
182  prev_value = value;
183  new_value = prev_value - val;
184  } while(!atomic_compare_and_swap(value, prev_value, new_value));
185  return prev_value;
186  }
187 
188  //! Performs an atomic exchange with 'val', returning the previous value
189  T exchange(const T val) { return __sync_lock_test_and_set(&value, val); }
190  };
191 } // namespace graphlab_impl
192 
193 template <typename T>
194 class atomic: public graphlab_impl::atomic_impl<T, boost::is_integral<T>::value> {
195  public:
196  //! Creates an atomic number with value "value"
197  atomic(const T& value = T()):
198  graphlab_impl::atomic_impl<T, boost::is_integral<T>::value>(value) { }
199 
200 };
201 
202 } // namespace graphlab
203 #endif
204