Commit 54c0ac72 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-30134 Assertion failed in buf_page_t::unfix() in buf_pool_t::watch_unset()

buf_pool_t::watch_set(): Always buffer-fix a block if one was found,
no matter if it is a watch sentinel or a buffer page. The type of
the block descriptor will be rechecked in buf_page_t::watch_unset().
Do not expect the caller to acquire the page hash latch. Starting with
commit bd5a6403 it is safe to release
buf_pool.mutex before acquiring a buf_pool.page_hash latch.

buf_page_get_low(): Adjust to the changed buf_pool_t::watch_set().

This simplifies the logic and fixes a bug that was reproduced when
using debug builds and the setting innodb_change_buffering_debug=1.
parent 9c157994
...@@ -1951,28 +1951,22 @@ static void buf_relocate(buf_page_t *bpage, buf_page_t *dpage) ...@@ -1951,28 +1951,22 @@ static void buf_relocate(buf_page_t *bpage, buf_page_t *dpage)
buf_pool.page_hash.replace(chain, bpage, dpage); buf_pool.page_hash.replace(chain, bpage, dpage);
} }
/** Register a watch for a page identifier. The caller must hold an buf_page_t *buf_pool_t::watch_set(const page_id_t id,
exclusive page hash latch. The *hash_lock may be released, buf_pool_t::hash_chain &chain)
relocated, and reacquired.
@param id page identifier
@param chain hash table chain with exclusively held page_hash
@return a buffer pool block corresponding to id
@retval nullptr if the block was not present, and a watch was installed */
inline buf_page_t *buf_pool_t::watch_set(const page_id_t id,
buf_pool_t::hash_chain &chain)
{ {
ut_ad(&chain == &page_hash.cell_get(id.fold())); ut_ad(&chain == &page_hash.cell_get(id.fold()));
ut_ad(page_hash.lock_get(chain).is_write_locked()); page_hash.lock_get(chain).lock();
retry: buf_page_t *bpage= page_hash.get(id, chain);
if (buf_page_t *bpage= page_hash.get(id, chain))
if (bpage)
{ {
if (!watch_is_sentinel(*bpage)) got_block:
/* The page was loaded meanwhile. */
return bpage;
/* Add to an existing watch. */
bpage->fix(); bpage->fix();
return nullptr; if (watch_is_sentinel(*bpage))
bpage= nullptr;
page_hash.lock_get(chain).unlock();
return bpage;
} }
page_hash.lock_get(chain).unlock(); page_hash.lock_get(chain).unlock();
...@@ -2001,25 +1995,23 @@ inline buf_page_t *buf_pool_t::watch_set(const page_id_t id, ...@@ -2001,25 +1995,23 @@ inline buf_page_t *buf_pool_t::watch_set(const page_id_t id,
w->set_state(buf_page_t::UNFIXED + 1); w->set_state(buf_page_t::UNFIXED + 1);
w->id_= id; w->id_= id;
buf_page_t *bpage= page_hash.get(id, chain); page_hash.lock_get(chain).lock();
bpage= page_hash.get(id, chain);
if (UNIV_LIKELY_NULL(bpage)) if (UNIV_LIKELY_NULL(bpage))
{ {
w->set_state(buf_page_t::NOT_USED); w->set_state(buf_page_t::NOT_USED);
page_hash.lock_get(chain).lock();
mysql_mutex_unlock(&mutex); mysql_mutex_unlock(&mutex);
goto retry; goto got_block;
} }
page_hash.lock_get(chain).lock();
ut_ad(w->state() == buf_page_t::UNFIXED + 1); ut_ad(w->state() == buf_page_t::UNFIXED + 1);
buf_pool.page_hash.append(chain, w); buf_pool.page_hash.append(chain, w);
mysql_mutex_unlock(&mutex); mysql_mutex_unlock(&mutex);
page_hash.lock_get(chain).unlock();
return nullptr; return nullptr;
} }
ut_error; ut_error;
mysql_mutex_unlock(&mutex);
return nullptr;
} }
/** Stop watching whether a page has been read in. /** Stop watching whether a page has been read in.
...@@ -2467,16 +2459,13 @@ buf_page_get_low( ...@@ -2467,16 +2459,13 @@ buf_page_get_low(
case BUF_PEEK_IF_IN_POOL: case BUF_PEEK_IF_IN_POOL:
return nullptr; return nullptr;
case BUF_GET_IF_IN_POOL_OR_WATCH: case BUF_GET_IF_IN_POOL_OR_WATCH:
/* We cannot easily use a memory transaction here. */ /* Buffer-fixing inside watch_set() will prevent eviction */
hash_lock.lock();
block = reinterpret_cast<buf_block_t*> block = reinterpret_cast<buf_block_t*>
(buf_pool.watch_set(page_id, chain)); (buf_pool.watch_set(page_id, chain));
/* buffer-fixing will prevent eviction */
state = block ? block->page.fix() : 0;
hash_lock.unlock();
if (block) { if (block) {
goto got_block; state = block->page.state();
goto got_block_fixed;
} }
return nullptr; return nullptr;
...@@ -2515,6 +2504,7 @@ buf_page_get_low( ...@@ -2515,6 +2504,7 @@ buf_page_get_low(
got_block: got_block:
ut_ad(!block->page.in_zip_hash); ut_ad(!block->page.in_zip_hash);
state++; state++;
got_block_fixed:
ut_ad(state > buf_page_t::FREED); ut_ad(state > buf_page_t::FREED);
if (state > buf_page_t::READ_FIX && state < buf_page_t::WRITE_FIX) { if (state > buf_page_t::READ_FIX && state < buf_page_t::WRITE_FIX) {
...@@ -2724,24 +2714,19 @@ buf_page_get_low( ...@@ -2724,24 +2714,19 @@ buf_page_get_low(
const bool evicted = buf_LRU_free_page(&block->page, true); const bool evicted = buf_LRU_free_page(&block->page, true);
space->release(); space->release();
if (!evicted) {
block->fix();
}
mysql_mutex_unlock(&buf_pool.mutex);
if (evicted) { if (evicted) {
page_hash_latch& hash_lock if (mode == BUF_GET_IF_IN_POOL_OR_WATCH) {
= buf_pool.page_hash.lock_get(chain); buf_pool.watch_set(page_id, chain);
hash_lock.lock(); }
mysql_mutex_unlock(&buf_pool.mutex);
/* We may set the watch, as it would have
been set if the page were not in the
buffer pool in the first place. */
block= reinterpret_cast<buf_block_t*>(
mode == BUF_GET_IF_IN_POOL_OR_WATCH
? buf_pool.watch_set(page_id, chain)
: buf_pool.page_hash.get(page_id, chain));
hash_lock.unlock();
return(NULL); return(NULL);
} }
block->fix();
mysql_mutex_unlock(&buf_pool.mutex);
buf_flush_sync(); buf_flush_sync();
state = block->page.state(); state = block->page.state();
......
...@@ -1470,14 +1470,12 @@ class buf_pool_t ...@@ -1470,14 +1470,12 @@ class buf_pool_t
return !watch_is_sentinel(*page_hash.get(id, chain)); return !watch_is_sentinel(*page_hash.get(id, chain));
} }
/** Register a watch for a page identifier. The caller must hold an /** Register a watch for a page identifier.
exclusive page hash latch. The *hash_lock may be released,
relocated, and reacquired.
@param id page identifier @param id page identifier
@param chain hash table chain with exclusively held page_hash @param chain page_hash.cell_get(id.fold())
@return a buffer pool block corresponding to id @return a buffer page corresponding to id
@retval nullptr if the block was not present, and a watch was installed */ @retval nullptr if the block was not present in page_hash */
inline buf_page_t *watch_set(const page_id_t id, hash_chain &chain); buf_page_t *watch_set(const page_id_t id, hash_chain &chain);
/** Stop watching whether a page has been read in. /** Stop watching whether a page has been read in.
watch_set(id) must have returned nullptr before. watch_set(id) must have returned nullptr before.
......
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