/* Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of LibCat nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Modified work: Copyright (c) 2017, SLikeSoft UG (haftungsbeschränkt) * * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style * license found in the license.txt file in the root directory of this source tree. * Alternatively you are permitted to license the modifications under the Modified BSD License. */ #ifndef CAT_ATOMIC_HPP #define CAT_ATOMIC_HPP #include #if defined(CAT_OS_WINDOWS) # include #endif namespace cat { namespace Atomic { // Compare-and-Swap 2x word size (CAS2) // On 32-bit architectures, the arguments point to 64-bit values, and must be aligned to 8 byte boundary // On 64-bit architectures, the arguments point to 128-bit values, and must be aligned to 16 byte boundary // Returns true if the old value was equal to the expected value CAT_INLINE bool CAS2(volatile void *x, const void *expected_old_value, const void *new_value); // Will define CAT_NO_ATOMIC_CAS2 if the platform/compiler does not support atomic CAS2 // Add y to x, returning the previous state of x CAT_INLINE u32 Add(volatile u32 *x, s32 y); // Will define CAT_NO_ATOMIC_ADD if the platform/compiler does not support atomic add // Set x to new value, returning the previous state of x CAT_INLINE u32 Set(volatile u32 *x, u32 new_value); // Will define CAT_NO_ATOMIC_SET if the platform/compiler does not support atomic set // Bit Test and Set (BTS) // Returns true if the bit was 1 and is still 1, otherwise false CAT_INLINE bool BTS(volatile u32 *x, int bit); // Will define CAT_NO_ATOMIC_BTS if the platform/compiler does not support atomic bts // Bit Test and Reset (BTR) // Returns true if the bit was 1 and is now 0, otherwise false CAT_INLINE bool BTR(volatile u32 *x, int bit); // Will define CAT_NO_ATOMIC_BTR if the platform/compiler does not support atomic btr } // namespace Atomic //// Compare-and-Swap #if defined(CAT_WORD_64) bool Atomic::CAS2(volatile void *x, const void *expected_old_value, const void *new_value) { CAT_FENCE_COMPILER #if defined(CAT_COMPILER_MSVC) && (_MSC_VER > 1400) // MSVC 2008+ __int64 ComparandResult[2] = { ((u64*)expected_old_value)[0], ((u64*)expected_old_value)[1] }; // Requires MSVC 2008 or newer bool success = 1 == _InterlockedCompareExchange128((s64*)x, ((u64*)new_value)[1], ((u64*)new_value)[0], ComparandResult); CAT_FENCE_COMPILER return success; #elif defined(CAT_ASM_ATT) && defined(CAT_ISA_X86) u128 *target = (u128*)x; u64 *replace = (u64*)new_value; u128 *expected = (u128*)expected_old_value; bool retval; CAT_ASM_BEGIN "lock; CMPXCHG16B %0\n\t" "sete %%al" : "=m" (*target), "=a" (retval) : "m" (*target), "b" (replace[0]), "c" (replace[1]), "A" (*expected) : "memory", "cc" CAT_ASM_END CAT_FENCE_COMPILER return retval; #else #define CAT_NO_ATOMIC_CAS2 /* Platform/compiler does not support CAS2 */ (void) x; // avoid unused parameter warning (void) expected_old_value; (void) new_value; return true; #endif } #else // 32-bit version: bool Atomic::CAS2(volatile void *x, const void *expected_old_value, const void *new_value) { CAT_FENCE_COMPILER #if defined(CAT_COMPILER_MSVC) s64 old_value = ((s64*)expected_old_value)[0]; bool success = (old_value == _InterlockedCompareExchange64((s64*)x, ((s64*)new_value)[0], old_value)); CAT_FENCE_COMPILER return success; #elif defined(CAT_ASM_INTEL) && defined(CAT_ISA_X86) CAT_ASM_BEGIN push ebx mov eax, new_value push esi mov ebx, dword ptr[eax] mov ecx, dword ptr[eax+4] mov edx, expected_old_value mov esi, x mov eax, dword ptr[edx] mov edx, dword ptr[edx+4] lock CMPXCHG8B qword ptr[esi] pop ebx mov eax, 0 pop esi setz al CAT_ASM_END CAT_FENCE_COMPILER #elif defined(CAT_ASM_ATT) && defined(CAT_ISA_X86) u64 *target = (u64*)x; u32 *replace = (u32*)new_value; u64 *expected = (u64*)expected_old_value; bool retval; CAT_ASM_BEGIN "lock; CMPXCHG8B %0\n\t" "sete %%al" : "=m" (*target), "=a" (retval) : "m" (*target), "b" (replace[0]), "c" (replace[1]), "A" (*expected) : "memory", "cc" CAT_ASM_END CAT_FENCE_COMPILER return retval; #else #define CAT_NO_ATOMIC_CAS2 /* Platform/compiler does not support atomic CAS2 */ (void) x; // avoid unused parameter warning (void) expected_old_value; (void) new_value; return true; #endif } #endif // defined(CAT_WORD_64) //// Add y to x, returning the previous state of x u32 Atomic::Add(volatile u32 *x, s32 y) { CAT_FENCE_COMPILER #if defined(CAT_COMPILER_MSVC) && defined(CAT_WORD_64) u32 result = InterlockedAdd((volatile LONG*)x, y) - y; CAT_FENCE_COMPILER return result; #elif defined(CAT_ASM_INTEL) && defined(CAT_WORD_32) && defined(CAT_ISA_X86) CAT_ASM_BEGIN mov edx,x mov eax,y lock XADD [edx],eax CAT_ASM_END CAT_FENCE_COMPILER #elif defined(CAT_ASM_ATT) && defined(CAT_ISA_X86) u32 retval; CAT_ASM_BEGIN "lock; XADDl %%eax, %0\n\t" : "=m" (*x), "=a" (retval) : "m" (*x), "a" (y) : "memory", "cc" CAT_ASM_END CAT_FENCE_COMPILER return retval; #else #define CAT_NO_ATOMIC_ADD /* Platform/compiler does not support atomic add */ u32 old_x = *x; *x = old_x + y; CAT_FENCE_COMPILER return old_x; #endif } //// Set x to new value, returning the previous state of x u32 Atomic::Set(volatile u32 *x, u32 new_value) { CAT_FENCE_COMPILER #if defined(CAT_COMPILER_MSVC) #if (_MSC_VER <= 1400) // MSVC 2005 u32 result = _InterlockedExchange((long*)x, new_value); #else // MSVC 2008+ u32 result = _InterlockedExchange((volatile LONG*)x, new_value); #endif CAT_FENCE_COMPILER return result; #elif defined(CAT_ASM_INTEL) && defined(CAT_WORD_32) && defined(CAT_ISA_X86) CAT_ASM_BEGIN mov edx,x mov eax,new_value lock XCHG [edx],eax CAT_ASM_END CAT_FENCE_COMPILER #elif defined(CAT_ASM_ATT) && defined(CAT_ISA_X86) u32 retval; CAT_ASM_BEGIN "lock; XCHGl %%eax, %0\n\t" : "=m" (*x), "=a" (retval) : "m" (*x), "a" (new_value) : "memory", "cc" CAT_ASM_END CAT_FENCE_COMPILER return retval; #else #define CAT_NO_ATOMIC_SET /* Platform/compiler does not support atomic set */ u32 old_x = *x; *x = new_value; CAT_FENCE_COMPILER return old_x; #endif } //// Bit Test and Set (BTS) bool Atomic::BTS(volatile u32 *x, int bit) { CAT_FENCE_COMPILER #if defined(CAT_COMPILER_MSVC) #if (_MSC_VER <= 1400) // MSVC 2005 bool success = !!_interlockedbittestandset((long*)x, bit); #else // MSVC 2008+ bool success = !!_interlockedbittestandset((volatile LONG*)x, bit); #endif CAT_FENCE_COMPILER return success; #elif defined(CAT_ASM_INTEL) && defined(CAT_WORD_32) && defined(CAT_ISA_X86) CAT_ASM_BEGIN mov edx,x mov ecx,bit lock BTS [edx],ecx mov eax,0 setc al CAT_ASM_END CAT_FENCE_COMPILER #elif defined(CAT_ASM_ATT) && defined(CAT_ISA_X86) bool retval; CAT_ASM_BEGIN "lock; BTSl %2, %0\n\t" "setc %%al" : "=m" (*x), "=a" (retval) : "Ir" (bit) : "memory", "cc" CAT_ASM_END CAT_FENCE_COMPILER return retval; #else #define CAT_NO_ATOMIC_BTS /* Platform/compiler does not support atomic bts */ u32 mask = 1 << bit; u32 old_x = *x; *x = old_x | mask; CAT_FENCE_COMPILER return (old_x & mask) ? true : false; #endif } //// Bit Test and Reset (BTR) bool Atomic::BTR(volatile u32 *x, int bit) { CAT_FENCE_COMPILER #if defined(CAT_COMPILER_MSVC) #if (_MSC_VER <= 1400) // MSVC 2005 bool success = !!_interlockedbittestandreset((long*)x, bit); #else // MSVC 2008+ bool success = !!_interlockedbittestandreset((volatile LONG*)x, bit); #endif CAT_FENCE_COMPILER return success; #elif defined(CAT_ASM_INTEL) && defined(CAT_WORD_32) && defined(CAT_ISA_X86) CAT_ASM_BEGIN mov edx,x mov ecx,bit lock BTR [edx],ecx mov eax,0 setc al CAT_ASM_END CAT_FENCE_COMPILER #elif defined(CAT_ASM_ATT) && defined(CAT_ISA_X86) bool retval; CAT_ASM_BEGIN "lock; BTRl %2, %0\n\t" "setc %%al" : "=m" (*x), "=a" (retval) : "Ir" (bit) : "memory", "cc" CAT_ASM_END CAT_FENCE_COMPILER return retval; #else #define CAT_NO_ATOMIC_BTR /* Platform/compiler does not support atomic btr */ u32 mask = 1 << bit; u32 old_x = *x; *x = old_x & ~mask; CAT_FENCE_COMPILER return (old_x & mask) ? true : false; #endif } } // namespace cat #endif // CAT_ATOMIC_HPP