Commit 4f8803c0 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-34678 pthread_mutex_init() without pthread_mutex_destroy()

When SUX_LOCK_GENERIC is defined, the srw_mutex, srw_lock, sux_lock are
implemented based on pthread_mutex_t and pthread_cond_t.  This is the
only option for systems that lack a futex-like system call.

In the SUX_LOCK_GENERIC mode, if pthread_mutex_init() is allocating
some resources that need to be freed by pthread_mutex_destroy(),
a memory leak could occur when we are repeatedly invoking
pthread_mutex_init() without a pthread_mutex_destroy() in between.

pthread_mutex_wrapper::initialized: A debug field to track whether
pthread_mutex_init() has been invoked.  This also helps find bugs
like the one that was fixed by
commit 1c8af2ae (MDEV-34422);
one simply needs to add -DSUX_LOCK_GENERIC to the CMAKE_CXX_FLAGS
to catch that particular bug on the initial server bootstrap.

buf_block_init(), buf_page_init_for_read(): Invoke block_lock::init()
because buf_page_t::init() will no longer do that.

buf_page_t::init(): Instead of invoking lock.init(), assert that it
has already been invoked (the lock is vacant).

add_fts_index(), build_fts_hidden_table(): Explicitly invoke
index_lock::init() in order to avoid a pthread_mutex_destroy()
invocation on an uninitialized object.

srw_lock_debug::destroy(): Invoke readers_lock.destroy().

trx_sys_t::create(): Invoke trx_rseg_t::init() on all rollback segments
in order to guarantee a deterministic state for shutdown, even if
InnoDB fails to start up.

