diff options
author | Guilhem Bichot <guilhem@mysql.com> | 2009-08-04 13:25:19 +0200 |
---|---|---|
committer | Guilhem Bichot <guilhem@mysql.com> | 2009-08-04 13:25:19 +0200 |
commit | b57e4dbd88671df86e2cf39aff5178976d710b64 (patch) | |
tree | 32be2bfec3ca062c65566c60ecf59b673d1f97e9 /sql/sql_cache.cc | |
parent | 1a0c2153a036296785dcdfa7b5f4974515616e11 (diff) | |
parent | 94efc1c6b084ed531b513e70fb66e7b7a1186b56 (diff) | |
download | mariadb-git-b57e4dbd88671df86e2cf39aff5178976d710b64.tar.gz |
Creation of mysql-trunk = {summit + "Innodb plugin replacing the builtin"}:
bzr branch mysql-5.1-performance-version mysql-trunk # Summit
cd mysql-trunk
bzr merge mysql-5.1-innodb_plugin # which is 5.1 + Innodb plugin
bzr rm innobase # remove the builtin
Next step: build, test fixes.
Diffstat (limited to 'sql/sql_cache.cc')
-rw-r--r-- | sql/sql_cache.cc | 428 |
1 files changed, 234 insertions, 194 deletions
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 7c104ccc455..52de41ef1b2 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -353,11 +353,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();} @@ -404,8 +399,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() @@ -421,6 +414,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. @@ -714,14 +841,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) @@ -730,7 +851,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; } @@ -756,7 +877,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; } @@ -778,14 +899,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 @@ -804,8 +919,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; } @@ -833,15 +947,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) @@ -870,10 +977,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); @@ -886,13 +992,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; } @@ -952,11 +1056,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 @@ -988,13 +1088,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); } @@ -1091,15 +1188,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); @@ -1107,7 +1205,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; } @@ -1155,7 +1253,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)) @@ -1165,7 +1263,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); @@ -1175,7 +1273,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); @@ -1184,7 +1282,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")); } } @@ -1192,7 +1290,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")); } } @@ -1291,18 +1389,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. @@ -1436,7 +1533,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 @@ -1457,7 +1554,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 @@ -1500,7 +1597,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 @@ -1540,7 +1637,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: MYSQL_QUERY_CACHE_MISS(thd->query); DBUG_RETURN(0); // Query was not cached @@ -1662,47 +1759,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. */ @@ -1711,14 +1767,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; @@ -1774,7 +1827,7 @@ void Query_cache::invalidate(char *db) } while (restart); } // end if( tables_blocks ) } - STRUCT_UNLOCK(&structure_guard_mutex); + unlock(); DBUG_VOID_RETURN; } @@ -1798,7 +1851,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); @@ -1807,7 +1863,7 @@ void Query_cache::flush() } DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); - STRUCT_UNLOCK(&structure_guard_mutex); + unlock(); DBUG_VOID_RETURN; } @@ -1826,18 +1882,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; } @@ -1847,7 +1901,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; } @@ -1862,9 +1916,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); @@ -1883,7 +1937,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; } @@ -2123,23 +2177,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) @@ -2147,10 +2187,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); } /* @@ -2330,10 +2366,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, @@ -2353,10 +2385,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)); } @@ -2387,10 +2415,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, @@ -2405,7 +2429,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 @@ -2443,7 +2467,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 @@ -2601,6 +2625,18 @@ void Query_cache::invalidate_table(THD *thd, TABLE *table) void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length) { +#ifdef TO_BE_REMOVED +/* + This ifdef'd piece comes from Summit, it's a manual backport (2008-10-15) of + http://lists.mysql.com/commits/56418. + But that was an early, non-final patch: after that backport was made, the + author of the patch decided to abandon it, and his final patch (put into 6.0) + was different. + Then 5.1's code was changed for some other reasons, so now we have a + conflict between the old patch backported to Summit and the latest 5.1. + The backport cannot stay, it has to be removed and then rewritten if + desired. +*/ bool interrupt; if (m_query_cache_is_disabled) @@ -2619,28 +2655,35 @@ void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length) STRUCT_UNLOCK(&structure_guard_mutex); return; } +||||||| BASE-REVISION + bool interrupt; + STRUCT_LOCK(&structure_guard_mutex); + wait_while_table_flush_is_in_progress(&interrupt); + if (interrupt) + { + STRUCT_UNLOCK(&structure_guard_mutex); + return; + } +======= +/* current 5.1 code: */ +#endif + 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(); } @@ -2649,7 +2692,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 @@ -2667,7 +2710,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. @@ -4191,10 +4234,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)) { @@ -4443,7 +4483,7 @@ my_bool Query_cache::check_integrity(bool locked) } DBUG_ASSERT(result == 0); if (!locked) - STRUCT_UNLOCK(&structure_guard_mutex); + unlock(); DBUG_RETURN(result); } |