Commit 309209c5 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-26467: Use LOCK BTS also with the Microsoft compiler

Microsoft does not define inline assembler syntax for AMD64,
but it defines the intrinsic function _interlockedbittestandset()
that we can use.

ssux_lock_impl<bool>::rd_wait(): Remove a call to yield,
because writer.wr_lock() will take care of context switches
between loop iterations.

This addresses suggestions by Vladislav Vaintroub.
parent 27738bd7
...@@ -295,6 +295,35 @@ template void ssux_lock_impl<false>::wake(); ...@@ -295,6 +295,35 @@ template void ssux_lock_impl<false>::wake();
template void srw_mutex_impl<true>::wake(); template void srw_mutex_impl<true>::wake();
template void ssux_lock_impl<true>::wake(); template void ssux_lock_impl<true>::wake();
/*
Unfortunately, compilers targeting IA-32 or AMD64 currently cannot
translate the following single-bit operations into Intel 80386 instructions:
m.fetch_or(1<<b) & 1<<b LOCK BTS b, m
m.fetch_and(~(1<<b)) & 1<<b LOCK BTR b, m
m.fetch_xor(1<<b) & 1<<b LOCK BTC b, m
Hence, we will manually translate fetch_or() using GCC-style inline
assembler code or a Microsoft intrinsic function.
*/
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
# define IF_FETCH_OR_GOTO(mem, bit, label) \
__asm__ goto("lock btsl $" #bit ", %0\n\t" \
"jc %l1" : : "m" (mem) : "cc", "memory" : label);
# define IF_NOT_FETCH_OR_GOTO(mem, bit, label) \
__asm__ goto("lock btsl $" #bit ", %0\n\t" \
"jnc %l1" : : "m" (mem) : "cc", "memory" : label);
#elif defined _MSC_VER && (defined _M_IX86 || defined _M_IX64)
# define IF_FETCH_OR_GOTO(mem, bit, label) \
if (_interlockedbittestandset(reinterpret_cast<volatile long*>(&mem), bit)) \
goto label;
# define IF_NOT_FETCH_OR_GOTO(mem, bit, label) \
if (!_interlockedbittestandset(reinterpret_cast<volatile long*>(&mem), bit))\
goto label;
#endif
template<> template<>
void srw_mutex_impl<true>::wait_and_lock() void srw_mutex_impl<true>::wait_and_lock()
{ {
...@@ -307,24 +336,15 @@ void srw_mutex_impl<true>::wait_and_lock() ...@@ -307,24 +336,15 @@ void srw_mutex_impl<true>::wait_and_lock()
DBUG_ASSERT(~HOLDER & lk); DBUG_ASSERT(~HOLDER & lk);
if (lk & HOLDER) if (lk & HOLDER)
lk= lock.load(std::memory_order_relaxed); lk= lock.load(std::memory_order_relaxed);
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
#elif defined _M_IX86||defined _M_X64||defined __i386__||defined __x86_64__
else if (lock.compare_exchange_weak(lk, lk | HOLDER,
std::memory_order_acquire,
std::memory_order_relaxed))
return;
#else
else if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) &
HOLDER))
goto acquired;
#endif
else else
{ {
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) #ifdef IF_NOT_FETCH_OR_GOTO
static_assert(HOLDER == (1U << 31), "compatibility"); static_assert(HOLDER == (1U << 31), "compatibility");
__asm__ goto("lock btsl $31, %0\n\t" IF_NOT_FETCH_OR_GOTO(*this, 31, acquired);
"jnc %l1" : : "m" (*this) : "cc", "memory" : acquired);
lk|= HOLDER; lk|= HOLDER;
#else
if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & HOLDER))
goto acquired;
#endif #endif
srw_pause(delay); srw_pause(delay);
} }
...@@ -338,36 +358,25 @@ void srw_mutex_impl<true>::wait_and_lock() ...@@ -338,36 +358,25 @@ void srw_mutex_impl<true>::wait_and_lock()
if (lk & HOLDER) if (lk & HOLDER)
{ {
wait(lk); wait(lk);
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) #ifdef IF_FETCH_OR_GOTO
reload: reload:
#endif #endif
lk= lock.load(std::memory_order_relaxed); lk= lock.load(std::memory_order_relaxed);
} }
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
else else
{ {
#ifdef IF_FETCH_OR_GOTO
static_assert(HOLDER == (1U << 31), "compatibility"); static_assert(HOLDER == (1U << 31), "compatibility");
__asm__ goto("lock btsl $31, %0\n\t" IF_FETCH_OR_GOTO(*this, 31, reload);
"jc %l1" : : "m" (*this) : "cc", "memory" : reload);
acquired:
std::atomic_thread_fence(std::memory_order_acquire);
return;
}
#elif defined _M_IX86||defined _M_X64||defined __i386__||defined __x86_64__
else if (lock.compare_exchange_weak(lk, lk | HOLDER,
std::memory_order_acquire,
std::memory_order_relaxed))
return;
#else #else
else if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & if ((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & HOLDER)
HOLDER)) continue;
{
acquired:
DBUG_ASSERT(lk); DBUG_ASSERT(lk);
#endif
acquired:
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
return; return;
} }
#endif
} }
} }
...@@ -380,34 +389,24 @@ void srw_mutex_impl<false>::wait_and_lock() ...@@ -380,34 +389,24 @@ void srw_mutex_impl<false>::wait_and_lock()
if (lk & HOLDER) if (lk & HOLDER)
{ {
wait(lk); wait(lk);
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__) #ifdef IF_FETCH_OR_GOTO
reload: reload:
#endif #endif
lk= lock.load(std::memory_order_relaxed); lk= lock.load(std::memory_order_relaxed);
} }
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
else else
{ {
#ifdef IF_FETCH_OR_GOTO
static_assert(HOLDER == (1U << 31), "compatibility"); static_assert(HOLDER == (1U << 31), "compatibility");
__asm__ goto("lock btsl $31, %0\n\t" IF_FETCH_OR_GOTO(*this, 31, reload);
"jc %l1" : : "m" (*this) : "cc", "memory" : reload);
std::atomic_thread_fence(std::memory_order_acquire);
return;
}
#elif defined _M_IX86||defined _M_X64||defined __i386__||defined __x86_64__
else if (lock.compare_exchange_weak(lk, lk | HOLDER,
std::memory_order_acquire,
std::memory_order_relaxed))
return;
#else #else
else if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & if ((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & HOLDER)
HOLDER)) continue;
{
DBUG_ASSERT(lk); DBUG_ASSERT(lk);
#endif
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
return; return;
} }
#endif
} }
} }
...@@ -456,7 +455,6 @@ void ssux_lock_impl<spinloop>::rd_wait() ...@@ -456,7 +455,6 @@ void ssux_lock_impl<spinloop>::rd_wait()
writer.wr_unlock(); writer.wr_unlock();
if (acquired) if (acquired)
break; break;
std::this_thread::yield();
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment