Browse Source

MDEV-24884 Hang in ssux_lock_low::write_lock()

ssux_lock_low::write_lock(): Before invoking writer_wait(), keep
attempting write_lock_wait_try() as long as no conflict exists.

rw_lock::upgrade_trylock(): Relax a bogus assertion and correct
the acquisition operation. Another thread may be executing in
ssux_lock_low::write_lock() on the same latch. Because we are the
only thread that can make progress on that latch, we must become
the writer. Any waiting thread will be eventually woken up by
ssux_lock_low::u_unlock() or ssux_lock_low::wr_unlock(), but not
by wr_u_downgrade() because the upgrade is a very rare operation.
pull/1757/head
Marko Mäkelä 5 years ago
parent
commit
272a1289ad
  1. 10
      storage/innobase/include/rw_lock.h
  2. 9
      storage/innobase/sync/srw_lock.cc

10
storage/innobase/include/rw_lock.h

@ -92,16 +92,22 @@ protected:
bool upgrade_trylock()
{
auto l= UPDATER;
while (!lock.compare_exchange_strong(l, l ^ (WRITER | UPDATER),
while (!lock.compare_exchange_strong(l, WRITER,
std::memory_order_acquire,
std::memory_order_relaxed))
{
DBUG_ASSERT(!(~l & (UPDATER - 1)));
/* Either conflicting (read) locks have been granted, or
the WRITER_WAITING flag was set by some thread that is waiting
to become WRITER. */
DBUG_ASSERT(!(~l & (UPDATER - 1)) || l == (UPDATER | WRITER_WAITING));
DBUG_ASSERT(((WRITER | UPDATER) & l) == UPDATER);
if (~(WRITER_WAITING | UPDATER) & l)
return false;
}
DBUG_ASSERT((l & ~WRITER_WAITING) == UPDATER);
/* Any thread that had set WRITER_WAITING will eventually be woken
up by ssux_lock_low::x_unlock() or ssux_lock_low::u_unlock()
(not ssux_lock_low::wr_u_downgrade() to keep the code simple). */
return true;
}
/** Downgrade an exclusive lock to an update lock. */

9
storage/innobase/sync/srw_lock.cc

@ -204,7 +204,8 @@ void ssux_lock_low::write_lock(bool holding_u)
ut_delay(srv_spin_wait_delay);
}
l= holding_u ? WRITER_WAITING | UPDATER : WRITER_WAITING;
const uint32_t e= holding_u ? WRITER_WAITING | UPDATER : WRITER_WAITING;
l= e;
if (write_lock_wait_try(l))
return;
@ -220,7 +221,11 @@ void ssux_lock_low::write_lock(bool holding_u)
if (holding_u && upgrade_trylock())
return;
}
l= write_lock_wait_start() | WRITER_WAITING;
for (l= write_lock_wait_start() | WRITER_WAITING;
(l | WRITER_WAITING) == e; )
if (write_lock_wait_try(l))
return;
}
else
DBUG_ASSERT(~WRITER_WAITING & l);

Loading…
Cancel
Save