| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // An SbAtomic* implementation using the older "__sync_*" GCC intrinsics. |
| // These may be useful if, for example, you are using a GCC version prior to |
| // 4.7, or your toolchain has a broken implementation of the newer |
| // "__atomic_*" intrinsics. |
| // |
| // These definitions were mostly extracted from Chromium's |
| // base/atomicops_internals_gcc.h. |
| |
| #ifndef STARBOARD_SHARED_GCC_ATOMIC_GCC_SYNC_PUBLIC_H_ |
| #define STARBOARD_SHARED_GCC_ATOMIC_GCC_SYNC_PUBLIC_H_ |
| |
| #include "starboard/atomic.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicNoBarrier_CompareAndSwap(volatile SbAtomic32* ptr, |
| SbAtomic32 old_value, |
| SbAtomic32 new_value) { |
| SbAtomic32 prev_value; |
| do { |
| if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) |
| return old_value; |
| prev_value = *ptr; |
| } while (prev_value == old_value); |
| return prev_value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicNoBarrier_Exchange(volatile SbAtomic32* ptr, SbAtomic32 new_value) { |
| SbAtomic32 old_value; |
| do { |
| old_value = *ptr; |
| } while (!__sync_bool_compare_and_swap(ptr, old_value, new_value)); |
| return old_value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicNoBarrier_Increment(volatile SbAtomic32* ptr, SbAtomic32 increment) { |
| return SbAtomicBarrier_Increment(ptr, increment); |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicBarrier_Increment(volatile SbAtomic32* ptr, |
| SbAtomic32 increment) { |
| for (;;) { |
| // Atomic exchange the old value with an incremented one. |
| SbAtomic32 old_value = *ptr; |
| SbAtomic32 new_value = old_value + increment; |
| if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) { |
| // The exchange took place as expected. |
| return new_value; |
| } |
| // Otherwise, *ptr changed mid-loop and we need to retry. |
| } |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicAcquire_CompareAndSwap(volatile SbAtomic32* ptr, |
| SbAtomic32 old_value, |
| SbAtomic32 new_value) { |
| // Since NoBarrier_CompareAndSwap uses __sync_bool_compare_and_swap, which |
| // is a full memory barrier, none is needed here or below in Release. |
| return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value); |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicRelease_CompareAndSwap(volatile SbAtomic32* ptr, |
| SbAtomic32 old_value, |
| SbAtomic32 new_value) { |
| return SbAtomicNoBarrier_CompareAndSwap(ptr, old_value, new_value); |
| } |
| |
| static SB_C_FORCE_INLINE void SbAtomicMemoryBarrier() { |
| __sync_synchronize(); |
| } |
| |
| static SB_C_FORCE_INLINE void SbAtomicNoBarrier_Store(volatile SbAtomic32* ptr, |
| SbAtomic32 value) { |
| *ptr = value; |
| } |
| |
| static SB_C_FORCE_INLINE void SbAtomicAcquire_Store(volatile SbAtomic32* ptr, |
| SbAtomic32 value) { |
| *ptr = value; |
| SbAtomicMemoryBarrier(); |
| } |
| |
| static SB_C_FORCE_INLINE void SbAtomicRelease_Store(volatile SbAtomic32* ptr, |
| SbAtomic32 value) { |
| SbAtomicMemoryBarrier(); |
| *ptr = value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicNoBarrier_Load(volatile const SbAtomic32* ptr) { |
| return *ptr; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicAcquire_Load(volatile const SbAtomic32* ptr) { |
| SbAtomic32 value = *ptr; |
| SbAtomicMemoryBarrier(); |
| return value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic32 |
| SbAtomicRelease_Load(volatile const SbAtomic32* ptr) { |
| SbAtomicMemoryBarrier(); |
| return *ptr; |
| } |
| |
| // 64-bit atomic operations (only available on 64-bit processors). |
| #if SB_HAS(64_BIT_ATOMICS) |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicNoBarrier_CompareAndSwap64(volatile SbAtomic64* ptr, |
| SbAtomic64 old_value, |
| SbAtomic64 new_value) { |
| SbAtomic64 prev_value; |
| do { |
| if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) |
| return old_value; |
| prev_value = *ptr; |
| } while (prev_value == old_value); |
| return prev_value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicNoBarrier_Exchange64(volatile SbAtomic64* ptr, SbAtomic64 new_value) { |
| SbAtomic64 old_value; |
| do { |
| old_value = *ptr; |
| } while (!__sync_bool_compare_and_swap(ptr, old_value, new_value)); |
| return old_value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicNoBarrier_Increment64(volatile SbAtomic64* ptr, SbAtomic64 increment) { |
| return SbAtomicBarrier_Increment64(ptr, increment); |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicBarrier_Increment64(volatile SbAtomic64* ptr, SbAtomic64 increment) { |
| for (;;) { |
| // Atomic exchange the old value with an incremented one. |
| SbAtomic64 old_value = *ptr; |
| SbAtomic64 new_value = old_value + increment; |
| if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) { |
| // The exchange took place as expected. |
| return new_value; |
| } |
| // Otherwise, *ptr changed mid-loop and we need to retry. |
| } |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicAcquire_CompareAndSwap64(volatile SbAtomic64* ptr, |
| SbAtomic64 old_value, |
| SbAtomic64 new_value) { |
| return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value); |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicRelease_CompareAndSwap64(volatile SbAtomic64* ptr, |
| SbAtomic64 old_value, |
| SbAtomic64 new_value) { |
| return SbAtomicNoBarrier_CompareAndSwap64(ptr, old_value, new_value); |
| } |
| |
| static SB_C_FORCE_INLINE void |
| SbAtomicNoBarrier_Store64(volatile SbAtomic64* ptr, |
| SbAtomic64 value) { |
| *ptr = value; |
| } |
| |
| static SB_C_FORCE_INLINE void SbAtomicAcquire_Store64(volatile SbAtomic64* ptr, |
| SbAtomic64 value) { |
| *ptr = value; |
| SbAtomicMemoryBarrier(); |
| } |
| |
| static SB_C_FORCE_INLINE void SbAtomicRelease_Store64(volatile SbAtomic64* ptr, |
| SbAtomic64 value) { |
| SbAtomicMemoryBarrier(); |
| *ptr = value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicNoBarrier_Load64(volatile const SbAtomic64* ptr) { |
| return *ptr; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicAcquire_Load64(volatile const SbAtomic64* ptr) { |
| SbAtomic64 value = *ptr; |
| SbAtomicMemoryBarrier(); |
| return value; |
| } |
| |
| static SB_C_FORCE_INLINE SbAtomic64 |
| SbAtomicRelease_Load64(volatile const SbAtomic64* ptr) { |
| SbAtomicMemoryBarrier(); |
| return *ptr; |
| } |
| #endif // SB_HAS(64_BIT_ATOMICS) |
| |
| #ifdef __cplusplus |
| } // extern "C" |
| #endif |
| |
| #endif // STARBOARD_SHARED_GCC_ATOMIC_GCC_SYNC_PUBLIC_H_ |