summaryrefslogtreecommitdiff
path: root/mysys/thr_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'mysys/thr_lock.c')
-rw-r--r--mysys/thr_lock.c129
1 files changed, 87 insertions, 42 deletions
diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c
index bc992a3e35b..b2e51cde260 100644
--- a/mysys/thr_lock.c
+++ b/mysys/thr_lock.c
@@ -116,6 +116,31 @@ static inline mysql_cond_t *get_cond(void)
return &my_thread_var->suspend;
}
+
+/*
+ Sort locks in priority order
+
+ LOCK_CMP()
+ A First lock
+ B Second lock
+
+ Return:
+ 0 if A >= B
+ 1 if A < B
+
+ Priority for locks (decides in which order locks are locked)
+ We want all write locks to be first, followed by read locks.
+ Locks from MERGE tables has a little lower priority than other
+ locks, to allow one to release merge tables without having
+ to unlock and re-lock other locks.
+ The lower the number, the higher the priority for the lock.
+ For MERGE tables we add 2 (THR_LOCK_MERGE_PRIV) to the lock priority.
+ THR_LOCK_LATE_PRIV (1) is used when one locks other tables to be merged
+ with existing locks. This way we prioritize the original locks over the
+ new locks.
+*/
+
+
static inline int LOCK_CMP(THR_LOCK_DATA *a, THR_LOCK_DATA *b)
{
if (a->lock != b->lock)
@@ -154,15 +179,13 @@ static int check_lock(struct st_lock_list *list, const char* lock_type,
{
THR_LOCK_DATA *data,**prev;
uint count=0;
- THR_LOCK_INFO *UNINIT_VAR(first_owner);
prev= &list->data;
if (list->data)
{
- enum thr_lock_type last_lock_type=list->data->type;
+ enum thr_lock_type last_lock_type= list->data->type;
+ THR_LOCK_INFO *first_owner= list->data->owner;
- if (same_owner && list->data)
- first_owner= list->data->owner;
for (data=list->data; data && count++ < MAX_LOCKS ; data=data->next)
{
if (data->type != last_lock_type)
@@ -180,8 +203,10 @@ static int check_lock(struct st_lock_list *list, const char* lock_type,
last_lock_type != TL_WRITE_CONCURRENT_INSERT)
{
fprintf(stderr,
- "Warning: Found locks from different threads in %s: %s\n",
- lock_type,where);
+ "Warning: Found locks from different threads for lock '%s' in '%s' at '%s'. org_lock_type: %d last_lock_type: %d new_lock_type: %d\n",
+ data->lock->name ? data->lock->name : "",
+ lock_type, where, list->data->type, last_lock_type,
+ data->type);
return 1;
}
if (no_cond && data->cond)
@@ -211,6 +236,7 @@ static int check_lock(struct st_lock_list *list, const char* lock_type,
static void check_locks(THR_LOCK *lock, const char *where,
+ enum thr_lock_type type,
my_bool allow_no_locks)
{
uint old_found_errors=found_errors;
@@ -226,14 +252,16 @@ static void check_locks(THR_LOCK *lock, const char *where,
if (found_errors < MAX_FOUND_ERRORS)
{
- uint count=0;
+ uint count=0, count2= 0;
THR_LOCK_DATA *data;
for (data=lock->read.data ; data ; data=data->next)
{
+ count2++;
if (data->type == TL_READ_NO_INSERT)
count++;
/* Protect against infinite loop. */
- DBUG_ASSERT(count <= lock->read_no_write_count);
+ DBUG_ASSERT(count <= lock->read_no_write_count &&
+ count2 <= MAX_LOCKS);
}
if (count != lock->read_no_write_count)
{
@@ -274,6 +302,7 @@ static void check_locks(THR_LOCK *lock, const char *where,
found_errors++;
fprintf(stderr,
"Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type);
+ DBUG_PRINT("warning", ("Warning at '%s': Write lock %d waiting while no exclusive read locks\n",where,(int) lock->write_wait.data->type));
}
}
}
@@ -283,13 +312,18 @@ static void check_locks(THR_LOCK *lock, const char *where,
if (lock->write.data->type == TL_WRITE_CONCURRENT_INSERT)
{
THR_LOCK_DATA *data;
- for (data=lock->write.data->next ; data ; data=data->next)
+ uint count= 0;
+ for (data=lock->write.data->next;
+ data && count < MAX_LOCKS;
+ data=data->next)
{
if (data->type != TL_WRITE_CONCURRENT_INSERT)
{
fprintf(stderr,
- "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write locks\n",
- where);
+ "Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n",
+ where, data->type);
+ DBUG_PRINT("warning", ("Warning at '%s': Found TL_WRITE_CONCURRENT_INSERT lock mixed with other write lock: %d\n",
+ where, data->type));
break;
}
}
@@ -304,26 +338,34 @@ static void check_locks(THR_LOCK *lock, const char *where,
fprintf(stderr,
"Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n",
where);
+ DBUG_PRINT("warning", ("Warning at '%s': Found WRITE_ALLOW_WRITE lock waiting for WRITE_ALLOW_WRITE lock\n",
+ where));
+
}
}
if (lock->read.data)
{
- if (!thr_lock_owner_equal(lock->write.data->owner,
- lock->read.data->owner) &&
+ THR_LOCK_DATA *data;
+ for (data=lock->read.data ; data ; data=data->next)
+ {
+ if (!thr_lock_owner_equal(lock->write.data->owner,
+ data->owner) &&
((lock->write.data->type > TL_WRITE_DELAYED &&
lock->write.data->type != TL_WRITE_ONLY) ||
((lock->write.data->type == TL_WRITE_CONCURRENT_INSERT ||
lock->write.data->type == TL_WRITE_ALLOW_WRITE) &&
- lock->read_no_write_count)))
- {
- found_errors++;
- fprintf(stderr,
- "Warning at '%s': Found lock of type %d that is write and read locked\n",
- where, lock->write.data->type);
- DBUG_PRINT("warning",("At '%s': Found lock of type %d that is write and read locked\n",
- where, lock->write.data->type));
-
- }
+ data->type == TL_READ_NO_INSERT)))
+ {
+ found_errors++;
+ fprintf(stderr,
+ "Warning at '%s' for lock: %d: Found lock of type %d that is write and read locked. Read_no_write_count: %d\n",
+ where, (int) type, lock->write.data->type,
+ lock->read_no_write_count);
+ DBUG_PRINT("warning",("At '%s' for lock %d: Found lock of type %d that is write and read locked\n",
+ where, (int) type,
+ lock->write.data->type));
+ }
+ }
}
if (lock->read_wait.data)
{
@@ -349,7 +391,7 @@ static void check_locks(THR_LOCK *lock, const char *where,
}
#else /* EXTRA_DEBUG */
-#define check_locks(A,B,C)
+#define check_locks(A,B,C,D)
#endif
@@ -401,6 +443,7 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
data->status_param=param;
data->cond=0;
data->priority= 0;
+ data->debug_print_param= 0;
}
@@ -478,7 +521,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
data->cond= cond;
old_proc_info= proc_info_hook(NULL, "Waiting for table level lock",
- __func__, __FILE__, __LINE__);
+ __func__, __FILE__, __LINE__);
/*
Since before_lock_wait potentially can create more threads to
@@ -544,13 +587,14 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
else
wait->last=data->prev;
data->type= TL_UNLOCK; /* No lock */
- check_locks(data->lock, "killed or timed out wait_for_lock", 1);
+ check_locks(data->lock, "killed or timed out wait_for_lock", data->type,
+ 1);
wake_up_waiters(data->lock);
}
else
{
DBUG_PRINT("thr_lock", ("lock aborted"));
- check_locks(data->lock, "aborted wait_for_lock", 0);
+ check_locks(data->lock, "aborted wait_for_lock", data->type, 0);
}
}
else
@@ -559,7 +603,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
if (data->lock->get_status)
(*data->lock->get_status)(data->status_param,
data->type == TL_WRITE_CONCURRENT_INSERT);
- check_locks(data->lock,"got wait_for_lock",0);
+ check_locks(data->lock,"got wait_for_lock", data->type, 0);
}
mysql_mutex_unlock(&data->lock->mutex);
@@ -594,7 +638,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
(long) data, data->owner->thread_id,
(long) lock, (int) lock_type));
check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ?
- "enter read_lock" : "enter write_lock",0);
+ "enter read_lock" : "enter write_lock", lock_type, 0);
if ((int) lock_type <= (int) TL_READ_NO_INSERT)
{
/* Request for READ lock */
@@ -639,7 +683,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
lock->read.last= &data->next;
if (lock_type == TL_READ_NO_INSERT)
lock->read_no_write_count++;
- check_locks(lock,"read lock with old write lock",0);
+ check_locks(lock,"read lock with old write lock", lock_type, 0);
if (lock->get_status)
(*lock->get_status)(data->status_param, 0);
statistic_increment(locks_immediate,&THR_LOCK_lock);
@@ -663,7 +707,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
lock->read.last= &data->next;
if (lock_type == TL_READ_NO_INSERT)
lock->read_no_write_count++;
- check_locks(lock,"read lock with no write locks",0);
+ check_locks(lock,"read lock with no write locks", lock_type, 0);
if (lock->get_status)
(*lock->get_status)(data->status_param, 0);
statistic_increment(locks_immediate,&THR_LOCK_lock);
@@ -769,7 +813,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
(*lock->write.last)=data; /* Add to running fifo */
data->prev=lock->write.last;
lock->write.last= &data->next;
- check_locks(lock,"second write lock",0);
+ check_locks(lock,"second write lock", lock_type, 0);
if (lock->get_status)
(*lock->get_status)(data->status_param,
lock_type == TL_WRITE_CONCURRENT_INSERT);
@@ -807,7 +851,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_INFO *owner,
lock->write.last= &data->next;
if (lock->get_status)
(*lock->get_status)(data->status_param, concurrent_insert);
- check_locks(lock,"only write lock",0);
+ check_locks(lock,"only write lock", lock_type, 0);
statistic_increment(locks_immediate,&THR_LOCK_lock);
goto end;
}
@@ -830,7 +874,7 @@ static inline void free_all_read_locks(THR_LOCK *lock,
{
THR_LOCK_DATA *data=lock->read_wait.data;
- check_locks(lock,"before freeing read locks",1);
+ check_locks(lock,"before freeing read locks", TL_UNLOCK, 1);
/* move all locks from read_wait list to read list */
(*lock->read.last)=data;
@@ -872,7 +916,7 @@ static inline void free_all_read_locks(THR_LOCK *lock,
*lock->read_wait.last=0;
if (!lock->read_wait.data)
lock->write_lock_count=0;
- check_locks(lock,"after giving read locks",0);
+ check_locks(lock,"after giving read locks", TL_UNLOCK, 0);
}
/* Unlock lock and free next thread on same lock */
@@ -885,7 +929,7 @@ void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags)
DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx",
(long) data, data->owner->thread_id, (long) lock));
mysql_mutex_lock(&lock->mutex);
- check_locks(lock,"start of release lock",0);
+ check_locks(lock,"start of release lock", lock_type, 0);
if (((*data->prev)=data->next)) /* remove from lock-list */
data->next->prev= data->prev;
@@ -919,8 +963,9 @@ void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags)
if (lock_type == TL_READ_NO_INSERT)
lock->read_no_write_count--;
data->type=TL_UNLOCK; /* Mark unlocked */
- check_locks(lock,"after releasing lock",1);
+ check_locks(lock,"after releasing lock", lock_type, 1);
wake_up_waiters(lock);
+ check_locks(lock,"end of thr_unlock", lock_type, 1);
mysql_mutex_unlock(&lock->mutex);
DBUG_VOID_RETURN;
}
@@ -1045,7 +1090,7 @@ static void wake_up_waiters(THR_LOCK *lock)
free_all_read_locks(lock,0);
}
end:
- check_locks(lock, "after waking up waiters", 0);
+ check_locks(lock, "after waking up waiters", TL_UNLOCK, 0);
DBUG_VOID_RETURN;
}
@@ -1350,7 +1395,7 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
DBUG_ASSERT(old_lock_type == TL_WRITE_ONLY);
DBUG_ASSERT(old_lock_type > new_lock_type);
in_data->type= new_lock_type;
- check_locks(lock,"after downgrading lock",0);
+ check_locks(lock,"after downgrading lock", old_lock_type, 0);
mysql_mutex_unlock(&lock->mutex);
DBUG_VOID_RETURN;
@@ -1372,7 +1417,7 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
mysql_mutex_unlock(&lock->mutex);
DBUG_RETURN(data->type == TL_UNLOCK); /* Test if Aborted */
}
- check_locks(lock,"before upgrading lock",0);
+ check_locks(lock,"before upgrading lock", data->type, 0);
/* TODO: Upgrade to TL_WRITE_CONCURRENT_INSERT in some cases */
data->type= new_lock_type; /* Upgrade lock */
@@ -1400,11 +1445,11 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
lock->write_wait.last= &data->next;
data->prev= &lock->write_wait.data;
lock->write_wait.data=data;
- check_locks(lock,"upgrading lock",0);
+ check_locks(lock,"upgrading lock", new_lock_type, 0);
}
else
{
- check_locks(lock,"waiting for lock",0);
+ check_locks(lock,"waiting for lock", new_lock_type, 0);
}
res= wait_for_lock(&lock->write_wait, data, 1, lock_wait_timeout);
if (res == THR_LOCK_SUCCESS && lock->start_trans)