diff options
-rwxr-xr-x | mysql-test/mysql-test-run.pl | 2 | ||||
-rw-r--r-- | mysql-test/r/lock.result | 10 | ||||
-rw-r--r-- | mysql-test/t/lock.test | 25 | ||||
-rw-r--r-- | mysys/thr_lock.c | 112 |
4 files changed, 106 insertions, 43 deletions
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 542f1ca9660..e2b2a3c2262 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4397,7 +4397,7 @@ sub extract_warning_lines ($$) { my @patterns = ( - qr/^Warning:|mysqld: Warning|\[Warning\]/, + qr/^Warning|mysqld: Warning|\[Warning\]/, qr/^Error:|\[ERROR\]/, qr/^==\d+==\s+\S/, # valgrind errors qr/InnoDB: Warning|InnoDB: Error/, diff --git a/mysql-test/r/lock.result b/mysql-test/r/lock.result index 7ec07fb5273..1828a5f11e0 100644 --- a/mysql-test/r/lock.result +++ b/mysql-test/r/lock.result @@ -194,3 +194,13 @@ ERROR HY000: Table 't2' was locked with a READ lock and can't be updated UNLOCK TABLES; DROP TABLE t1,t2; End of 5.1 tests. +create table t1 (a int) engine=myisam; +lock tables t1 write concurrent, t1 as t2 read; +lock tables t1 read local; +unlock tables; +unlock tables; +lock tables t1 read local; +lock tables t1 write concurrent, t1 as t2 read; +unlock tables; +unlock tables; +drop table t1; diff --git a/mysql-test/t/lock.test b/mysql-test/t/lock.test index 30f4d4d6c61..fdacb9c57d4 100644 --- a/mysql-test/t/lock.test +++ b/mysql-test/t/lock.test @@ -245,3 +245,28 @@ UNLOCK TABLES; DROP TABLE t1,t2; --echo End of 5.1 tests. + +# +# Test concurrent lock and read locks +# This gave a warning: +# Warning at 'read lock with old write lock' for lock: 5: +# Found lock of type 8 that is write and read locked. Read_no_write_count: 1 +# +create table t1 (a int) engine=myisam; +lock tables t1 write concurrent, t1 as t2 read; +connect (con1,localhost,root,,); +connection con1; +lock tables t1 read local; +unlock tables; +connection default; +unlock tables; +connection con1; +lock tables t1 read local; +connection default; +lock tables t1 write concurrent, t1 as t2 read; +unlock tables; +connection con1; +unlock tables; +disconnect con1; +connection default; +drop table t1; diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 2c008a8817a..ac70282c050 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -112,25 +112,37 @@ static inline pthread_cond_t *get_cond(void) /* + 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. - Read locks should have 4, write locks should have 0. - UNLOCK is 8, to force these last in thr_merge_locks. 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 uint lock_priority[(uint)TL_WRITE_ONLY+1] = -{ 8, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}; - -#define LOCK_CMP(A,B) ((uchar*) ((A)->lock) + lock_priority[(uint) (A)->type] + (A)->priority < (uchar*) ((B)->lock) + lock_priority[(uint) (B)->type] + (B)->priority) +inline int LOCK_CMP(THR_LOCK_DATA *A, THR_LOCK_DATA *B) +{ + if (A->lock != B->lock) + return A->lock < B->lock; + if (A->type != B->type) + return A->type > B->type; /* Prioritize read */ + return A->priority < B->priority; +} /* For the future (now the thread specific cond is alloced by my_pthread.c) @@ -215,6 +227,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; @@ -281,6 +294,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)); } } } @@ -298,8 +312,10 @@ static void check_locks(THR_LOCK *lock, const char *where, 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; } } @@ -314,26 +330,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) { @@ -359,7 +383,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 @@ -536,13 +560,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 @@ -551,7 +576,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); } pthread_mutex_unlock(&data->lock->mutex); @@ -584,7 +609,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, (long) data, data->owner->info->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 */ @@ -613,7 +638,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *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); @@ -637,7 +662,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *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); @@ -722,7 +747,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *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); @@ -760,7 +785,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *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; } @@ -796,7 +821,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; @@ -838,7 +863,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 */ @@ -851,7 +876,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->info->thread_id, (long) lock)); pthread_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; @@ -885,9 +910,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",1); + check_locks(lock,"end of thr_unlock", lock_type, 1); pthread_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; } @@ -1012,7 +1037,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; } @@ -1322,7 +1347,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); #if TO_BE_REMOVED switch (old_lock_type) @@ -1422,7 +1447,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data, data->prev= lock->write.last; lock->write.last= &data->next; data->next= 0; - check_locks(lock, "Started write lock after downgrade",0); + check_locks(lock, "Started write lock after downgrade", new_lock_type, + 0); data->cond= 0; pthread_cond_signal(cond); } @@ -1475,13 +1501,15 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data, if (data->type == TL_READ_NO_INSERT) lock->read_no_write_count++; - check_locks(lock, "Started read lock after downgrade",0); + check_locks(lock, "Started read lock after downgrade", new_lock_type, + 0); data->cond= 0; pthread_cond_signal(cond); } } } - check_locks(lock,"after starting waiters after downgrading lock",0); + check_locks(lock,"after starting waiters after downgrading lock", + new_lock_type, 0); #endif pthread_mutex_unlock(&lock->mutex); DBUG_VOID_RETURN; @@ -1502,7 +1530,7 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, pthread_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 */ @@ -1530,11 +1558,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); if (res == THR_LOCK_SUCCESS && lock->start_trans) |