trx_rseg_array_init(), trx_temp_rseg_create(), trx_rseg_create():
Invoke trx_rseg_t::destroy() before trx_rseg_t::init() in order to
balance pthread_mutex_init() and pthread_mutex_destroy() calls.
parent 2e580dc2
......@@ -842,6 +842,7 @@ buf_block_init(buf_block_t* block, byte* frame)
MEM_MAKE_DEFINED(&block->modify_clock, sizeof block->modify_clock);
ut_ad(!block->modify_clock);
MEM_MAKE_DEFINED(&block->page.lock, sizeof block->page.lock);
block->page.lock.init();
block->page.init(buf_page_t::NOT_USED, page_id_t(~0ULL));
#ifdef BTR_CUR_HASH_ADAPT
MEM_MAKE_DEFINED(&block->index, sizeof block->index);
......
......@@ -212,6 +212,7 @@ static buf_page_t* buf_page_init_for_read(ulint mode, const page_id_t page_id,
page_zip_set_size(&bpage->zip, zip_size);
bpage->zip.data = (page_zip_t*) data;
bpage->lock.init();
bpage->init(buf_page_t::READ_FIX, page_id);
bpage->lock.x_lock(true);
......
......@@ -648,10 +648,10 @@ class buf_page_t
void init(uint32_t state, page_id_t id)
{
ut_ad(state < REMOVE_HASH || state >= UNFIXED);
ut_ad(!lock.is_locked_or_waiting());
id_= id;
zip.fix= state;
oldest_modification_= 0;
lock.init();
ut_d(in_zip_hash= false);
ut_d(in_free_list= false);
ut_d(in_LRU_list= false);
......
......@@ -40,32 +40,45 @@ template<bool spinloop>
class pthread_mutex_wrapper final
{
pthread_mutex_t lock;
#ifdef UNIV_DEBUG
/** whether the mutex is usable; set by init(); cleared by destroy() */
bool initialized{false};
public:
~pthread_mutex_wrapper() { ut_ad(!initialized); }
#endif
public:
void init()
{
ut_ad(!initialized);
ut_d(initialized= true);
if (spinloop)
pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST);
else
pthread_mutex_init(&lock, nullptr);
}
void destroy() { pthread_mutex_destroy(&lock); }
void destroy()
{
ut_ad(initialized); ut_d(initialized=false);
pthread_mutex_destroy(&lock);
}
# ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
void wr_lock() { pthread_mutex_lock(&lock); }
void wr_lock() { ut_ad(initialized); pthread_mutex_lock(&lock); }
# else
private:
void wr_wait();
public:
inline void wr_lock();
# endif
void wr_unlock() { pthread_mutex_unlock(&lock); }
bool wr_lock_try() { return !pthread_mutex_trylock(&lock); }
void wr_unlock() { ut_ad(initialized); pthread_mutex_unlock(&lock); }
bool wr_lock_try()
{ ut_ad(initialized); return !pthread_mutex_trylock(&lock); }
};
# ifndef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
template<> void pthread_mutex_wrapper<true>::wr_wait();
template<>
inline void pthread_mutex_wrapper<false>::wr_lock()
{ pthread_mutex_lock(&lock); }
{ ut_ad(initialized); pthread_mutex_lock(&lock); }
template<>
inline void pthread_mutex_wrapper<true>::wr_lock()
{ if (!wr_lock_try()) wr_wait(); }
......
......@@ -3212,6 +3212,7 @@ static void add_fts_index(dict_table_t *table)
{
dict_index_t *fts_index= dict_mem_index_create(
table, FTS_DOC_ID_INDEX_NAME, DICT_UNIQUE, 2);
fts_index->lock.SRW_LOCK_INIT(index_tree_rw_lock_key);
fts_index->page= FIL_NULL;
fts_index->cached= 1;
fts_index->n_uniq= 1;
......@@ -3293,6 +3294,7 @@ static dict_table_t *build_fts_hidden_table(
new_table, old_index->name, old_index->type,
old_index->n_fields + is_clustered);
new_index->lock.SRW_LOCK_INIT(index_tree_rw_lock_key);
new_index->id= old_index->id;
new_index->n_uniq= old_index->n_uniq;
new_index->type= old_index->type;
......
......@@ -661,6 +661,7 @@ void srw_lock_debug::destroy()
ut_ad(r->empty());
delete r;
}
readers_lock.destroy();
srw_lock::destroy();
}
......
......@@ -631,6 +631,7 @@ dberr_t trx_rseg_array_init()
sys, rseg_id);
if (page_no != FIL_NULL) {
trx_rseg_t& rseg = trx_sys.rseg_array[rseg_id];
rseg.destroy();
rseg.init(fil_space_get(
trx_sysf_rseg_get_space(
sys, rseg_id)),
......@@ -715,6 +716,7 @@ dberr_t trx_temp_rseg_create(mtr_t *mtr)
mtr->commit();
return err;
}
trx_sys.temp_rsegs[i].destroy();
trx_sys.temp_rsegs[i].init(fil_system.temp_space,
rblock->page.id().page_no());
mtr->commit();
......
......@@ -152,6 +152,10 @@ void trx_sys_t::create()
m_initialised= true;
trx_list.create();
rw_trx_hash.init();
for (auto &rseg : temp_rsegs)
rseg.init(nullptr, FIL_NULL);
for (auto &rseg : rseg_array)
rseg.init(nullptr, FIL_NULL);
}
size_t trx_sys_t::history_size()
......@@ -230,6 +234,7 @@ static trx_rseg_t *trx_rseg_create(ulint space_id)
? nullptr : trx_rseg_header_create(space, rseg_id, 0, &mtr, &err))
{
rseg= &trx_sys.rseg_array[rseg_id];
rseg->destroy();
rseg->init(space, rblock->page.id().page_no());
ut_ad(rseg->is_persistent());
mtr.write<4,mtr_t::MAYBE_NOP>
......@@ -329,13 +334,8 @@ trx_sys_t::close()
rw_trx_hash.destroy();
/* There can't be any active transactions. */
for (ulint i = 0; i < array_elements(temp_rsegs); ++i) {
temp_rsegs[i].destroy();
}
for (ulint i = 0; i < array_elements(rseg_array); ++i) {
rseg_array[i].destroy();
}
for (auto& rseg : temp_rsegs) rseg.destroy();
for (auto& rseg : rseg_array) rseg.destroy();
ut_a(trx_list.empty());
trx_list.close();
......
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