1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Bug#43758 Query cache can lock up threads in 'freeing items' state

Early patch submitted for discussion.

It is possible for more than one thread to enter the condition
in query_cache_insert(), but the condition predicate is to
signal one thread each time the cache status changes between
the following states: {NO_FLUSH_IN_PROGRESS,FLUSH_IN_PROGRESS,
TABLE_FLUSH_IN_PROGRESS}

Consider three threads THD1, THD2, THD3

   THD2: select ... => Got a writer in ::store_query
   THD3: select ... => Got a writer in ::store_query
   THD1: flush tables => qc status= FLUSH_IN_PROGRESS;
                      new writers are blocked.
   THD2: select ... => Still got a writer and enters cond in
                       query_cache_insert
   THD3: select ... => Still got a writer and enters cond in
                       query_cache_insert
   THD1: flush tables => finished and signal status change.
   THD2: select ... => Wakes up and completes the insert.
   THD3: select ... => Happily waiting for better times. Why hurry?

This patch is a refactoring of this lock system. It introduces four new methods:
   Query_cache::try_lock()
   Query_cache::lock()
   Query_cache::lock_and_suspend()
   Query_cache::unlock()

This change also deprecates wait_while_table_flush_is_in_progress(). All threads are
queued and put on a conditional wait. On each unlock the queue is signalled. This resolve
the issues with left over threads. To assure that no threads are spending unnecessary
time waiting a signal broadcast is issued every time a lock is taken before a full
cache flush.
This commit is contained in:
Kristofer Pettersson
2009-06-16 10:34:47 +02:00
parent 8f2ff69434
commit b22d02ad42
4 changed files with 472 additions and 214 deletions

View File

@ -352,11 +352,6 @@ TODO list:
#define RW_UNLOCK(M) {DBUG_PRINT("lock", ("rwlock unlock 0x%lx",(ulong)(M))); \
if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")); \
else DBUG_PRINT("lock", ("rwlock unlock FAILED %d", errno)); }
#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \
pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));}
#define STRUCT_UNLOCK(M) { \
DBUG_PRINT("lock", ("%d struct unlock...",__LINE__)); \
pthread_mutex_unlock(M);DBUG_PRINT("lock", ("struct unlock OK"));}
#define BLOCK_LOCK_WR(B) {DBUG_PRINT("lock", ("%d LOCK_WR 0x%lx",\
__LINE__,(ulong)(B))); \
B->query()->lock_writing();}
@ -403,8 +398,6 @@ static void debug_wait_for_kill(const char *info)
#define RW_WLOCK(M) rw_wrlock(M)
#define RW_RLOCK(M) rw_rdlock(M)
#define RW_UNLOCK(M) rw_unlock(M)
#define STRUCT_LOCK(M) pthread_mutex_lock(M)
#define STRUCT_UNLOCK(M) pthread_mutex_unlock(M)
#define BLOCK_LOCK_WR(B) B->query()->lock_writing()
#define BLOCK_LOCK_RD(B) B->query()->lock_reading()
#define BLOCK_UNLOCK_WR(B) B->query()->unlock_writing()
@ -419,6 +412,140 @@ TYPELIB query_cache_type_typelib=
};
/**
Serialize access to the query cache.
If the lock cannot be granted the thread hangs in a conditional wait which
is signalled on each unlock.
The lock attempt will also fail without wait if lock_and_suspend() is in
effect by another thread. This enables a quick path in execution to skip waits
when the outcome is known.
@return
@retval FALSE An exclusive lock was taken
@retval TRUE The locking attempt failed
*/
bool Query_cache::try_lock(void)
{
bool interrupt= FALSE;
DBUG_ENTER("Query_cache::try_lock");
pthread_mutex_lock(&structure_guard_mutex);
while (1)
{
if (m_cache_lock_status == Query_cache::UNLOCKED)
{
m_cache_lock_status= Query_cache::LOCKED;
#ifndef DBUG_OFF
THD *thd= current_thd;
if (thd)
m_cache_lock_thread_id= thd->thread_id;
#endif
break;
}
else if (m_cache_lock_status == Query_cache::LOCKED_NO_WAIT)
{
/*
If query cache is protected by a LOCKED_NO_WAIT lock this thread
should avoid using the query cache as it is being evicted.
*/
interrupt= TRUE;
break;
}
else
{
DBUG_ASSERT(m_cache_lock_status == Query_cache::LOCKED);
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
}
}
pthread_mutex_unlock(&structure_guard_mutex);
DBUG_RETURN(interrupt);
}
/**
Serialize access to the query cache.
If the lock cannot be granted the thread hangs in a conditional wait which
is signalled on each unlock.
This method also suspends the query cache so that other threads attempting to
lock the cache with try_lock() will fail directly without waiting.
It is used by all methods which flushes or destroys the whole cache.
*/
void Query_cache::lock_and_suspend(void)
{
DBUG_ENTER("Query_cache::lock_and_suspend");
pthread_mutex_lock(&structure_guard_mutex);
while (m_cache_lock_status != Query_cache::UNLOCKED)
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
m_cache_lock_status= Query_cache::LOCKED_NO_WAIT;
#ifndef DBUG_OFF
THD *thd= current_thd;
if (thd)
m_cache_lock_thread_id= thd->thread_id;
#endif
/* Wake up everybody, a whole cache flush is starting! */
pthread_cond_broadcast(&COND_cache_status_changed);
pthread_mutex_unlock(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
/**
Serialize access to the query cache.
If the lock cannot be granted the thread hangs in a conditional wait which
is signalled on each unlock.
It is used by all methods which invalidates one or more tables.
*/
void Query_cache::lock(void)
{
DBUG_ENTER("Query_cache::lock");
pthread_mutex_lock(&structure_guard_mutex);
while (m_cache_lock_status != Query_cache::UNLOCKED)
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
m_cache_lock_status= Query_cache::LOCKED;
#ifndef DBUG_OFF
THD *thd= current_thd;
if (thd)
m_cache_lock_thread_id= thd->thread_id;
#endif
pthread_mutex_unlock(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
/**
Set the query cache to UNLOCKED and signal waiting threads.
*/
void Query_cache::unlock(void)
{
DBUG_ENTER("Query_cache::unlock");
pthread_mutex_lock(&structure_guard_mutex);
#ifndef DBUG_OFF
THD *thd= current_thd;
if (thd)
DBUG_ASSERT(m_cache_lock_thread_id == thd->thread_id);
#endif
DBUG_ASSERT(m_cache_lock_status == Query_cache::LOCKED ||
m_cache_lock_status == Query_cache::LOCKED_NO_WAIT);
m_cache_lock_status= Query_cache::UNLOCKED;
DBUG_PRINT("Query_cache",("Sending signal"));
pthread_cond_signal(&COND_cache_status_changed);
pthread_mutex_unlock(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
/**
Helper function for determine if a SELECT statement has a SQL_NO_CACHE
directive.
@ -713,14 +840,8 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
DBUG_EXECUTE_IF("wait_in_query_cache_insert",
debug_wait_for_kill("wait_in_query_cache_insert"); );
STRUCT_LOCK(&query_cache.structure_guard_mutex);
bool interrupt;
query_cache.wait_while_table_flush_is_in_progress(&interrupt);
if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
if (query_cache.try_lock())
DBUG_VOID_RETURN;
}
Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
if (!query_block)
@ -729,7 +850,7 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
We lost the writer and the currently processed query has been
invalidated; there is nothing left to do.
*/
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
query_cache.unlock();
DBUG_VOID_RETURN;
}
@ -755,7 +876,7 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
query_cache.free_query(query_block);
query_cache.refused++;
// append_result_data no success => we need unlock
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
query_cache.unlock();
DBUG_VOID_RETURN;
}
@ -777,14 +898,8 @@ void query_cache_abort(NET *net)
if (net->query_cache_query == 0)
DBUG_VOID_RETURN;
STRUCT_LOCK(&query_cache.structure_guard_mutex);
bool interrupt;
query_cache.wait_while_table_flush_is_in_progress(&interrupt);
if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
if (query_cache.try_lock())
DBUG_VOID_RETURN;
}
/*
While we were waiting another thread might have changed the status
@ -803,8 +918,7 @@ void query_cache_abort(NET *net)
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
}
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
query_cache.unlock();
DBUG_VOID_RETURN;
}
@ -832,15 +946,8 @@ void query_cache_end_of_result(THD *thd)
emb_count_querycache_size(thd));
#endif
STRUCT_LOCK(&query_cache.structure_guard_mutex);
bool interrupt;
query_cache.wait_while_table_flush_is_in_progress(&interrupt);
if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
if (query_cache.try_lock())
DBUG_VOID_RETURN;
}
query_block= ((Query_cache_block*) thd->net.query_cache_query);
if (query_block)
@ -869,10 +976,9 @@ void query_cache_end_of_result(THD *thd)
*/
DBUG_ASSERT(0);
query_cache.free_query(query_block);
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
query_cache.unlock();
DBUG_VOID_RETURN;
}
last_result_block= header->result()->prev;
allign_size= ALIGN_SIZE(last_result_block->used);
len= max(query_cache.min_allocation_unit, allign_size);
@ -885,13 +991,11 @@ void query_cache_end_of_result(THD *thd)
/* Drop the writer. */
header->writer(0);
thd->net.query_cache_query= 0;
BLOCK_UNLOCK_WR(query_block);
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
}
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
query_cache.unlock();
DBUG_VOID_RETURN;
}
@ -950,11 +1054,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
query_cache_size_arg));
DBUG_ASSERT(initialized);
STRUCT_LOCK(&structure_guard_mutex);
while (is_flushing())
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
lock_and_suspend();
/*
Wait for all readers and writers to exit. When the list of all queries
@ -986,13 +1086,10 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
query_cache_size= query_cache_size_arg;
new_query_cache_size= init_cache();
STRUCT_LOCK(&structure_guard_mutex);
m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
pthread_cond_signal(&COND_cache_status_changed);
if (new_query_cache_size)
DBUG_EXECUTE("check_querycache",check_integrity(1););
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_RETURN(new_query_cache_size);
}
@ -1089,15 +1186,16 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
*/
ha_release_temporary_latches(thd);
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size == 0 || is_flushing())
/*
A table- or a full flush operation can potentially take a long time to
finish. We choose not to wait for them and skip caching statements
instead.
*/
if (try_lock())
DBUG_VOID_RETURN;
if (query_cache_size == 0)
{
/*
A table- or a full flush operation can potentially take a long time to
finish. We choose not to wait for them and skip caching statements
instead.
*/
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_VOID_RETURN;
}
DUMP(this);
@ -1105,7 +1203,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
if (ask_handler_allowance(thd, tables_used))
{
refused++;
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_VOID_RETURN;
}
@ -1153,7 +1251,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
DBUG_PRINT("qcache", ("insertion in query hash"));
header->unlock_n_destroy();
free_memory_block(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
goto end;
}
if (!register_all_tables(query_block, tables_used, local_tables))
@ -1163,7 +1261,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
hash_delete(&queries, (uchar *) query_block);
header->unlock_n_destroy();
free_memory_block(query_block);
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
goto end;
}
double_linked_list_simple_include(query_block, &queries_blocks);
@ -1173,7 +1271,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
header->writer(net);
header->tables_type(tables_type);
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
// init_n_lock make query block locked
BLOCK_UNLOCK_WR(query_block);
@ -1182,7 +1280,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
{
// We have not enough memory to store query => do nothing
refused++;
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_PRINT("warning", ("Can't allocate query"));
}
}
@ -1190,7 +1288,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
{
// Another thread is processing the same query => do nothing
refused++;
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_PRINT("qcache", ("Another thread process same query"));
}
}
@ -1282,18 +1380,17 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
}
}
STRUCT_LOCK(&structure_guard_mutex);
/*
Try to obtain an exclusive lock on the query cache. If the cache is
disabled or if a full cache flush is in progress, the attempt to
get the lock is aborted.
*/
if (try_lock())
goto err;
if (query_cache_size == 0)
goto err_unlock;
if (is_flushing())
{
/* Return; Query cache is temporarily disabled while we flush. */
DBUG_PRINT("qcache",("query cache disabled"));
goto err_unlock;
}
/*
Check that we haven't forgot to reset the query cache variables;
make sure there are no attached query cache writer to this thread.
@ -1427,7 +1524,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
DBUG_PRINT("qcache",
("Temporary table detected: '%s.%s'",
table_list.db, table_list.alias));
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
/*
We should not store result of this query because it contain
temporary tables => assign following variable to make check
@ -1448,7 +1545,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
DBUG_PRINT("qcache",
("probably no SELECT access to %s.%s => return to normal processing",
table_list.db, table_list.alias));
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
thd->lex->safe_to_cache_query=0; // Don't try to cache this
BLOCK_UNLOCK_RD(query_block);
DBUG_RETURN(-1); // Privilege error
@ -1491,7 +1588,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
}
move_to_query_list_end(query_block);
hits++;
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
/*
Send cached result to client
@ -1530,7 +1627,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
DBUG_RETURN(1); // Result sent to client
err_unlock:
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
err:
DBUG_RETURN(0); // Query was not cached
}
@ -1650,47 +1747,6 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
}
/**
Synchronize the thread with any flushing operations.
This helper function is called whenever a thread needs to operate on the
query cache structure (example: during invalidation). If a table flush is in
progress this function will wait for it to stop. If a full flush is in
progress, the function will set the interrupt parameter to indicate that the
current operation is redundant and should be interrupted.
@param[out] interrupt This out-parameter will be set to TRUE if the calling
function is redundant and should be interrupted.
@return If the interrupt-parameter is TRUE then m_cache_status is set to
NO_FLUSH_IN_PROGRESS. If the interrupt-parameter is FALSE then
m_cache_status is set to FLUSH_IN_PROGRESS.
The structure_guard_mutex will in any case be locked.
*/
void Query_cache::wait_while_table_flush_is_in_progress(bool *interrupt)
{
while (is_flushing())
{
/*
If there already is a full flush in progress query cache isn't enabled
and additional flushes are redundant; just return instead.
*/
if (m_cache_status == Query_cache::FLUSH_IN_PROGRESS)
{
*interrupt= TRUE;
return;
}
/*
If a table flush is in progress; wait on cache status to change.
*/
if (m_cache_status == Query_cache::TABLE_FLUSH_IN_PROGRESS)
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
}
*interrupt= FALSE;
}
/**
Remove all cached queries that uses the given database.
*/
@ -1700,14 +1756,11 @@ void Query_cache::invalidate(char *db)
bool restart= FALSE;
DBUG_ENTER("Query_cache::invalidate (db)");
STRUCT_LOCK(&structure_guard_mutex);
bool interrupt;
wait_while_table_flush_is_in_progress(&interrupt);
if (interrupt)
{
STRUCT_UNLOCK(&structure_guard_mutex);
return;
}
/*
Lock the query cache and queue all invalidation attempts to avoid
the risk of a race between invalidation, cache inserts and flushes.
*/
lock();
THD *thd= current_thd;
@ -1763,7 +1816,7 @@ void Query_cache::invalidate(char *db)
} while (restart);
} // end if( tables_blocks )
}
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_VOID_RETURN;
}
@ -1787,7 +1840,10 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
void Query_cache::flush()
{
DBUG_ENTER("Query_cache::flush");
STRUCT_LOCK(&structure_guard_mutex);
DBUG_EXECUTE_IF("wait_in_query_cache_flush1",
debug_wait_for_kill("wait_in_query_cache_flush1"););
lock_and_suspend();
if (query_cache_size > 0)
{
DUMP(this);
@ -1796,7 +1852,7 @@ void Query_cache::flush()
}
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_VOID_RETURN;
}
@ -1815,18 +1871,16 @@ void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
bool interrupt;
STRUCT_LOCK(&structure_guard_mutex);
wait_while_table_flush_is_in_progress(&interrupt);
if (interrupt)
{
STRUCT_UNLOCK(&structure_guard_mutex);
/*
If the entire qc is being invalidated we can bail out early
instead of waiting for the lock.
*/
if (try_lock())
DBUG_VOID_RETURN;
}
if (query_cache_size == 0)
{
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_VOID_RETURN;
}
@ -1836,7 +1890,7 @@ void Query_cache::pack(ulong join_limit, uint iteration_limit)
pack_cache();
} while ((++i < iteration_limit) && join_results(join_limit));
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_VOID_RETURN;
}
@ -1851,9 +1905,9 @@ void Query_cache::destroy()
else
{
/* Underlying code expects the lock. */
STRUCT_LOCK(&structure_guard_mutex);
lock_and_suspend();
free_cache();
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
pthread_cond_destroy(&COND_cache_status_changed);
pthread_mutex_destroy(&structure_guard_mutex);
@ -1872,7 +1926,7 @@ void Query_cache::init()
DBUG_ENTER("Query_cache::init");
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
pthread_cond_init(&COND_cache_status_changed, NULL);
m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
m_cache_lock_status= Query_cache::UNLOCKED;
initialized = 1;
DBUG_VOID_RETURN;
}
@ -2112,23 +2166,9 @@ void Query_cache::free_cache()
void Query_cache::flush_cache()
{
/*
If there is flush in progress, wait for it to finish, and then do
our flush. This is necessary because something could be added to
the cache before we acquire the lock again, and some code (like
Query_cache::free_cache()) depends on the fact that after the
flush the cache is empty.
*/
while (is_flushing())
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
/*
Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using
the cache while we are in the middle of the flush, and we release
the lock so that other threads won't block.
*/
m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_EXECUTE_IF("wait_in_query_cache_flush2",
debug_wait_for_kill("wait_in_query_cache_flush2"););
my_hash_reset(&queries);
while (queries_blocks != 0)
@ -2136,10 +2176,6 @@ void Query_cache::flush_cache()
BLOCK_LOCK_WR(queries_blocks);
free_query_internal(queries_blocks);
}
STRUCT_LOCK(&structure_guard_mutex);
m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
pthread_cond_signal(&COND_cache_status_changed);
}
/*
@ -2319,10 +2355,6 @@ Query_cache::write_block_data(ulong data_len, uchar* data,
}
/*
On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be done.
*/
my_bool
Query_cache::append_result_data(Query_cache_block **current_block,
ulong data_len, uchar* data,
@ -2342,10 +2374,6 @@ Query_cache::append_result_data(Query_cache_block **current_block,
if (*current_block == 0)
{
DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len));
/*
STRUCT_UNLOCK(&structure_guard_mutex) Will be done by
write_result_data if success;
*/
DBUG_RETURN(write_result_data(current_block, data_len, data, query_block,
Query_cache_block::RES_BEG));
}
@ -2376,10 +2404,6 @@ Query_cache::append_result_data(Query_cache_block **current_block,
DBUG_PRINT("qcache", ("allocate new block for %lu bytes",
data_len-last_block_free_space));
Query_cache_block *new_block = 0;
/*
On success STRUCT_UNLOCK(&structure_guard_mutex) will be done
by the next call
*/
success = write_result_data(&new_block, data_len-last_block_free_space,
(uchar*)(((uchar*)data)+last_block_free_space),
query_block,
@ -2394,7 +2418,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
else
{
// It is success (nobody can prevent us write data)
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
}
// Now finally write data to the last block
@ -2432,7 +2456,7 @@ my_bool Query_cache::write_result_data(Query_cache_block **result_block,
if (success)
{
// It is success (nobody can prevent us write data)
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
uint headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(sizeof(Query_cache_result)));
#ifndef EMBEDDED_LIBRARY
@ -2590,36 +2614,23 @@ void Query_cache::invalidate_table(THD *thd, TABLE *table)
void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
{
bool interrupt;
STRUCT_LOCK(&structure_guard_mutex);
wait_while_table_flush_is_in_progress(&interrupt);
if (interrupt)
{
STRUCT_UNLOCK(&structure_guard_mutex);
return;
}
DBUG_EXECUTE_IF("wait_in_query_cache_invalidate1",
debug_wait_for_kill("wait_in_query_cache_invalidate1"); );
/*
Setting 'TABLE_FLUSH_IN_PROGRESS' will temporarily disable the cache
so that structural changes to cache won't block the entire server.
However, threads requesting to change the query cache will still have
to wait for the flush to finish.
Lock the query cache and queue all invalidation attempts to avoid
the risk of a race between invalidation, cache inserts and flushes.
*/
m_cache_status= Query_cache::TABLE_FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
lock();
DBUG_EXECUTE_IF("wait_in_query_cache_invalidate2",
debug_wait_for_kill("wait_in_query_cache_invalidate2"); );
if (query_cache_size > 0)
invalidate_table_internal(thd, key, key_length);
STRUCT_LOCK(&structure_guard_mutex);
m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
/*
net_real_write might be waiting on a change on the m_cache_status
variable.
*/
pthread_cond_signal(&COND_cache_status_changed);
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
}
@ -2628,7 +2639,7 @@ void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
The caller must ensure that no other thread is trying to work with
the query cache when this function is executed.
@pre structure_guard_mutex is acquired or TABLE_FLUSH_IN_PROGRESS is set.
@pre structure_guard_mutex is acquired or LOCKED is set.
*/
void
@ -2646,7 +2657,7 @@ Query_cache::invalidate_table_internal(THD *thd, uchar *key, uint32 key_length)
/**
Invalidate a linked list of query cache blocks.
Each block tries to aquire a block level lock before
Each block tries to acquire a block level lock before
free_query is a called. This function will in turn affect
related table- and result-blocks.
@ -4170,10 +4181,7 @@ my_bool Query_cache::check_integrity(bool locked)
DBUG_ENTER("check_integrity");
if (!locked)
STRUCT_LOCK(&structure_guard_mutex);
while (is_flushing())
pthread_cond_wait(&COND_cache_status_changed,&structure_guard_mutex);
lock_and_suspend();
if (hash_check(&queries))
{
@ -4422,7 +4430,7 @@ my_bool Query_cache::check_integrity(bool locked)
}
DBUG_ASSERT(result == 0);
if (!locked)
STRUCT_UNLOCK(&structure_guard_mutex);
unlock();
DBUG_RETURN(result);
}