diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-09-29 10:15:07 +0300 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-09-29 10:15:07 +0300 |
commit | 309209c51c211d0733a6c7a67fb70a523a1706f7 (patch) | |
tree | fd473cb070c019f43628b1ca78e02cb0d5794a61 | |
parent | 27738bd774a5435026602ef94a3abc492f1f72e6 (diff) | |
download | mariadb-git-bb-10.6-MDEV-26467.tar.gz |
MDEV-26467: Use LOCK BTS also with the Microsoft compilerbb-10.6-MDEV-26467
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.
-rw-r--r-- | storage/innobase/sync/srw_lock.cc | 96 |
1 files changed, 47 insertions, 49 deletions
diff --git a/storage/innobase/sync/srw_lock.cc b/storage/innobase/sync/srw_lock.cc index f38847f852b..7d26dd2b590 100644 --- a/storage/innobase/sync/srw_lock.cc +++ b/storage/innobase/sync/srw_lock.cc @@ -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(); } } |