Commit c44665ae authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

------------------------------------------------------------
revno: 3035.4.1
committer: Davi Arnaut <Davi.Arnaut@Sun.COM>
branch nick: 39897-6.0
timestamp: Thu 2009-01-15 12:17:57 -0200
message:
Bug#39897: lock_multi fails in pushbuild: timeout waiting for processlist

The problem is that relying on the "Table lock" thread state in
its current position to detect that a thread is waiting on a lock
is race prone. The "Table lock" state change happens before the
thread actually tries to grab a lock on a table.

The solution is to move the "Table lock" state so that its set
only when a thread is actually going to wait for a lock. The state
change happens after the thread fails to grab the lock (because it
is owned by other thread) and proceeds to wait on a condition.

This is considered part of work related to WL#4284 "Transactional
DDL locking"
Warning: this patch contains an incompatible change. 
When waiting on a lock in thr_lock.c, the server used to display "Locked"
processlist state. After this patch, the state is "Table lock".
The new state was actually intended to be display since year 2002,
when Monty added it. But up until removal of thd->locked boolean
member, this state was ignored by SHOW PROCESSLIST code.  
parent 36897c86
...@@ -203,7 +203,7 @@ drop table if exists t1,t2; ...@@ -203,7 +203,7 @@ drop table if exists t1,t2;
create table t1 (a int); create table t1 (a int);
flush status; flush status;
lock tables t1 read; lock tables t1 read;
insert into t1 values(1);; insert into t1 values(1);
unlock tables; unlock tables;
drop table t1; drop table t1;
select @tlwa < @tlwb; select @tlwa < @tlwb;
......
...@@ -35,7 +35,7 @@ call bug9486(); ...@@ -35,7 +35,7 @@ call bug9486();
show processlist; show processlist;
Id User Host db Command Time State Info Id User Host db Command Time State Info
# root localhost test Sleep # NULL # root localhost test Sleep # NULL
# root localhost test Query # Locked update t1, t2 set val= 1 where id1=id2 # root localhost test Query # Table lock update t1, t2 set val= 1 where id1=id2
# root localhost test Query # NULL show processlist # root localhost test Query # NULL show processlist
# root localhost test Sleep # NULL # root localhost test Sleep # NULL
unlock tables; unlock tables;
......
...@@ -307,7 +307,7 @@ connection update; ...@@ -307,7 +307,7 @@ connection update;
connection default; connection default;
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where command = "Delayed insert" and state = "upgrading lock"; where command = "Delayed insert" and state = "Table lock";
--source include/wait_condition.inc --source include/wait_condition.inc
connect (select,localhost,root,,); connect (select,localhost,root,,);
--echo connection: select --echo connection: select
......
...@@ -174,7 +174,7 @@ connection default; ...@@ -174,7 +174,7 @@ connection default;
# we must wait till the insert opens and locks the table # we must wait till the insert opens and locks the table
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and id = $ID; where state = "Table lock" and id = $ID;
--source include/wait_condition.inc --source include/wait_condition.inc
connect (select,localhost,root,,); connect (select,localhost,root,,);
--echo connection: select --echo connection: select
......
...@@ -22,7 +22,7 @@ connection reader; ...@@ -22,7 +22,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs # Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update low_priority t1 set n = 4"; where state = "Table lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc --source include/wait_condition.inc
send send
select n from t1; select n from t1;
...@@ -30,7 +30,7 @@ connection locker; ...@@ -30,7 +30,7 @@ connection locker;
# Sleep a bit till the select of connection reader is in work and hangs # Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "select n from t1"; where state = "Table lock" and info = "select n from t1";
--source include/wait_condition.inc --source include/wait_condition.inc
unlock tables; unlock tables;
connection writer; connection writer;
...@@ -50,7 +50,7 @@ connection reader; ...@@ -50,7 +50,7 @@ connection reader;
# Sleep a bit till the update of connection writer is in work and hangs # Sleep a bit till the update of connection writer is in work and hangs
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update low_priority t1 set n = 4"; where state = "Table lock" and info = "update low_priority t1 set n = 4";
--source include/wait_condition.inc --source include/wait_condition.inc
select n from t1; select n from t1;
connection locker; connection locker;
...@@ -95,7 +95,7 @@ insert t1 select * from t2; ...@@ -95,7 +95,7 @@ insert t1 select * from t2;
connection locker; connection locker;
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert t1 select * from t2"; where state = "Table lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc --source include/wait_condition.inc
drop table t2; drop table t2;
connection reader; connection reader;
...@@ -119,7 +119,7 @@ connection locker; ...@@ -119,7 +119,7 @@ connection locker;
# Sleep a bit till the insert of connection reader is in work and hangs # Sleep a bit till the insert of connection reader is in work and hangs
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert t1 select * from t2"; where state = "Table lock" and info = "insert t1 select * from t2";
--source include/wait_condition.inc --source include/wait_condition.inc
drop table t2; drop table t2;
connection reader; connection reader;
...@@ -163,8 +163,8 @@ SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; ...@@ -163,8 +163,8 @@ SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1;
connection locker; connection locker;
# Sleep a bit till the select of connection reader is in work and hangs # Sleep a bit till the select of connection reader is in work and hangs
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist SELECT COUNT(*) = 1 FROM information_schema.processlist
where state = "Locked" and info = WHERE state = "Table lock" AND info =
"SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1"; "SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1";
--source include/wait_condition.inc --source include/wait_condition.inc
# Make test case independent from earlier grants. # Make test case independent from earlier grants.
...@@ -298,7 +298,7 @@ connection reader; ...@@ -298,7 +298,7 @@ connection reader;
# Wait till connection writer is blocked # Wait till connection writer is blocked
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "alter table t1 auto_increment=0"; where state = "Table lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc --source include/wait_condition.inc
send send
alter table t1 auto_increment=0; alter table t1 auto_increment=0;
...@@ -306,7 +306,7 @@ connection locker; ...@@ -306,7 +306,7 @@ connection locker;
# Wait till connection reader is blocked # Wait till connection reader is blocked
let $wait_condition= let $wait_condition=
select count(*) = 2 from information_schema.processlist select count(*) = 2 from information_schema.processlist
where state = "Locked" and info = "alter table t1 auto_increment=0"; where state = "Table lock" and info = "alter table t1 auto_increment=0";
--source include/wait_condition.inc --source include/wait_condition.inc
unlock tables; unlock tables;
connection writer; connection writer;
...@@ -461,16 +461,16 @@ update t1 set i= 10; ...@@ -461,16 +461,16 @@ update t1 set i= 10;
connection reader; connection reader;
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "update t1 set i= 10"; where state = "Table lock" and info = "update t1 set i= 10";
--source include/wait_condition.inc --source include/wait_condition.inc
send send
select * from t1; select * from t1;
connection default; connection default;
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "select * from t1"; where state = "Table lock" and info = "select * from t1";
--source include/wait_condition.inc --source include/wait_condition.inc
let $ID= `select id from information_schema.processlist where state = "Locked" and info = "update t1 set i= 10"`; let $ID= `select id from information_schema.processlist where state = "Table lock" and info = "update t1 set i= 10"`;
--replace_result $ID ID --replace_result $ID ID
eval kill query $ID; eval kill query $ID;
connection reader; connection reader;
...@@ -613,11 +613,11 @@ lock tables t1 read; ...@@ -613,11 +613,11 @@ lock tables t1 read;
let $tlwa= `show status like 'Table_locks_waited'`; let $tlwa= `show status like 'Table_locks_waited'`;
connect (waiter,localhost,root,,); connect (waiter,localhost,root,,);
connection waiter; connection waiter;
--send insert into t1 values(1); send insert into t1 values(1);
connection default; connection default;
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and info = "insert into t1 values(1)"; where state = "Table lock" and info = "insert into t1 values(1)";
--source include/wait_condition.inc --source include/wait_condition.inc
let $tlwb= `show status like 'Table_locks_waited'`; let $tlwb= `show status like 'Table_locks_waited'`;
unlock tables; unlock tables;
......
...@@ -71,7 +71,7 @@ set debug_sync= 'now WAIT_FOR parked'; ...@@ -71,7 +71,7 @@ set debug_sync= 'now WAIT_FOR parked';
connection default; connection default;
--echo # Wait until this LOCK TABLES statement starts waiting for table lock. --echo # Wait until this LOCK TABLES statement starts waiting for table lock.
let $wait_condition= select count(*)= 1 from information_schema.processlist let $wait_condition= select count(*)= 1 from information_schema.processlist
where state= 'Locked' and where state= 'Table lock' and
info='lock table t1 write'; info='lock table t1 write';
--source include/wait_condition.inc --source include/wait_condition.inc
--echo # Allow SELECT ... FOR UPDATE to resume. --echo # Allow SELECT ... FOR UPDATE to resume.
......
...@@ -496,7 +496,7 @@ connection updater; ...@@ -496,7 +496,7 @@ connection updater;
# Wait till "alter table t1 ..." of session changer is in work. # Wait till "alter table t1 ..." of session changer is in work.
# = There is one session is in state "Locked". # = There is one session is in state "Locked".
let $wait_condition= select count(*)= 1 from information_schema.processlist let $wait_condition= select count(*)= 1 from information_schema.processlist
where state= 'Locked'; where state= 'Table lock';
--source include/wait_condition.inc --source include/wait_condition.inc
send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a; send update t1, v1 set t1.b=t1.a+t1.b+v1.b where t1.a=v1.a;
...@@ -507,7 +507,7 @@ connection locker; ...@@ -507,7 +507,7 @@ connection locker;
# are in work. # are in work.
# = There are two session is in state "Locked". # = There are two session is in state "Locked".
let $wait_condition= select count(*)= 2 from information_schema.processlist let $wait_condition= select count(*)= 2 from information_schema.processlist
where state= 'Locked'; where state= 'Table lock';
--source include/wait_condition.inc --source include/wait_condition.inc
unlock tables; unlock tables;
......
...@@ -58,18 +58,18 @@ connection user3; ...@@ -58,18 +58,18 @@ connection user3;
# Typical information_schema.processlist content after sufficient sleep time # Typical information_schema.processlist content after sufficient sleep time
# ID USER COMMAND TIME STATE INFO # ID USER COMMAND TIME STATE INFO
# .... # ....
# 2 root Query 5 Locked SELECT *, (SELECT COUNT(*) FROM t2) FROM t1 # 2 root Query 5 Table lock SELECT *, (SELECT COUNT(*) FROM t2) FROM t1
# .... # ....
# XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# The values marked with 'X' must be reached. # The values marked with 'X' must be reached.
--echo # Poll till the select of connection user1 is blocked by the write lock on t1. --echo # Poll till the select of connection user1 is blocked by the write lock on t1.
let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist let $wait_condition= SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = 'Locked' WHERE state = 'Table lock'
AND info = '$select_for_qc'; AND info = '$select_for_qc';
--source include/wait_condition.inc --source include/wait_condition.inc
eval eval
SELECT user,command,state,info FROM information_schema.processlist SELECT user,command,state,info FROM information_schema.processlist
WHERE state = 'Locked' WHERE state = 'Table lock'
AND info = '$select_for_qc'; AND info = '$select_for_qc';
INSERT INTO t1 VALUES (4); INSERT INTO t1 VALUES (4);
......
...@@ -322,7 +322,7 @@ set session low_priority_updates=on; ...@@ -322,7 +322,7 @@ set session low_priority_updates=on;
connection rl_wait; connection rl_wait;
let $wait_condition= let $wait_condition=
select count(*) = 1 from information_schema.processlist select count(*) = 1 from information_schema.processlist
where state = "Locked" and where state = "Table lock" and
info = "update t1 set value='updated' where value='old'"; info = "update t1 set value='updated' where value='old'";
--source include/wait_condition.inc --source include/wait_condition.inc
......
...@@ -58,7 +58,7 @@ let $ID= `select connection_id()`; ...@@ -58,7 +58,7 @@ let $ID= `select connection_id()`;
connection con2; connection con2;
--echo # Switched to connection: con2 --echo # Switched to connection: con2
# wait for the other query to start executing # wait for the other query to start executing
let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Locked"; let $wait_condition= select 1 from INFORMATION_SCHEMA.PROCESSLIST where ID = $ID and STATE = "Table lock";
--source include/wait_condition.inc --source include/wait_condition.inc
unlock tables; unlock tables;
......
...@@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, ...@@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
struct timespec wait_timeout; struct timespec wait_timeout;
enum enum_thr_lock_result result= THR_LOCK_ABORTED; enum enum_thr_lock_result result= THR_LOCK_ABORTED;
my_bool can_deadlock= test(data->owner->info->n_cursors); my_bool can_deadlock= test(data->owner->info->n_cursors);
const char *old_proc_info;
DBUG_ENTER("wait_for_lock"); DBUG_ENTER("wait_for_lock");
/* /*
...@@ -434,6 +435,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, ...@@ -434,6 +435,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
thread_var->current_cond= cond; thread_var->current_cond= cond;
data->cond= cond; data->cond= cond;
old_proc_info= proc_info_hook(NULL, "Table lock",
__func__, __FILE__, __LINE__);
if (can_deadlock) if (can_deadlock)
set_timespec(wait_timeout, table_lock_wait_timeout); set_timespec(wait_timeout, table_lock_wait_timeout);
while (!thread_var->abort || in_wait_list) while (!thread_var->abort || in_wait_list)
...@@ -504,6 +508,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, ...@@ -504,6 +508,9 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
thread_var->current_mutex= 0; thread_var->current_mutex= 0;
thread_var->current_cond= 0; thread_var->current_cond= 0;
pthread_mutex_unlock(&thread_var->mutex); pthread_mutex_unlock(&thread_var->mutex);
proc_info_hook(NULL, old_proc_info, __func__, __FILE__, __LINE__);
DBUG_RETURN(result); DBUG_RETURN(result);
} }
......
...@@ -310,7 +310,6 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, ...@@ -310,7 +310,6 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
break; break;
} }
DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
thd_proc_info(thd, "Locked");
/* Copy the lock data array. thr_multi_lock() reorders its contens. */ /* Copy the lock data array. thr_multi_lock() reorders its contens. */
memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks, memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
sql_lock->lock_count * sizeof(*sql_lock->locks)); sql_lock->lock_count * sizeof(*sql_lock->locks));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment