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();
template void srw_mutex_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<>
void srw_mutex_impl<true>::wait_and_lock()
{
......@@ -307,24 +336,15 @@ void srw_mutex_impl<true>::wait_and_lock()
DBUG_ASSERT(~HOLDER & lk);
if (lk & HOLDER)
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
{
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
#ifdef IF_NOT_FETCH_OR_GOTO
static_assert(HOLDER == (1U << 31), "compatibility");
__asm__ goto("lock btsl $31, %0\n\t"
"jnc %l1" : : "m" (*this) : "cc", "memory" : acquired);
IF_NOT_FETCH_OR_GOTO(*this, 31, acquired);
lk|= HOLDER;
#else
if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & HOLDER))
goto acquired;
#endif
srw_pause(delay);
}
......@@ -338,36 +358,25 @@ void srw_mutex_impl<true>::wait_and_lock()
if (lk & HOLDER)
{
wait(lk);
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
#ifdef IF_FETCH_OR_GOTO
reload:
#endif
lk= lock.load(std::memory_order_relaxed);
}
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
else
{
#ifdef IF_FETCH_OR_GOTO
static_assert(HOLDER == (1U << 31), "compatibility");
__asm__ goto("lock btsl $31, %0\n\t"
"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;
IF_FETCH_OR_GOTO(*this, 31, reload);
#else
else if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) &
HOLDER))
{
acquired:
if ((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & HOLDER)
continue;
DBUG_ASSERT(lk);
#endif
acquired:
std::atomic_thread_fence(std::memory_order_acquire);
return;
}
#endif
}
}
......@@ -380,34 +389,24 @@ void srw_mutex_impl<false>::wait_and_lock()
if (lk & HOLDER)
{
wait(lk);
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
#ifdef IF_FETCH_OR_GOTO
reload:
#endif
lk= lock.load(std::memory_order_relaxed);
}
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
else
{
#ifdef IF_FETCH_OR_GOTO
static_assert(HOLDER == (1U << 31), "compatibility");
__asm__ goto("lock btsl $31, %0\n\t"
"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;
IF_FETCH_OR_GOTO(*this, 31, reload);
#else
else if (!((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) &
HOLDER))
{
if ((lk= lock.fetch_or(HOLDER, std::memory_order_relaxed)) & HOLDER)
continue;
DBUG_ASSERT(lk);
#endif
std::atomic_thread_fence(std::memory_order_acquire);
return;
}
#endif
}
}
......@@ -456,7 +455,6 @@ void ssux_lock_impl<spinloop>::rd_wait()
writer.wr_unlock();
if (acquired)
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