# At a local mysql-6.0-bugteam repository of davi
3179 Davi Arnaut 2009-04-03 [merge]
Merge Bug#43230 into mysql-6.0-bugteam
modified:
mysql-test/r/lock_multi.result
mysql-test/t/lock_multi.test
sql/sql_lex.cc
sql/sql_lex.h
sql/sql_parse.cc
sql/sql_yacc.yy
=== modified file 'mysql-test/r/lock_multi.result'
--- a/mysql-test/r/lock_multi.result 2009-03-24 14:24:44 +0000
+++ b/mysql-test/r/lock_multi.result 2009-04-03 21:07:07 +0000
@@ -97,6 +97,61 @@ alter table t1 auto_increment=0;
alter table t1 auto_increment=0;
unlock tables;
drop table t1;
+create table t1 (a int);
+create table t2 like t1;
+# con1
+lock tables t1 write;
+# con2
+flush tables with read lock;
+# con5
+# global read lock is taken
+# con3
+select * from t2 for update;
+# waiting for release of read lock
+# con4
+# would hang and later cause a deadlock
+flush tables t2;
+# clean up
+unlock tables;
+unlock tables;
+a
+drop table t1,t2;
+#
+# Lightweight version:
+# Ensure that the wait for a GRL is done before opening tables.
+#
+create table t1 (a int);
+create table t2 like t1;
+#
+# UPDATE
+#
+# default
+flush tables with read lock;
+# con1
+update t2 set a = 1;
+# default
+# statement is waiting for release of read lock
+# con2
+flush table t2;
+# default
+unlock tables;
+# con1
+#
+# LOCK TABLES .. WRITE
+#
+# default
+flush tables with read lock;
+# con1
+lock tables t2 write;
+# default
+# statement is waiting for release of read lock
+# con2
+flush table t2;
+# default
+unlock tables;
+# con1
+unlock tables;
+drop table t1,t2;
End of 5.0 tests
create table t1 (i int);
lock table t1 read;
=== modified file 'mysql-test/t/lock_multi.test'
--- a/mysql-test/t/lock_multi.test 2009-03-24 14:24:44 +0000
+++ b/mysql-test/t/lock_multi.test 2009-04-03 21:07:07 +0000
@@ -318,6 +318,134 @@ reap;
connection locker;
drop table t1;
+#
+# Bug#43230: SELECT ... FOR UPDATE can hang with FLUSH TABLES WITH READ LOCK indefinitely
+#
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connect (con3,localhost,root,,);
+connect (con4,localhost,root,,);
+connect (con5,localhost,root,,);
+
+create table t1 (a int);
+create table t2 like t1;
+
+connection con1;
+--echo # con1
+lock tables t1 write;
+connection con2;
+--echo # con2
+send flush tables with read lock;
+connection con5;
+--echo # con5
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for table" and info = "flush tables with read lock";
+--source include/wait_condition.inc
+--echo # global read lock is taken
+connection con3;
+--echo # con3
+send select * from t2 for update;
+connection con5;
+let $show_statement= SHOW PROCESSLIST;
+let $field= State;
+let $condition= = 'Waiting for release of readlock';
+--source include/wait_show_condition.inc
+--echo # waiting for release of read lock
+connection con4;
+--echo # con4
+--echo # would hang and later cause a deadlock
+flush tables t2;
+connection con1;
+--echo # clean up
+unlock tables;
+connection con2;
+--reap
+unlock tables;
+connection con3;
+--reap
+connection default;
+disconnect con5;
+disconnect con4;
+disconnect con3;
+disconnect con2;
+disconnect con1;
+
+drop table t1,t2;
+
+--echo #
+--echo # Lightweight version:
+--echo # Ensure that the wait for a GRL is done before opening tables.
+--echo #
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+
+create table t1 (a int);
+create table t2 like t1;
+
+--echo #
+--echo # UPDATE
+--echo #
+
+connection default;
+--echo # default
+flush tables with read lock;
+connection con1;
+--echo # con1
+send update t2 set a = 1;
+connection default;
+--echo # default
+let $show_statement= SHOW PROCESSLIST;
+let $field= State;
+let $condition= = 'Waiting for release of readlock';
+--source include/wait_show_condition.inc
+--echo # statement is waiting for release of read lock
+connection con2;
+--echo # con2
+flush table t2;
+connection default;
+--echo # default
+unlock tables;
+connection con1;
+--echo # con1
+--reap
+
+--echo #
+--echo # LOCK TABLES .. WRITE
+--echo #
+
+connection default;
+--echo # default
+flush tables with read lock;
+connection con1;
+--echo # con1
+send lock tables t2 write;
+connection default;
+--echo # default
+let $show_statement= SHOW PROCESSLIST;
+let $field= State;
+let $condition= = 'Waiting for release of readlock';
+--source include/wait_show_condition.inc
+--echo # statement is waiting for release of read lock
+connection con2;
+--echo # con2
+flush table t2;
+connection default;
+--echo # default
+unlock tables;
+connection con1;
+--echo # con1
+--reap
+unlock tables;
+
+connection default;
+disconnect con2;
+disconnect con1;
+
+drop table t1,t2;
+
--echo End of 5.0 tests
=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc 2009-04-01 09:34:34 +0000
+++ b/sql/sql_lex.cc 2009-04-03 21:07:07 +0000
@@ -360,6 +360,7 @@ void lex_start(THD *thd)
lex->nest_level=0 ;
lex->allow_sum_func= 0;
lex->in_sum_func= NULL;
+ lex->protect_against_global_read_lock= FALSE;
/*
ok, there must be a better solution for this, long-term
I tried "bzero" in the sql_yacc.yy code, but that for
=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h 2009-03-19 16:42:23 +0000
+++ b/sql/sql_lex.h 2009-04-03 21:07:07 +0000
@@ -1839,6 +1839,22 @@ struct LEX: public Query_tables_list
bool escape_used;
bool is_lex_started; /* If lex_start() did run. For debugging. */
+ /*
+ Special case for SELECT .. FOR UPDATE and LOCK TABLES .. WRITE.
+
+ Protect from a impending GRL as otherwise the thread might deadlock
+ if it starts waiting for the GRL in mysql_lock_tables.
+
+ The protection is needed because there is a race between setting
+ the global read lock and waiting for all open tables to be closed.
+ The problem is a circular wait where a thread holding "old" open
+ tables will wait for the global read lock to be released while the
+ thread holding the global read lock will wait for all "old" open
+ tables to be closed -- the flush part of flush tables with read
+ lock.
+ */
+ bool protect_against_global_read_lock;
+
LEX();
virtual ~LEX()
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2009-04-01 09:34:34 +0000
+++ b/sql/sql_parse.cc 2009-04-03 21:07:07 +0000
@@ -2152,8 +2152,15 @@ mysql_execute_command(THD *thd)
res= check_access(thd,
privileges_requested,
any_db, 0, 0, 0, 0);
- if (!res)
- res= execute_sqlcom_select(thd, all_tables);
+
+ if (res)
+ break;
+
+ if (!thd->locked_tables_mode && lex->protect_against_global_read_lock &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ break;
+
+ res= execute_sqlcom_select(thd, all_tables);
break;
}
case SQLCOM_PREPARE:
@@ -3110,6 +3117,9 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (update_precheck(thd, all_tables))
break;
+ if (!thd->locked_tables_mode &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
DBUG_ASSERT(select_lex->offset_limit == 0);
unit->set_limit(select_lex);
MYSQL_UPDATE_START(thd->query);
@@ -3137,6 +3147,15 @@ end_with_restore_list:
else
res= 0;
+ /*
+ Protection might have already been risen if its a fall through
+ from the SQLCOM_UPDATE case above.
+ */
+ if (!thd->locked_tables_mode &&
+ lex->sql_command == SQLCOM_UPDATE_MULTI &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
+
res= mysql_multi_update_prepare(thd);
#ifdef HAVE_REPLICATION
@@ -3333,7 +3352,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
-
+ if (!(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
res= mysql_truncate(thd, first_table, 0);
break;
@@ -3504,6 +3524,10 @@ end_with_restore_list:
if (check_one_table_access(thd, privilege, all_tables))
goto error;
+ if (!thd->locked_tables_mode &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
+
res= mysql_load(thd, lex->exchange, first_table, lex->field_list,
lex->update_list, lex->value_list, lex->duplicates,
lex->ignore, (bool) lex->local_file);
@@ -3615,6 +3639,9 @@ end_with_restore_list:
goto error;
/* release transactional metadata locks. */
thd->mdl_context.release_all_locks();
+ if (lex->protect_against_global_read_lock &&
+ !(need_start_waiting= !wait_if_global_read_lock(thd, 0, 1)))
+ goto error;
alloc_mdl_requests(all_tables, thd->locked_tables_list.locked_tables_root());
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2009-03-18 21:09:40 +0000
+++ b/sql/sql_yacc.yy 2009-04-03 21:07:07 +0000
@@ -7175,6 +7175,7 @@ select_lock_type:
lex->current_select->set_lock_for_tables(TL_WRITE);
lex->current_select->lock_option= TL_WRITE;
lex->safe_to_cache_query=0;
+ lex->protect_against_global_read_lock= TRUE;
}
| LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
{
@@ -12961,6 +12962,9 @@ table_lock:
/* Compute the resulting lock method for all tables. */
if (!$3.lock_transactional)
Lex->lock_transactional= FALSE;
+ /* If table is to be write locked, protect from a impending GRL. */
+ if ($3.lock_type >= TL_WRITE_ALLOW_WRITE)
+ Lex->protect_against_global_read_lock= TRUE;
}
;
Attachment: [text/bzr-bundle] bzr/davi.arnaut@sun.com-20090403210707-fuwh3s3lb3utknrm.bundle
| Thread |
|---|
| • bzr commit into mysql-6.0-bugteam branch (davi:3179) Bug#43230 | Davi Arnaut | 3 Apr |