diff options
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/hash.c | 16 | ||||
-rw-r--r-- | mysys/my_static.c | 13 | ||||
-rw-r--r-- | mysys/thr_lock.c | 112 |
3 files changed, 115 insertions, 26 deletions
diff --git a/mysys/hash.c b/mysys/hash.c index 5fa804ce7ce..39f3ad8d31e 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -232,6 +232,8 @@ my_hash_value_type my_calc_hash(const HASH *hash, { return calc_hash(hash, key, length ? length : hash->key_length); } + + /* Search after a record based on a key @@ -242,11 +244,17 @@ my_hash_value_type my_calc_hash(const HASH *hash, uchar* my_hash_first(const HASH *hash, const uchar *key, size_t length, HASH_SEARCH_STATE *current_record) { - return my_hash_first_from_hash_value(hash, - calc_hash(hash, key, length ? length : hash->key_length), - key, length, current_record); + uchar *res; + if (my_hash_inited(hash)) + res= my_hash_first_from_hash_value(hash, + calc_hash(hash, key, length ? length : hash->key_length), + key, length, current_record); + else + res= 0; + return res; } - + + uchar* my_hash_first_from_hash_value(const HASH *hash, my_hash_value_type hash_value, const uchar *key, diff --git a/mysys/my_static.c b/mysys/my_static.c index fb62e92dfd1..a86fe6c7ab7 100644 --- a/mysys/my_static.c +++ b/mysys/my_static.c @@ -92,6 +92,19 @@ void (*error_handler_hook)(uint error,const char *str,myf MyFlags)= void (*fatal_error_handler_hook)(uint error,const char *str,myf MyFlags)= my_message_no_curses; +static const char *proc_info_dummy(void *a __attribute__((unused)), + const char *b __attribute__((unused)), + const char *c __attribute__((unused)), + const char *d __attribute__((unused)), + const unsigned int e __attribute__((unused))) +{ + return 0; +} + +/* this is to be able to call set_thd_proc_info from the C code */ +const char *(*proc_info_hook)(void *, const char *, const char *, const char *, + const unsigned int)= proc_info_dummy; + #if defined(ENABLED_DEBUG_SYNC) /** Global pointer to be set if callback function is defined diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 5a71e58cdbf..6d62476ecf3 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, struct timespec wait_timeout; enum enum_thr_lock_result result= THR_LOCK_ABORTED; my_bool can_deadlock= test(data->owner->info->n_cursors); + const char *old_proc_info; DBUG_ENTER("wait_for_lock"); /* @@ -434,6 +435,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, thread_var->current_cond= cond; data->cond= cond; + old_proc_info= proc_info_hook(NULL, "Table lock", + __func__, __FILE__, __LINE__); + if (can_deadlock) set_timespec(wait_timeout, table_lock_wait_timeout); while (!thread_var->abort || in_wait_list) @@ -504,6 +508,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, thread_var->current_mutex= 0; thread_var->current_cond= 0; mysql_mutex_unlock(&thread_var->mutex); + + proc_info_hook(NULL, old_proc_info, __func__, __FILE__, __LINE__); + DBUG_RETURN(result); } @@ -533,13 +540,31 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, /* Request for READ lock */ if (lock->write.data) { - /* We can allow a read lock even if there is already a write lock - on the table in one the following cases: - - This thread alread have a write lock on the table - - The write lock is TL_WRITE_ALLOW_READ or TL_WRITE_DELAYED - and the read lock is TL_READ_HIGH_PRIORITY or TL_READ - - The write lock is TL_WRITE_CONCURRENT_INSERT or TL_WRITE_ALLOW_WRITE - and the read lock is not TL_READ_NO_INSERT + /* + We can allow a read lock even if there is already a + write lock on the table if they are owned by the same + thread or if they satisfy the following lock + compatibility matrix: + + Request + /------- + H|++++ WRITE_ALLOW_WRITE + e|+++- WRITE_ALLOW_READ + l|+++- WRITE_CONCURRENT_INSERT + d|++++ WRITE_DELAYED + |||| + |||\= READ_NO_INSERT + ||\ = READ_HIGH_PRIORITY + |\ = READ_WITH_SHARED_LOCKS + \ = READ + + + = Request can be satisified. + - = Request cannot be satisified. + + READ_NO_INSERT and WRITE_ALLOW_WRITE should in principle + be incompatible. However this will cause starvation of + LOCK TABLE READ in InnoDB under high write load. + See Bug#42147 for more information. */ DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx", @@ -631,6 +656,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, { if (lock->write.data->type == TL_WRITE_ONLY) { + /* purecov: begin tested */ /* Allow lock owner to bypass TL_WRITE_ONLY. */ if (!thr_lock_owner_equal(data->owner, lock->write.data->owner)) { @@ -639,6 +665,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, result= THR_LOCK_ABORTED; /* Can't wait for this one */ goto end; } + /* purecov: end */ } /* @@ -647,14 +674,23 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, write locks are of TL_WRITE_ALLOW_WRITE type. Note that, since lock requests for the same table are sorted in - such way that requests with higher thr_lock_type value come first, - lock being requested usually has equal or "weaker" type than one - which thread might have already acquired. - The exceptions are situations when: - - old lock type is TL_WRITE_ALLOW_READ and new lock type is - TL_WRITE_ALLOW_WRITE - - when old lock type is TL_WRITE_DELAYED - But these should never happen within MySQL. + such way that requests with higher thr_lock_type value come first + (with one exception (*)), lock being requested usually (**) has + equal or "weaker" type than one which thread might have already + acquired. + *) The only exception to this rule is case when type of old lock + is TL_WRITE_LOW_PRIORITY and type of new lock is changed inside + of thr_lock() from TL_WRITE_CONCURRENT_INSERT to TL_WRITE since + engine turns out to be not supporting concurrent inserts. + Note that since TL_WRITE has the same compatibility rules as + TL_WRITE_LOW_PRIORITY (their only difference is priority), + it is OK to grant new lock without additional checks in such + situation. + **) The exceptions are situations when: + - old lock type is TL_WRITE_ALLOW_READ and new lock type is + TL_WRITE_ALLOW_WRITE + - when old lock type is TL_WRITE_DELAYED + But these should never happen within MySQL. Therefore it is OK to allow acquiring write lock on the table if this thread already holds some write lock on it. @@ -663,7 +699,9 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, different types of write lock on the same table). */ DBUG_ASSERT(! has_old_lock(lock->write.data, data->owner) || - (lock_type <= lock->write.data->type && + ((lock_type <= lock->write.data->type || + (lock_type == TL_WRITE && + lock->write.data->type == TL_WRITE_LOW_PRIORITY)) && ! ((lock_type < TL_WRITE_ALLOW_READ && lock->write.data->type == TL_WRITE_ALLOW_READ) || lock->write.data->type == TL_WRITE_DELAYED))); @@ -1026,12 +1064,43 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) (long) pos[0]->lock, pos[0]->type); fflush(stdout); #endif } - /* - Ensure that all get_locks() have the same status - If we lock the same table multiple times, we must use the same - status_param! - */ + thr_lock_merge_status(data, count); + DBUG_RETURN(THR_LOCK_SUCCESS); +} + + +/** + Ensure that all locks for a given table have the same + status_param. + + This is a MyISAM and possibly Maria specific crutch. MyISAM + engine stores data file length, record count and other table + properties in status_param member of handler. When a table is + locked, connection-local copy is made from a global copy + (myisam_share) by mi_get_status(). When a table is unlocked, + the changed status is transferred back to the global share by + mi_update_status(). + + One thing MyISAM doesn't do is to ensure that when the same + table is opened twice in a connection all instances share the + same status_param. This is necessary, however: for one, to keep + all instances of a connection "on the same page" with regard to + the current state of the table. For other, unless this is done, + myisam_share will always get updated from the last unlocked + instance (in mi_update_status()), and when this instance was not + the one that was used to update data, records may be lost. + + For each table, this function looks up the last lock_data in the + list of acquired locks, and makes sure that all other instances + share status_param with it. +*/ + +void +thr_lock_merge_status(THR_LOCK_DATA **data, uint count) +{ #if !defined(DONT_USE_RW_LOCKS) + THR_LOCK_DATA **pos= data; + THR_LOCK_DATA **end= data + count; if (count > 1) { THR_LOCK_DATA *last_lock= end[-1]; @@ -1073,7 +1142,6 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) } while (pos != data); } #endif - DBUG_RETURN(THR_LOCK_SUCCESS); } /* free all locks */ |