summaryrefslogtreecommitdiff
path: root/mysys
diff options
context:
space:
mode:
authorVladislav Vaintroub <vvaintroub@mysql.com>2010-02-16 23:19:47 +0100
committerVladislav Vaintroub <vvaintroub@mysql.com>2010-02-16 23:19:47 +0100
commitd4b10d1fd541505c850c32895d8a1f8308d35c15 (patch)
treebb2aacb96d3588f443a65da32dbfa2b6508efb80 /mysys
parent09dfcfa14bda0d57493a3a15949a8e50a73f6d88 (diff)
parentcbb02487f6b0e95b35e2ee6f3e3242108a75deca (diff)
downloadmariadb-git-d4b10d1fd541505c850c32895d8a1f8308d35c15.tar.gz
merge
Diffstat (limited to 'mysys')
-rw-r--r--mysys/hash.c16
-rw-r--r--mysys/my_static.c13
-rw-r--r--mysys/thr_lock.c148
3 files changed, 132 insertions, 45 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..43db0470735 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -83,7 +83,6 @@ multiple read locks.
my_bool thr_lock_inited=0;
ulong locks_immediate = 0L, locks_waited = 0L;
-ulong table_lock_wait_timeout;
enum thr_lock_type thr_upgraded_concurrent_insert_lock = TL_WRITE;
/* The following constants are only for debug output */
@@ -389,13 +388,13 @@ static void wake_up_waiters(THR_LOCK *lock);
static enum enum_thr_lock_result
wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
- my_bool in_wait_list)
+ my_bool in_wait_list, ulong lock_wait_timeout)
{
struct st_my_thread_var *thread_var= my_thread_var;
mysql_cond_t *cond= &thread_var->suspend;
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,14 +433,13 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
thread_var->current_cond= cond;
data->cond= cond;
- if (can_deadlock)
- set_timespec(wait_timeout, table_lock_wait_timeout);
+ old_proc_info= proc_info_hook(NULL, "Table lock",
+ __func__, __FILE__, __LINE__);
+
+ set_timespec(wait_timeout, lock_wait_timeout);
while (!thread_var->abort || in_wait_list)
{
- int rc= (can_deadlock ?
- mysql_cond_timedwait(cond, &data->lock->mutex,
- &wait_timeout) :
- mysql_cond_wait(cond, &data->lock->mutex));
+ int rc= mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout);
/*
We must break the wait if one of the following occurs:
- the connection has been aborted (!thread_var->abort), but
@@ -504,13 +502,16 @@ 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);
}
enum enum_thr_lock_result
thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
- enum thr_lock_type lock_type)
+ enum thr_lock_type lock_type, ulong lock_wait_timeout)
{
THR_LOCK *lock=data->lock;
enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
@@ -533,13 +534,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",
@@ -547,8 +566,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
(lock->write.data->type <= TL_WRITE_DELAYED &&
(((int) lock_type <= (int) TL_READ_HIGH_PRIORITY) ||
- (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT &&
- lock->write.data->type != TL_WRITE_ALLOW_READ))))
+ (lock->write.data->type != TL_WRITE_CONCURRENT_INSERT))))
{ /* Already got a write lock */
(*lock->read.last)=data; /* Add to running FIFO */
data->prev=lock->read.last;
@@ -631,6 +649,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 +658,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 +667,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 +692,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)));
@@ -745,7 +776,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
goto end;
}
/* Can't get lock yet; Wait for it */
- DBUG_RETURN(wait_for_lock(wait_queue, data, 0));
+ DBUG_RETURN(wait_for_lock(wait_queue, data, 0, lock_wait_timeout));
end:
mysql_mutex_unlock(&lock->mutex);
DBUG_RETURN(result);
@@ -1004,7 +1035,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count)
enum enum_thr_lock_result
-thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner)
+thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner,
+ ulong lock_wait_timeout)
{
THR_LOCK_DATA **pos,**end;
DBUG_ENTER("thr_multi_lock");
@@ -1014,7 +1046,8 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner)
/* lock everything */
for (pos=data,end=data+count; pos < end ; pos++)
{
- enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type);
+ enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type,
+ lock_wait_timeout);
if (result != THR_LOCK_SUCCESS)
{ /* Aborted */
thr_multi_unlock(data,(uint) (pos-data));
@@ -1026,12 +1059,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 +1137,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 */
@@ -1405,7 +1468,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
- enum thr_lock_type new_lock_type)
+ enum thr_lock_type new_lock_type,
+ ulong lock_wait_timeout)
{
THR_LOCK *lock=data->lock;
DBUG_ENTER("thr_upgrade_write_delay_lock");
@@ -1448,13 +1512,14 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
{
check_locks(lock,"waiting for lock",0);
}
- DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1));
+ DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1, lock_wait_timeout));
}
/* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */
-my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data)
+my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data,
+ ulong lock_wait_timeout)
{
THR_LOCK *lock=data->lock;
enum thr_lock_type write_lock_type;
@@ -1486,7 +1551,8 @@ my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data)
free_all_read_locks(lock,0);
mysql_mutex_unlock(&lock->mutex);
- DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type));
+ DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type,
+ lock_wait_timeout));
}