diff options
author | Igor Babaev <igor@askmonty.org> | 2012-06-11 22:12:47 -0700 |
---|---|---|
committer | Igor Babaev <igor@askmonty.org> | 2012-06-11 22:12:47 -0700 |
commit | 7b32d88c05756a37b15e7f11f4cd83fec25f42e2 (patch) | |
tree | d8463d63a4abcc99d560cc1897ae396c54c82d9f /mysys/mf_keycache.c | |
parent | 41d860ef53e4461fa665da063fa34efb947ec4cd (diff) | |
download | mariadb-git-7b32d88c05756a37b15e7f11f4cd83fec25f42e2.tar.gz |
Fixed LP bug #1008293.
One of the reported problems manifested itself in the scenario when one
thread tried to to get statistics on a key cache while the second thread
had not finished initialization of the key cache structure yet.
The problem was resolved by forcing serialization of such operations
on key caches.
To serialize function calls to perform certain operations over a key cache
a new mutex associated with the key cache now is used. It is stored in the
field op_lock of the KEY_CACHE structure. It is locked when the operation
is performed. Some of the serialized key cache operations utilize calls
for other key cache operations. To avoid recursive locking of op_lock
the new functions that perform the operations of key cache initialization,
destruction and re-partitioning with an additional parameter were introduced.
The parameter says whether the operation over op_lock are to be performed or
are to be omitted. The old functions for the operations of key cache
initialization, destruction,and re-partitioning now just call the
corresponding new functions with the additional parameter set to true
requesting to use op_lock while all other calls of these new function
have this parameter set to false.
Another problem reported in the bug entry concerned the operation of
assigning an index to a key cache. This operation can be called
while the key cache structures are not initialized yet. In this
case any call of flush_key_blocks() should return without any actions.
No test case is provided with this patch.
Diffstat (limited to 'mysys/mf_keycache.c')
-rw-r--r-- | mysys/mf_keycache.c | 252 |
1 files changed, 197 insertions, 55 deletions
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index e0d495ce8e0..dc2d7fdd127 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -5863,31 +5863,31 @@ static KEY_CACHE_FUNCS partitioned_key_cache_funcs = ******************************************************************************/ +static +int repartition_key_cache_internal(KEY_CACHE *keycache, + uint key_cache_block_size, size_t use_mem, + uint division_limit, uint age_threshold, + uint partitions, my_bool use_op_lock); /* - Initialize a key cache + Initialize a key cache : internal SYNOPSIS - init_key_cache() + init_key_cache_internal() keycache pointer to the key cache to be initialized key_cache_block_size size of blocks to keep cached data use_mem total memory to use for cache buffers/structures division_limit division limit (may be zero) age_threshold age threshold (may be zero) partitions number of partitions in the key cache + use_op_lock if TRUE use keycache->op_lock, otherwise - ignore it DESCRIPTION - The function creates a control block structure for a key cache and - places the pointer to this block in the structure keycache. - If the value of the parameter 'partitions' is 0 then a simple key cache - is created. Otherwise a partitioned key cache with the specified number - of partitions is created. - The parameter key_cache_block_size specifies the size of the blocks in - the key cache to be created. The parameters division_limit and - age_threshold determine the initial values of those characteristics of - the key cache that are used for midpoint insertion strategy. The parameter - use_mem specifies the total amount of memory to be allocated for the - key cache buffers and for all auxiliary structures. + The function performs the actions required from init_key_cache(). + It has an additional parameter: use_op_lock. When the parameter + is TRUE than the function initializes keycache->op_lock if needed, + then locks it, and unlocks it before the return. Otherwise the actions + with the lock are omitted. RETURN VALUE total number of blocks in key cache partitions, if successful, @@ -5896,19 +5896,22 @@ static KEY_CACHE_FUNCS partitioned_key_cache_funcs = NOTES if keycache->key_cache_inited != 0 we assume that the memory for the control block of the key cache has been already allocated. - - It's assumed that no two threads call this function simultaneously - referring to the same key cache handle. */ -int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, - size_t use_mem, uint division_limit, - uint age_threshold, uint partitions) +static +int init_key_cache_internal(KEY_CACHE *keycache, uint key_cache_block_size, + size_t use_mem, uint division_limit, + uint age_threshold, uint partitions, + my_bool use_op_lock) { void *keycache_cb; int blocks; if (keycache->key_cache_inited) + { + if (use_op_lock) + pthread_mutex_lock(&keycache->op_lock); keycache_cb= keycache->keycache_cb; + } else { if (partitions == 0) @@ -5929,8 +5932,17 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, keycache->key_cache_type= PARTITIONED_KEY_CACHE; keycache->interface_funcs= &partitioned_key_cache_funcs; } + /* + Initialize op_lock if it's not initialized before. + The mutex may have been initialized before if we are being called + from repartition_key_cache_internal(). + */ + if (use_op_lock) + pthread_mutex_init(&keycache->op_lock, MY_MUTEX_INIT_FAST); keycache->keycache_cb= keycache_cb; keycache->key_cache_inited= 1; + if (use_op_lock) + pthread_mutex_lock(&keycache->op_lock); } if (partitions != 0) @@ -5951,11 +5963,58 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, ((SIMPLE_KEY_CACHE_CB *) keycache_cb)->key_cache_mem_size; if (blocks > 0) keycache->can_be_used= 1; + if (use_op_lock) + pthread_mutex_unlock(&keycache->op_lock); return blocks; } /* + Initialize a key cache + + SYNOPSIS + init_key_cache() + keycache pointer to the key cache to be initialized + key_cache_block_size size of blocks to keep cached data + use_mem total memory to use for cache buffers/structures + division_limit division limit (may be zero) + age_threshold age threshold (may be zero) + partitions number of partitions in the key cache + + DESCRIPTION + The function creates a control block structure for a key cache and + places the pointer to this block in the structure keycache. + If the value of the parameter 'partitions' is 0 then a simple key cache + is created. Otherwise a partitioned key cache with the specified number + of partitions is created. + The parameter key_cache_block_size specifies the size of the blocks in + the key cache to be created. The parameters division_limit and + age_threshold determine the initial values of those characteristics of + the key cache that are used for midpoint insertion strategy. The parameter + use_mem specifies the total amount of memory to be allocated for the + key cache buffers and for all auxiliary structures. + The function calls init_key_cache_internal() to perform all these actions + with the last parameter set to TRUE. + + RETURN VALUE + total number of blocks in key cache partitions, if successful, + <= 0 - otherwise. + + NOTES + It's assumed that no two threads call this function simultaneously + referring to the same key cache handle. +*/ + +int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, + size_t use_mem, uint division_limit, + uint age_threshold, uint partitions) +{ + return init_key_cache_internal(keycache, key_cache_block_size, use_mem, + division_limit, age_threshold, partitions, 1); +} + + +/* Resize a key cache SYNOPSIS @@ -5995,11 +6054,13 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, int blocks= -1; if (keycache->key_cache_inited) { + pthread_mutex_lock(&keycache->op_lock); if ((uint) keycache->param_partitions != keycache->partitions && use_mem) - blocks= repartition_key_cache(keycache, - key_cache_block_size, use_mem, - division_limit, age_threshold, - (uint) keycache->param_partitions); + blocks= repartition_key_cache_internal(keycache, + key_cache_block_size, use_mem, + division_limit, age_threshold, + (uint) keycache->param_partitions, + 0); else { blocks= keycache->interface_funcs->resize(keycache->keycache_cb, @@ -6018,6 +6079,7 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, ((SIMPLE_KEY_CACHE_CB *)(keycache->keycache_cb))->key_cache_mem_size; keycache->can_be_used= (blocks >= 0); + pthread_mutex_unlock(&keycache->op_lock); } return blocks; } @@ -6051,33 +6113,37 @@ void change_key_cache_param(KEY_CACHE *keycache, uint division_limit, { if (keycache->key_cache_inited) { - + pthread_mutex_lock(&keycache->op_lock); keycache->interface_funcs->change_param(keycache->keycache_cb, division_limit, - age_threshold); + age_threshold); + pthread_mutex_unlock(&keycache->op_lock); } } /* - Destroy a key cache + Destroy a key cache : internal SYNOPSIS - end_key_cache() + end_key_cache_internal() keycache pointer to the key cache to be destroyed cleanup <=> complete free + use_op_lock if TRUE use keycache->op_lock, otherwise - ignore it DESCRIPTION - The function frees the memory allocated for the cache blocks and - auxiliary structures used by the key cache keycache. If the value - of the parameter cleanup is TRUE then all resources used by the key - cache are to be freed. + The function performs the actions required from end_key_cache(). + It has an additional parameter: use_op_lock. When the parameter + is TRUE than the function destroys keycache->op_lock if cleanup is true. + Otherwise the action with the lock is omitted. RETURN VALUE none */ -void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) +static +void end_key_cache_internal(KEY_CACHE *keycache, my_bool cleanup, + my_bool use_op_lock) { if (keycache->key_cache_inited) { @@ -6089,6 +6155,12 @@ void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) my_free((uchar *) keycache->keycache_cb, MYF(0)); keycache->keycache_cb= 0; } + /* + We do not destroy op_lock if we are going to reuse the same key cache. + This happens if we are called from repartition_key_cache_internal(). + */ + if (use_op_lock) + pthread_mutex_destroy(&keycache->op_lock); keycache->key_cache_inited= 0; } keycache->can_be_used= 0; @@ -6097,6 +6169,32 @@ void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) /* + Destroy a key cache + + SYNOPSIS + end_key_cache() + keycache pointer to the key cache to be destroyed + cleanup <=> complete free + + DESCRIPTION + The function frees the memory allocated for the cache blocks and + auxiliary structures used by the key cache keycache. If the value + of the parameter cleanup is TRUE then all resources used by the key + cache are to be freed. + The function calls end_key_cache_internal() to perform all these actions + with the last parameter set to TRUE. + + RETURN VALUE + none +*/ + +void end_key_cache(KEY_CACHE *keycache, my_bool cleanup) +{ + end_key_cache_internal(keycache, cleanup, 1); +} + + +/* Read a block of data from a key cache into a buffer SYNOPSIS @@ -6140,7 +6238,7 @@ uchar *key_cache_read(KEY_CACHE *keycache, uchar *buff, uint length, uint block_length, int return_buffer) { - if (keycache->key_cache_inited && keycache->can_be_used) + if (keycache->can_be_used) return keycache->interface_funcs->read(keycache->keycache_cb, file, filepos, level, buff, length, @@ -6192,7 +6290,7 @@ int key_cache_insert(KEY_CACHE *keycache, File file, my_off_t filepos, int level, uchar *buff, uint length) { - if (keycache->key_cache_inited && keycache->can_be_used) + if (keycache->can_be_used) return keycache->interface_funcs->insert(keycache->keycache_cb, file, filepos, level, buff, length); @@ -6247,7 +6345,7 @@ int key_cache_write(KEY_CACHE *keycache, uchar *buff, uint length, uint block_length, int force_write) { - if (keycache->key_cache_inited && keycache->can_be_used) + if (keycache->can_be_used) return keycache->interface_funcs->write(keycache->keycache_cb, file, file_extra, filepos, level, @@ -6299,7 +6397,7 @@ int flush_key_blocks(KEY_CACHE *keycache, int file, void *file_extra, enum flush_type type) { - if (keycache->key_cache_inited) + if (keycache->can_be_used) return keycache->interface_funcs->flush(keycache->keycache_cb, file, file_extra, type); return 0; @@ -6330,13 +6428,15 @@ int flush_key_blocks(KEY_CACHE *keycache, int reset_key_cache_counters(const char *name __attribute__((unused)), KEY_CACHE *keycache) { + int rc= 0; if (keycache->key_cache_inited) { - - return keycache->interface_funcs->reset_counters(name, - keycache->keycache_cb); + pthread_mutex_lock(&keycache->op_lock); + rc= keycache->interface_funcs->reset_counters(name, + keycache->keycache_cb); + pthread_mutex_unlock(&keycache->op_lock); } - return 0; + return rc; } @@ -6366,11 +6466,63 @@ void get_key_cache_statistics(KEY_CACHE *keycache, uint partition_no, { if (keycache->key_cache_inited) { + pthread_mutex_lock(&keycache->op_lock); keycache->interface_funcs->get_stats(keycache->keycache_cb, partition_no, key_cache_stats); + pthread_mutex_unlock(&keycache->op_lock); } } + +/* + Repartition a key cache : internal + + SYNOPSIS + repartition_key_cache_internal() + keycache pointer to the key cache to be repartitioned + key_cache_block_size size of blocks to keep cached data + use_mem total memory to use for the new key cache + division_limit new division limit (if not zero) + age_threshold new age threshold (if not zero) + partitions new number of partitions in the key cache + use_op_lock if TRUE use keycache->op_lock, otherwise - ignore it + + DESCRIPTION + The function performs the actions required from repartition_key_cache(). + It has an additional parameter: use_op_lock. When the parameter + is TRUE then the function locks keycache->op_lock at start and + unlocks it before the return. Otherwise the actions with the lock + are omitted. + + RETURN VALUE + number of blocks in the key cache, if successful, + 0 - otherwise. +*/ + +static +int repartition_key_cache_internal(KEY_CACHE *keycache, + uint key_cache_block_size, size_t use_mem, + uint division_limit, uint age_threshold, + uint partitions, my_bool use_op_lock) +{ + uint blocks= -1; + if (keycache->key_cache_inited) + { + if (use_op_lock) + pthread_mutex_lock(&keycache->op_lock); + keycache->interface_funcs->resize(keycache->keycache_cb, + key_cache_block_size, 0, + division_limit, age_threshold); + end_key_cache_internal(keycache, 1, 0); + blocks= init_key_cache_internal(keycache, key_cache_block_size, use_mem, + division_limit, age_threshold, partitions, + 0); + if (use_op_lock) + pthread_mutex_unlock(&keycache->op_lock); + } + return blocks; +} + /* Repartition a key cache @@ -6394,16 +6546,14 @@ void get_key_cache_statistics(KEY_CACHE *keycache, uint partition_no, that are used for midpoint insertion strategy. The parameter use_mem specifies the total amount of memory to be allocated for the new key cache buffers and for all auxiliary structures. + The function calls repartition_key_cache_internal() to perform all these + actions with the last parameter set to TRUE. RETURN VALUE number of blocks in the key cache, if successful, 0 - otherwise. NOTES - The function does not block the calls and executions of other functions - from the key cache interface. However it assumes that the calls of - resize_key_cache itself are serialized. - Currently the function is called when the value of the variable key_cache_partitions is being reset for the key cache keycache. */ @@ -6412,16 +6562,8 @@ int repartition_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, size_t use_mem, uint division_limit, uint age_threshold, uint partitions) { - uint blocks= -1; - if (keycache->key_cache_inited) - { - keycache->interface_funcs->resize(keycache->keycache_cb, - key_cache_block_size, 0, - division_limit, age_threshold); - end_key_cache(keycache, 1); - blocks= init_key_cache(keycache, key_cache_block_size, use_mem, - division_limit, age_threshold, partitions); - } - return blocks; + return repartition_key_cache_internal(keycache, key_cache_block_size, use_mem, + division_limit, age_threshold, + partitions, 1); } |