Files
2025-11-24 14:19:51 +05:30

436 lines
10 KiB
C++

/*
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 <cat/Platform.hpp>
#if defined(CAT_OS_WINDOWS)
# include <cat/port/WindowsInclude.hpp>
#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