1 //////////////////////////////////////////////////////////////////////////////
3 // (C) Copyright Ion Gaztanaga 2006-2011
4 // (C) Copyright Markus Schoepflin 2007
5 // (C) Copyright Bryce Lelbach 2010
7 // Distributed under the Boost Software License, Version 1.0. (See
8 // accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
11 // See http://www.boost.org/libs/interprocess for documentation.
13 //////////////////////////////////////////////////////////////////////////////
15 #ifndef BOOST_INTERPROCESS_DETAIL_ATOMIC_HPP
16 #define BOOST_INTERPROCESS_DETAIL_ATOMIC_HPP
18 #include <boost/interprocess/detail/config_begin.hpp>
19 #include <boost/interprocess/detail/workaround.hpp>
20 #include <boost/cstdint.hpp>
23 namespace interprocess{
26 //! Atomically increment an boost::uint32_t by 1
27 //! "mem": pointer to the object
28 //! Returns the old value pointed to by mem
29 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem);
31 //! Atomically read an boost::uint32_t from memory
32 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem);
34 //! Atomically set an boost::uint32_t in memory
35 //! "mem": pointer to the object
36 //! "param": val value that the object will assume
37 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val);
39 //! Compare an boost::uint32_t's value with "cmp".
40 //! If they are the same swap the value with "with"
41 //! "mem": pointer to the value
42 //! "with": what to swap it with
43 //! "cmp": the value to compare it to
44 //! Returns the old value of *mem
45 inline boost::uint32_t atomic_cas32
46 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp);
48 } //namespace ipcdetail{
49 } //namespace interprocess{
52 #if (defined BOOST_INTERPROCESS_WINDOWS)
54 #include <boost/interprocess/detail/win32_api.hpp>
57 namespace interprocess{
60 //! Atomically decrement an boost::uint32_t by 1
61 //! "mem": pointer to the atomic value
62 //! Returns the old value pointed to by mem
63 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
64 { return winapi::interlocked_decrement(reinterpret_cast<volatile long*>(mem)) + 1; }
66 //! Atomically increment an apr_uint32_t by 1
67 //! "mem": pointer to the object
68 //! Returns the old value pointed to by mem
69 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
70 { return winapi::interlocked_increment(reinterpret_cast<volatile long*>(mem))-1; }
72 //! Atomically read an boost::uint32_t from memory
73 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
76 //! Atomically set an boost::uint32_t in memory
77 //! "mem": pointer to the object
78 //! "param": val value that the object will assume
79 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
80 { winapi::interlocked_exchange(reinterpret_cast<volatile long*>(mem), val); }
82 //! Compare an boost::uint32_t's value with "cmp".
83 //! If they are the same swap the value with "with"
84 //! "mem": pointer to the value
85 //! "with": what to swap it with
86 //! "cmp": the value to compare it to
87 //! Returns the old value of *mem
88 inline boost::uint32_t atomic_cas32
89 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
90 { return winapi::interlocked_compare_exchange(reinterpret_cast<volatile long*>(mem), with, cmp); }
92 } //namespace ipcdetail{
93 } //namespace interprocess{
96 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
99 namespace interprocess {
102 //! Compare an boost::uint32_t's value with "cmp".
103 //! If they are the same swap the value with "with"
104 //! "mem": pointer to the value
105 //! "with" what to swap it with
106 //! "cmp": the value to compare it to
107 //! Returns the old value of *mem
108 inline boost::uint32_t atomic_cas32
109 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
111 boost::uint32_t prev = cmp;
112 // This version by Mans Rullgard of Pathscale
113 __asm__ __volatile__ ( "lock\n\t"
115 : "+m"(*mem), "+a"(prev)
122 //! Atomically add 'val' to an boost::uint32_t
123 //! "mem": pointer to the object
124 //! "val": amount to add
125 //! Returns the old value pointed to by mem
126 inline boost::uint32_t atomic_add32
127 (volatile boost::uint32_t *mem, boost::uint32_t val)
138 "+m"( *mem ), "=r"( r ): // outputs (%0, %1)
139 "1"( val ): // inputs (%2 == %1)
140 "memory", "cc" // clobbers
146 //! Atomically increment an apr_uint32_t by 1
147 //! "mem": pointer to the object
148 //! Returns the old value pointed to by mem
149 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
150 { return atomic_add32(mem, 1); }
152 //! Atomically decrement an boost::uint32_t by 1
153 //! "mem": pointer to the atomic value
154 //! Returns the old value pointed to by mem
155 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
156 { return atomic_add32(mem, (boost::uint32_t)-1); }
158 //! Atomically read an boost::uint32_t from memory
159 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
162 //! Atomically set an boost::uint32_t in memory
163 //! "mem": pointer to the object
164 //! "param": val value that the object will assume
165 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
168 } //namespace ipcdetail{
169 } //namespace interprocess{
172 #elif defined(__GNUC__) && (defined(__PPC__) || defined(__ppc__))
175 namespace interprocess {
178 //! Atomically add 'val' to an boost::uint32_t
179 //! "mem": pointer to the object
180 //! "val": amount to add
181 //! Returns the old value pointed to by mem
182 inline boost::uint32_t atomic_add32(volatile boost::uint32_t *mem, boost::uint32_t val)
184 boost::uint32_t prev, temp;
186 asm volatile ("1:\n\t"
191 : "=&r" (prev), "=&r" (temp)
192 : "b" (mem), "r" (val)
197 //! Compare an boost::uint32_t's value with "cmp".
198 //! If they are the same swap the value with "with"
199 //! "mem": pointer to the value
200 //! "with" what to swap it with
201 //! "cmp": the value to compare it to
202 //! Returns the old value of *mem
203 inline boost::uint32_t atomic_cas32
204 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
206 boost::uint32_t prev;
208 asm volatile ("1:\n\t"
216 : "b" (mem), "r"(cmp), "r" (with)
221 //! Atomically increment an apr_uint32_t by 1
222 //! "mem": pointer to the object
223 //! Returns the old value pointed to by mem
224 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
225 { return atomic_add32(mem, 1); }
227 //! Atomically decrement an boost::uint32_t by 1
228 //! "mem": pointer to the atomic value
229 //! Returns the old value pointed to by mem
230 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
231 { return atomic_add32(mem, boost::uint32_t(-1u)); }
233 //! Atomically read an boost::uint32_t from memory
234 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
237 //! Atomically set an boost::uint32_t in memory
238 //! "mem": pointer to the object
239 //! "param": val value that the object will assume
240 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
243 } //namespace ipcdetail{
244 } //namespace interprocess{
247 #elif (defined(sun) || defined(__sun))
252 namespace interprocess{
255 //! Atomically add 'val' to an boost::uint32_t
256 //! "mem": pointer to the object
257 //! "val": amount to add
258 //! Returns the old value pointed to by mem
259 inline boost::uint32_t atomic_add32(volatile boost::uint32_t *mem, boost::uint32_t val)
260 { return atomic_add_32_nv(reinterpret_cast<volatile ::uint32_t*>(mem), (int32_t)val) - val; }
262 //! Compare an boost::uint32_t's value with "cmp".
263 //! If they are the same swap the value with "with"
264 //! "mem": pointer to the value
265 //! "with" what to swap it with
266 //! "cmp": the value to compare it to
267 //! Returns the old value of *mem
268 inline boost::uint32_t atomic_cas32
269 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
270 { return atomic_cas_32(reinterpret_cast<volatile ::uint32_t*>(mem), cmp, with); }
272 //! Atomically increment an apr_uint32_t by 1
273 //! "mem": pointer to the object
274 //! Returns the old value pointed to by mem
275 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
276 { return atomic_add_32_nv(reinterpret_cast<volatile ::uint32_t*>(mem), 1) - 1; }
278 //! Atomically decrement an boost::uint32_t by 1
279 //! "mem": pointer to the atomic value
280 //! Returns the old value pointed to by mem
281 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
282 { return atomic_add_32_nv(reinterpret_cast<volatile ::uint32_t*>(mem), (boost::uint32_t)-1) + 1; }
284 //! Atomically read an boost::uint32_t from memory
285 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
288 //! Atomically set an boost::uint32_t in memory
289 //! "mem": pointer to the object
290 //! "param": val value that the object will assume
291 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
294 } //namespace ipcdetail{
295 } //namespace interprocess{
298 #elif defined(__osf__) && defined(__DECCXX)
300 #include <machine/builtins.h>
304 namespace interprocess{
307 //! Atomically decrement a uint32_t by 1
308 //! "mem": pointer to the atomic value
309 //! Returns the old value pointed to by mem
310 //! Acquire, memory barrier after decrement.
311 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
312 { boost::uint32_t old_val = __ATOMIC_DECREMENT_LONG(mem); __MB(); return old_val; }
314 //! Atomically increment a uint32_t by 1
315 //! "mem": pointer to the object
316 //! Returns the old value pointed to by mem
317 //! Release, memory barrier before increment.
318 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
319 { __MB(); return __ATOMIC_INCREMENT_LONG(mem); }
321 // Rational for the implementation of the atomic read and write functions.
323 // 1. The Alpha Architecture Handbook requires that access to a byte,
324 // an aligned word, an aligned longword, or an aligned quadword is
325 // atomic. (See 'Alpha Architecture Handbook', version 4, chapter 5.2.2.)
327 // 2. The CXX User's Guide states that volatile quantities are accessed
328 // with single assembler instructions, and that a compilation error
329 // occurs when declaring a quantity as volatile which is not properly
332 //! Atomically read an boost::uint32_t from memory
333 //! Acquire, memory barrier after load.
334 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
335 { boost::uint32_t old_val = *mem; __MB(); return old_val; }
337 //! Atomically set an boost::uint32_t in memory
338 //! "mem": pointer to the object
339 //! "param": val value that the object will assume
340 //! Release, memory barrier before store.
341 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
342 { __MB(); *mem = val; }
344 //! Compare an boost::uint32_t's value with "cmp".
345 //! If they are the same swap the value with "with"
346 //! "mem": pointer to the value
347 //! "with" what to swap it with
348 //! "cmp": the value to compare it to
349 //! Returns the old value of *mem
350 //! Memory barrier between load and store.
351 inline boost::uint32_t atomic_cas32(
352 volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
356 // Branch prediction prefers backward branches, and the Alpha Architecture
357 // Handbook explicitely states that the loop should not be implemented like
358 // it is below. (See chapter 4.2.5.) Therefore the code should probably look
362 // "10: ldl_l %v0,(%a0) ;"
363 // " cmpeq %v0,%a2,%t0 ;"
367 // " stl_c %t0,(%a0) ;"
373 // But as the compiler always transforms this into the form where a backward
374 // branch is taken on failure, we can as well implement it in the straight
375 // forward form, as this is what it will end up in anyway.
378 "10: ldl_l %v0,(%a0) ;" // load prev value from mem and lock mem
379 " cmpeq %v0,%a2,%t0 ;" // compare with given value
380 " beq %t0,20f ;" // if not equal, we're done
381 " mb ;" // memory barrier
382 " mov %a1,%t0 ;" // load new value into scratch register
383 " stl_c %t0,(%a0) ;" // store new value to locked mem (overwriting scratch)
384 " beq %t0,10b ;" // store failed because lock has been stolen, retry
389 } //namespace ipcdetail{
390 } //namespace interprocess{
393 #elif defined(__IBMCPP__) && (__IBMCPP__ >= 800) && defined(_AIX)
395 #include <builtins.h>
398 namespace interprocess {
401 //first define boost::uint32_t versions of __lwarx and __stwcx to avoid poluting
402 //all the functions with casts
404 //! From XLC documenation :
405 //! This function can be used with a subsequent stwcxu call to implement a
406 //! read-modify-write on a specified memory location. The two functions work
407 //! together to ensure that if the store is successfully performed, no other
408 //! processor or mechanism can modify the target doubleword between the time
409 //! lwarxu function is executed and the time the stwcxu functio ncompletes.
410 //! "mem" : pointer to the object
411 //! Returns the value at pointed to by mem
412 inline boost::uint32_t lwarxu(volatile boost::uint32_t *mem)
414 return static_cast<boost::uint32_t>(__lwarx(reinterpret_cast<volatile int*>(mem)));
417 //! "mem" : pointer to the object
418 //! "val" : the value to store
419 //! Returns true if the update of mem is successful and false if it is
421 inline bool stwcxu(volatile boost::uint32_t* mem, boost::uint32_t val)
423 return (__stwcx(reinterpret_cast<volatile int*>(mem), static_cast<int>(val)) != 0);
426 //! "mem": pointer to the object
427 //! "val": amount to add
428 //! Returns the old value pointed to by mem
429 inline boost::uint32_t atomic_add32
430 (volatile boost::uint32_t *mem, boost::uint32_t val)
432 boost::uint32_t oldValue;
435 oldValue = lwarxu(mem);
436 }while (!stwcxu(mem, oldValue+val));
440 //! Atomically increment an apr_uint32_t by 1
441 //! "mem": pointer to the object
442 //! Returns the old value pointed to by mem
443 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
444 { return atomic_add32(mem, 1); }
446 //! Atomically decrement an boost::uint32_t by 1
447 //! "mem": pointer to the atomic value
448 //! Returns the old value pointed to by mem
449 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
450 { return atomic_add32(mem, (boost::uint32_t)-1); }
452 //! Atomically read an boost::uint32_t from memory
453 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
456 //! Compare an boost::uint32_t's value with "cmp".
457 //! If they are the same swap the value with "with"
458 //! "mem": pointer to the value
459 //! "with" what to swap it with
460 //! "cmp": the value to compare it to
461 //! Returns the old value of *mem
462 inline boost::uint32_t atomic_cas32
463 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
465 boost::uint32_t oldValue;
466 boost::uint32_t valueToStore;
469 oldValue = lwarxu(mem);
470 } while (!stwcxu(mem, (oldValue == with) ? cmp : oldValue));
475 //! Atomically set an boost::uint32_t in memory
476 //! "mem": pointer to the object
477 //! "param": val value that the object will assume
478 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
481 } //namespace ipcdetail
482 } //namespace interprocess
485 #elif defined(__GNUC__) && ( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 )
488 namespace interprocess {
491 //! Atomically add 'val' to an boost::uint32_t
492 //! "mem": pointer to the object
493 //! "val": amount to add
494 //! Returns the old value pointed to by mem
495 inline boost::uint32_t atomic_add32
496 (volatile boost::uint32_t *mem, boost::uint32_t val)
497 { return __sync_fetch_and_add(const_cast<boost::uint32_t *>(mem), val); }
499 //! Atomically increment an apr_uint32_t by 1
500 //! "mem": pointer to the object
501 //! Returns the old value pointed to by mem
502 inline boost::uint32_t atomic_inc32(volatile boost::uint32_t *mem)
503 { return atomic_add32(mem, 1); }
505 //! Atomically decrement an boost::uint32_t by 1
506 //! "mem": pointer to the atomic value
507 //! Returns the old value pointed to by mem
508 inline boost::uint32_t atomic_dec32(volatile boost::uint32_t *mem)
509 { return atomic_add32(mem, (boost::uint32_t)-1); }
511 //! Atomically read an boost::uint32_t from memory
512 inline boost::uint32_t atomic_read32(volatile boost::uint32_t *mem)
515 //! Compare an boost::uint32_t's value with "cmp".
516 //! If they are the same swap the value with "with"
517 //! "mem": pointer to the value
518 //! "with" what to swap it with
519 //! "cmp": the value to compare it to
520 //! Returns the old value of *mem
521 inline boost::uint32_t atomic_cas32
522 (volatile boost::uint32_t *mem, boost::uint32_t with, boost::uint32_t cmp)
523 { return __sync_val_compare_and_swap(const_cast<boost::uint32_t *>(mem), cmp, with); }
525 //! Atomically set an boost::uint32_t in memory
526 //! "mem": pointer to the object
527 //! "param": val value that the object will assume
528 inline void atomic_write32(volatile boost::uint32_t *mem, boost::uint32_t val)
531 } //namespace ipcdetail{
532 } //namespace interprocess{
537 #error No atomic operations implemented for this platform, sorry!
542 namespace interprocess{
545 inline bool atomic_add_unless32
546 (volatile boost::uint32_t *mem, boost::uint32_t value, boost::uint32_t unless_this)
548 boost::uint32_t old, c(atomic_read32(mem));
549 while(c != unless_this && (old = atomic_cas32(mem, c + value, c)) != c){
552 return c != unless_this;
555 } //namespace ipcdetail
556 } //namespace interprocess
560 #include <boost/interprocess/detail/config_end.hpp>
562 #endif //BOOST_INTERPROCESS_DETAIL_ATOMIC_HPP