436 lines
10 KiB
C++
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
|