2 * Akupara/threading/atomic_ops.hpp
5 * Created by Udi Barzilai on 06/06.
6 * Copyright 2006 __MyCompanyName__. All rights reserved.
9 #if !defined(_AKUPARA_THREADING_ATOMIC_OPS_HPP__INCLUDED_)
10 #define _AKUPARA_THREADING_ATOMIC_OPS_HPP__INCLUDED_
12 #include "Akupara/basics.hpp" // for EXPECT macro
13 #include "Akupara/compiletime_functions.hpp" // for TR1 stuff, signed/unsigned stuff
23 // Machine capabilities
24 // The following templates are specialized by the machine-specific headers to indicate
25 // the capabilities of the machine being compiled for. A true 'value' member for a given
26 // byte count means that there is an implementation of the corresponding atomic operation.
27 //-------------------------------------
28 template<unsigned int _byte_count> struct implements_load : public false_type {}; // simple assignment from memory (assumes naturally aligned address)
29 template<unsigned int _byte_count> struct implements_store : public false_type {}; // simple assignment to memory (assumes naturally aligned address)
30 template<unsigned int _byte_count> struct implements_CAS : public false_type {}; // compare_and_store()
31 template<unsigned int _byte_count> struct implements_LL_SC : public false_type {}; // load_linked(), store_conditional()
32 template<unsigned int _byte_count> struct implements_add : public false_type {}; // add(), subtract()
33 template<unsigned int _byte_count> struct implements_fetch_and_add : public false_type {}; // fetch_and_add(), fetch_and_subtract()
34 template<unsigned int _byte_count> struct implements_add_and_fetch : public false_type {}; // add_and_fetch(), subtract_and_fetch()
35 //-------------------------------------
38 //-------------------------------------
39 // functions in this namespace may or may not be implemented, for any integer types, as specified by the machine capabilities templates above
40 template<typename _integer_type> bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store);
42 template<typename _integer_type> _integer_type load_linked(volatile _integer_type * operand_address);
43 template<typename _integer_type> bool store_conditional(volatile _integer_type * operand_address, const _integer_type & value_to_store);
45 template<typename _integer_type> void add(volatile _integer_type * operand_address, const _integer_type & addend);
46 template<typename _integer_type> void subtract(volatile _integer_type * operand_address, const _integer_type & subtrahend);
48 template<typename _integer_type> _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend);
49 template<typename _integer_type> _integer_type fetch_and_subtract(volatile _integer_type * operand_address, const _integer_type & subtrahend);
51 template<typename _integer_type> _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend);
52 template<typename _integer_type> _integer_type subtract_and_fetch(volatile _integer_type * operand_address, const _integer_type & subtrahend);
54 void memory_barrier_read();
55 void memory_barrier_write();
56 void memory_barrier_readwrite();
57 //-------------------------------------
59 } // namespace machine
61 } // namespace threading
62 } // namespace Akupara
64 // Include the machine-specific implementations; these only implement the templates above for some of the _signed_ integer types
65 #if defined(__GNUC__) && defined(__POWERPC__)
66 #include "atomic_ops_gcc_ppc.hpp"
67 #endif // defined(__GNUC__) && defined(__POWERPC__)
69 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
70 #include "atomic_ops_gcc_x86.hpp"
71 #endif // defined(__GNUC__) && defined(__i386__)
73 #if defined(_MSC_VER) && defined(_M_IX86)
74 #include "atomic_ops_msvc_x86.hpp"
75 #endif // defined(_MSC_VER) && defined(_M_IX86)
77 #if defined(_MSC_VER) && defined(_M_X64)
78 #include "atomic_ops_msvc_x86_64.hpp"
79 #endif // defined(_MSC_VER) && defined(_M_X64)
89 // Select the most convenient atomic integer type based on the machine's ability to load/store atomically
90 // The definition below selects that largest atomically accessible integer up to the size of int
91 //----------------------------------------------------------------------------------------
94 template<unsigned int _byte_count>
95 struct largest_atomic_byte_count_upto
97 static const unsigned int value =
98 machine::implements_load<_byte_count>::value && machine::implements_store<_byte_count>::value ?
100 largest_atomic_byte_count_upto<_byte_count/2>::value;
104 struct largest_atomic_byte_count_upto<0> { static const unsigned int value = 0; };
106 const unsigned int k_byte_count_best_atomic = largest_atomic_byte_count_upto<sizeof(int)>::value;
108 typedef signed_integer_with_byte_count< detail::k_byte_count_best_atomic >::type signed_integer_type;
109 typedef unsigned_integer_with_byte_count< detail::k_byte_count_best_atomic >::type unsigned_integer_type;
110 typedef signed_integer_type integer_type;
111 //----------------------------------------------------------------------------------------
113 //----------------------------------------------------------------------------------------
114 // These need to be implemented by all machines
115 using machine::memory_barrier_read;
116 using machine::memory_barrier_write;
117 using machine::memory_barrier_readwrite;
118 //----------------------------------------------------------------------------------------
120 //----------------------------------------------------------------------------------------
121 // These may or may not be implemented, but if they aren't, we can't help much
122 using machine::load_linked;
123 using machine::store_conditional;
124 //----------------------------------------------------------------------------------------
127 //----------------------------------------------------------------------------------------
128 // CAS implementation
132 typename _integer_type,
133 bool _implements_CAS = machine::implements_CAS <sizeof(_integer_type)>::value,
134 bool _implements_LL_SC = machine::implements_LL_SC<sizeof(_integer_type)>::value>
135 struct implementation_CAS
137 static const bool s_exists = false;
139 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
140 // specialization for native CAS support
141 template<typename _integer_type, bool _implements_LL_SC>
142 struct implementation_CAS<_integer_type, true, _implements_LL_SC>
144 static const bool s_exists = true;
145 static inline bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store)
147 return machine::compare_and_store(operand_address, expected_value, value_to_store);
150 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
151 // specialization for cases with no CAS but with LL/SC
152 template<typename _integer_type>
153 struct implementation_CAS<_integer_type, false, true>
155 static const bool s_exists = true;
156 static inline bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store)
158 while (machine::load_linked(operand_address) == expected_value)
159 if (AKUPARA_EXPECT_TRUE(machine::store_conditional(operand_address, value_to_store)))
164 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
165 } // namespace detail
166 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167 template<typename _integer_type>
168 inline bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store)
170 // if your compiler can't find the function to call here then there is no implementation available for your machine
171 return detail::implementation_CAS<_integer_type>::compare_and_store(operand_address, expected_value, value_to_store);
173 //----------------------------------------------------------------------------------------
179 //----------------------------------------------------------------------------------------
184 typename _integer_type,
185 bool _0 = machine::implements_fetch_and_add<sizeof(_integer_type)>::value,
186 bool _1 = machine::implements_add_and_fetch<sizeof(_integer_type)>::value,
187 bool _2 = machine::implements_LL_SC <sizeof(_integer_type)>::value,
188 bool _3 = machine::implements_CAS <sizeof(_integer_type)>::value>
189 struct implementation_FAA
191 static const bool s_exists = false;
193 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
194 // specialization for native support
195 template<typename _integer_type, bool _1, bool _2, bool _3>
196 struct implementation_FAA<_integer_type, true, _1, _2, _3>
198 static const bool s_exists = true;
199 static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend)
201 return machine::fetch_and_add(operand_address, addend);
204 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
205 // specialization using add_and_fetch
206 template<typename _integer_type, bool _2, bool _3>
207 struct implementation_FAA<_integer_type, false, true, _2, _3>
209 static const bool s_exists = true;
210 static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend)
212 return machine::add_and_fetch(operand_address, addend) - addend;
215 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
216 // specialization using LL/SC
217 template<typename _integer_type, bool _3>
218 struct implementation_FAA<_integer_type, false, false, true, _3>
220 static const bool s_exists = true;
221 static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend)
223 _integer_type old_value;
225 old_value = machine::load_linked(operand_address);
226 while (!machine::store_conditional(operand_address, old_value+addend));
230 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231 // specialization using CAS
232 template<typename _integer_type>
233 struct implementation_FAA<_integer_type, false, false, false, true>
235 static const bool s_exists = true;
236 static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend)
238 _integer_type old_value;
240 old_value = *operand_address;
241 while (!machine::compare_and_store(operand_address, old_value, old_value+addend));
245 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246 } // namespace detail
247 template<typename _integer_type>
248 inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend)
250 // if your compiler can't find the function to call here then there is no implementation available for your machine
251 return detail::implementation_FAA<_integer_type>::fetch_and_add(operand_address, addend);
253 //----------------------------------------------------------------------------------------
258 //----------------------------------------------------------------------------------------
263 typename _integer_type,
264 bool _0 = machine::implements_add_and_fetch<sizeof(_integer_type)>::value,
265 bool _1 = machine::implements_fetch_and_add<sizeof(_integer_type)>::value,
266 bool _2 = machine::implements_LL_SC <sizeof(_integer_type)>::value,
267 bool _3 = machine::implements_CAS <sizeof(_integer_type)>::value>
268 struct implementation_AAF
270 static const bool s_exists = false;
272 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273 // specialization for native support
274 template<typename _integer_type, bool _1, bool _2, bool _3>
275 struct implementation_AAF<_integer_type, true, _1, _2, _3>
277 static const bool s_exists = true;
278 static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend)
280 return machine::add_and_fetch(operand_address, addend);
283 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
284 // specialization using add_and_fetch
285 template<typename _integer_type, bool _2, bool _3>
286 struct implementation_AAF<_integer_type, false, true, _2, _3>
288 static const bool s_exists = true;
289 static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend)
291 return machine::fetch_and_add(operand_address, addend) + addend;
294 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
295 // specialization using LL/SC
296 template<typename _integer_type, bool _3>
297 struct implementation_AAF<_integer_type, false, false, true, _3>
299 static const bool s_exists = true;
300 static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend)
302 _integer_type new_value;
304 new_value = machine::load_linked(operand_address)+addend;
305 while (!machine::store_conditional(operand_address, new_value));
309 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
310 // specialization using CAS
311 template<typename _integer_type>
312 struct implementation_AAF<_integer_type, false, false, false, true>
314 static const bool s_exists = true;
315 static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend)
317 _integer_type old_value, new_value;
319 old_value = *operand_address, new_value = old_value + addend;
320 while (!machine::compare_and_store(operand_address, old_value, new_value));
324 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
325 } // namespace detail
326 template<typename _integer_type>
327 inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend)
329 // if your compiler can't find the function to call here then there is no implementation available for your machine
330 return detail::implementation_AAF<_integer_type>::add_and_fetch(operand_address, addend);
332 //----------------------------------------------------------------------------------------
336 //----------------------------------------------------------------------------------------
338 template<typename _integer_type>
339 inline void add(volatile _integer_type * operand_address, const _integer_type & addend)
341 if (machine::implements_add<sizeof(_integer_type)>::value)
342 machine::add(operand_address, addend);
343 else if (machine::implements_fetch_and_add<sizeof(_integer_type)>::value)
344 machine::fetch_and_add(operand_address, addend);
345 else if (machine::implements_add_and_fetch<sizeof(_integer_type)>::value)
346 machine::add_and_fetch(operand_address, addend);
348 fetch_and_add(operand_address, addend); // this will simulate using CAS or LL/SC (or it will fail the compilation if neither is available)
350 //----------------------------------------------------------------------------------------
354 //----------------------------------------------------------------------------------------
355 // TODO: this is where we add implementations for:
356 // - functions not implemented by the machine
357 // - functions that take unsigned types (routed to call the signed versions with appropriate conversions)
358 // For now we add nothing, so developers will need to stick to what their machine can do, and use signed
360 using machine::subtract;
361 using machine::subtract_and_fetch;
362 using machine::fetch_and_subtract;
363 //----------------------------------------------------------------------------------------
367 //---------------------------------------------------------------------
368 template<class _base_type, unsigned int _bytes_per_cache_line=machine::k_bytes_per_cache_line>
369 struct pad_to_cache_line : public _base_type
372 typedef pad_to_cache_line this_type;
373 typedef _base_type base_type;
375 static const unsigned int s_bytes_per_cache_line = _bytes_per_cache_line;
377 int m_padding[(s_bytes_per_cache_line - sizeof(base_type))/sizeof(int)];
379 pad_to_cache_line() {}
380 template<typename _arg_type> pad_to_cache_line(_arg_type arg) : base_type(arg) {}
382 //---------------------------------------------------------------------
384 } // namespace atomic
385 } // namespace threading
386 } // namespace Akupara
388 #endif // _AKUPARA_THREADING_ATOMIC_OPS_HPP__INCLUDED_