#At file:///export/home/z/mysql-next-bugfixing-bug45225/ based on revid:dlenev@stripped
2803 Jon Olav Hauglid 2009-08-27
Bug #45225 Locking: hang if drop table with no timeout
This patch introduces metadata lock timeouts for exclusive locks
(DDL operations). The timeout is specified in seconds using the
new server session variable "lock_wait_timeout".
The default setting of 0 causes no timeouts - i.e. infinite waiting.
Test case added to mdl_sync.test.
modified:
mysql-test/r/mdl_sync.result
mysql-test/t/mdl_sync.test
sql/lock.cc
sql/mdl.cc
sql/mdl.h
sql/set_var.cc
sql/sql_base.cc
sql/sql_class.h
sql/sql_delete.cc
sql/sql_table.cc
=== modified file 'mysql-test/r/mdl_sync.result'
--- a/mysql-test/r/mdl_sync.result 2009-08-27 06:22:17 +0000
+++ b/mysql-test/r/mdl_sync.result 2009-08-27 13:47:48 +0000
@@ -233,3 +233,32 @@ drop table t2;
# Switching to connection 'default'.
# Clean-up.
drop table t1;
+#
+# Bug#45225 Locking: hang if drop table with no timeout
+#
+DROP TABLE IF EXISTS t1;
+SET SESSION lock_wait_timeout=2;
+#
+# Test 1: acquire exclusive lock
+#
+CREATE TABLE t1 (s1 int primary key, s2 int, s3 int);
+# Connection 1
+START TRANSACTION;
+INSERT INTO t1 VALUES (7,7,7);
+# Connection 2
+DROP TABLE t1;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+COMMIT;
+DROP TABLE t1;
+#
+# Test 2: upgrade shared lock
+#
+CREATE TABLE t1 (si int);
+# Connection 1
+START TRANSACTION;
+INSERT INTO t1 VALUES (1);
+# Connection 2
+ALTER TABLE t1 RENAME TO t2;
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+COMMIT;
+DROP TABLE t1;
=== modified file 'mysql-test/t/mdl_sync.test'
--- a/mysql-test/t/mdl_sync.test 2009-08-27 06:22:17 +0000
+++ b/mysql-test/t/mdl_sync.test 2009-08-27 13:47:48 +0000
@@ -439,6 +439,59 @@ disconnect con46044_2;
drop table t1;
+
+--echo #
+--echo # Bug#45225 Locking: hang if drop table with no timeout
+--echo #
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+connect(con2, localhost, root,,);
+SET SESSION lock_wait_timeout=2;
+connection default;
+
+--echo #
+--echo # Test 1: acquire exclusive lock
+--echo #
+
+CREATE TABLE t1 (s1 int primary key, s2 int, s3 int);
+
+--echo # Connection 1
+START TRANSACTION;
+INSERT INTO t1 VALUES (7,7,7);
+
+--echo # Connection 2
+connection con2;
+--error ER_LOCK_WAIT_TIMEOUT
+DROP TABLE t1;
+
+connection default;
+COMMIT;
+DROP TABLE t1;
+
+--echo #
+--echo # Test 2: upgrade shared lock
+--echo #
+
+CREATE TABLE t1 (si int);
+
+--echo # Connection 1
+START TRANSACTION;
+INSERT INTO t1 VALUES (1);
+
+--echo # Connection 2
+connection con2;
+--error ER_LOCK_WAIT_TIMEOUT
+ALTER TABLE t1 RENAME TO t2;
+
+connection default;
+disconnect con2;
+COMMIT;
+DROP TABLE t1;
+
+
# Check that all connections opened by test cases in this file are really
# gone so execution of other tests won't be affected by their presence.
--source include/wait_until_count_sessions.inc
=== modified file 'sql/lock.cc'
--- a/sql/lock.cc 2009-08-25 07:22:47 +0000
+++ b/sql/lock.cc 2009-08-27 13:47:48 +0000
@@ -972,7 +972,8 @@ bool lock_table_names(THD *thd, TABLE_LI
MDL_EXCLUSIVE);
mdl_requests.push_front(&lock_table->mdl_request);
}
- if (thd->mdl_context.acquire_exclusive_locks(&mdl_requests))
+ if (thd->mdl_context.acquire_exclusive_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
return 1;
return 0;
}
=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc 2009-08-25 07:22:47 +0000
+++ b/sql/mdl.cc 2009-08-27 13:47:48 +0000
@@ -797,11 +797,12 @@ static bool notify_shared_lock(THD *thd,
wrapper around the method acquiring a list of locks.
*/
-bool MDL_context::acquire_exclusive_lock(MDL_request *mdl_request)
+bool MDL_context::acquire_exclusive_lock(MDL_request *mdl_request,
+ long lock_wait_timeout)
{
MDL_request_list mdl_requests;
mdl_requests.push_front(mdl_request);
- return acquire_exclusive_locks(&mdl_requests);
+ return acquire_exclusive_locks(&mdl_requests, lock_wait_timeout);
}
@@ -816,11 +817,15 @@ bool MDL_context::acquire_exclusive_lock
@note The MDL context may not have non-exclusive lock requests
or acquired locks.
+ @param lock_wait_timeout Seconds to wait before timeout.
+ Setting this to 0 causes infinite wait.
+
@retval FALSE Success
@retval TRUE Failure
*/
-bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests)
+bool MDL_context::acquire_exclusive_locks(MDL_request_list *mdl_requests,
+ long lock_wait_timeout)
{
MDL_lock *lock;
bool signalled= FALSE;
@@ -829,6 +834,8 @@ bool MDL_context::acquire_exclusive_lock
MDL_ticket *ticket;
st_my_thread_var *mysys_var= my_thread_var;
MDL_request_list::Iterator it(*mdl_requests);
+ /* Remaining time to wait before lock timeout */
+ long remaining_wait_time = lock_wait_timeout;
safe_mutex_assert_not_owner(&LOCK_open);
/* Exclusive locks must always be acquired first, all at once. */
@@ -909,20 +916,24 @@ bool MDL_context::acquire_exclusive_lock
/* There is a shared or exclusive lock on the object. */
DEBUG_SYNC(m_thd, "mdl_acquire_exclusive_locks_wait");
- if (signalled)
- pthread_cond_wait(&COND_mdl, &LOCK_mdl);
- else
+ if (lock_wait_timeout)
{
- /*
- Another thread obtained a shared MDL lock on some table but
- has not yet opened it and/or tried to obtain data lock on
- it. In this case we need to wait until this happens and try
- to abort this thread once again.
- */
+ uint waittime= signalled || remaining_wait_time < 10 ? remaining_wait_time : 10;
struct timespec abstime;
- set_timespec(abstime, 10);
- pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime);
+ set_timespec(abstime, waittime);
+ if (pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime) == ETIMEDOUT)
+ {
+ remaining_wait_time-= waittime;
+ if (remaining_wait_time <= 0)
+ {
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ goto err;
+ }
+ }
}
+ else /* Infinite wait */
+ pthread_cond_wait(&COND_mdl, &LOCK_mdl);
+
if (mysys_var->abort)
goto err;
}
@@ -985,16 +996,20 @@ err:
by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE,
TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock.
+ @param lock_wait_timeout Seconds to wait before timeout.
+ Setting this to 0 causes infinite wait.
@retval FALSE Success
@retval TRUE Failure (thread was killed)
*/
bool
-MDL_ticket::upgrade_shared_lock_to_exclusive()
+MDL_ticket::upgrade_shared_lock_to_exclusive(long lock_wait_timeout)
{
const char *old_msg;
st_my_thread_var *mysys_var= my_thread_var;
THD *thd= m_ctx->get_thd();
+ /* Remaining time to wait before lock timeout */
+ long remaining_wait_time = lock_wait_timeout;
DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive");
DEBUG_SYNC(thd, "mdl_upgrade_shared_lock_to_exclusive");
@@ -1033,28 +1048,28 @@ MDL_ticket::upgrade_shared_lock_to_exclu
/* There is a shared or exclusive lock on the object. */
DEBUG_SYNC(thd, "mdl_upgrade_shared_lock_to_exclusive_wait");
- if (signalled)
- pthread_cond_wait(&COND_mdl, &LOCK_mdl);
- else
+ if (lock_wait_timeout)
{
- /*
- Another thread obtained a shared MDL lock on some table but
- has not yet opened it and/or tried to obtain data lock on
- it. In this case we need to wait until this happens and try
- to abort this thread once again.
- */
+ uint waittime= signalled || remaining_wait_time < 10 ? remaining_wait_time : 10;
struct timespec abstime;
- set_timespec(abstime, 10);
- DBUG_PRINT("info", ("Failed to wake-up from table-level lock ... sleeping"));
- pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime);
+ set_timespec(abstime, waittime);
+ if (!signalled)
+ DBUG_PRINT("info", ("Failed to wake-up from table-level lock ... sleeping"));
+ if (pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime) == ETIMEDOUT)
+ {
+ remaining_wait_time-= waittime;
+ if (remaining_wait_time <= 0)
+ {
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ goto err;
+ }
+ }
}
+ else
+ pthread_cond_wait(&COND_mdl, &LOCK_mdl);
+
if (mysys_var->abort)
- {
- /* Pending requests for shared locks can be satisfied now. */
- pthread_cond_broadcast(&COND_mdl);
- MDL_EXIT_COND(thd, mysys_var, old_msg);
- DBUG_RETURN(TRUE);
- }
+ goto err;
}
m_lock->type= MDL_lock::MDL_LOCK_EXCLUSIVE;
@@ -1067,6 +1082,12 @@ MDL_ticket::upgrade_shared_lock_to_exclu
/* As a side-effect MDL_EXIT_COND() unlocks LOCK_mdl. */
MDL_EXIT_COND(thd, mysys_var, old_msg);
DBUG_RETURN(FALSE);
+
+err:
+ /* Pending requests for shared locks can be satisfied now. */
+ pthread_cond_broadcast(&COND_mdl);
+ MDL_EXIT_COND(thd, mysys_var, old_msg);
+ DBUG_RETURN(TRUE);
}
=== modified file 'sql/mdl.h'
--- a/sql/mdl.h 2009-08-25 07:22:47 +0000
+++ b/sql/mdl.h 2009-08-27 13:47:48 +0000
@@ -254,7 +254,7 @@ public:
mdl_cached_object_release_hook release_hook);
const MDL_context *get_ctx() const { return m_ctx; }
bool is_shared() const { return m_type < MDL_EXCLUSIVE; }
- bool upgrade_shared_lock_to_exclusive();
+ bool upgrade_shared_lock_to_exclusive(long lock_wait_timeout);
void downgrade_exclusive_lock();
private:
friend class MDL_context;
@@ -315,8 +315,8 @@ public:
void merge(MDL_context *source);
bool try_acquire_shared_lock(MDL_request *mdl_request);
- bool acquire_exclusive_lock(MDL_request *mdl_request);
- bool acquire_exclusive_locks(MDL_request_list *requests);
+ bool acquire_exclusive_lock(MDL_request *mdl_request, long lock_wait_timeout);
+ bool acquire_exclusive_locks(MDL_request_list *requests, long lock_wait_timeout);
bool try_acquire_exclusive_lock(MDL_request *mdl_request);
bool acquire_global_shared_lock();
=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc 2009-07-31 19:46:24 +0000
+++ b/sql/set_var.cc 2009-08-27 13:47:48 +0000
@@ -329,6 +329,8 @@ static sys_var_const sys_language(&va
(uchar*) lc_messages_dir);
static sys_var_bool_ptr sys_local_infile(&vars, "local_infile",
&opt_local_infile);
+static sys_var_thd_ulong sys_lock_wait_timeout(&vars, "lock_wait_timeout",
+ &SV::lock_wait_timeout);
#ifdef HAVE_MLOCKALL
static sys_var_const sys_locked_in_memory(&vars, "locked_in_memory",
OPT_GLOBAL, SHOW_MY_BOOL,
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc 2009-08-27 06:22:17 +0000
+++ b/sql/sql_base.cc 2009-08-27 13:47:48 +0000
@@ -2102,7 +2102,7 @@ bool wait_while_table_is_used(THD *thd,
old_lock_type= table->reginfo.lock_type;
mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
- if (table->mdl_ticket->upgrade_shared_lock_to_exclusive())
+ if (table->mdl_ticket->upgrade_shared_lock_to_exclusive(thd->variables.lock_wait_timeout))
{
mysql_lock_downgrade_write(thd, table, old_lock_type);
DBUG_RETURN(TRUE);
@@ -2310,7 +2310,7 @@ open_table_get_mdl_lock(THD *thd, TABLE_
mdl_request->set_type(MDL_EXCLUSIVE);
DBUG_ASSERT(! thd->mdl_context.has_locks());
- if (thd->mdl_context.acquire_exclusive_lock(mdl_request))
+ if (thd->mdl_context.acquire_exclusive_lock(mdl_request, thd->variables.lock_wait_timeout))
return 1;
}
else
@@ -3639,7 +3639,8 @@ recover_from_failed_open_table_attempt(T
MDL_request mdl_xlock_request(&table->mdl_request);
mdl_xlock_request.set_type(MDL_EXCLUSIVE);
if ((result=
- thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request)))
+ thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request,
+ thd->variables.lock_wait_timeout)))
break;
pthread_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
@@ -3656,7 +3657,8 @@ recover_from_failed_open_table_attempt(T
MDL_request mdl_xlock_request(&table->mdl_request);
mdl_xlock_request.set_type(MDL_EXCLUSIVE);
if ((result=
- thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request)))
+ thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request,
+ thd->variables.lock_wait_timeout)))
break;
pthread_mutex_lock(&LOCK_open);
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2009-08-26 09:14:05 +0000
+++ b/sql/sql_class.h 2009-08-27 13:47:48 +0000
@@ -355,6 +355,7 @@ struct system_variables
ulong net_read_timeout;
ulong net_retry_count;
ulong net_wait_timeout;
+ ulong lock_wait_timeout;
ulong net_write_timeout;
ulong optimizer_prune_level;
ulong optimizer_search_depth;
=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc 2009-08-25 07:22:47 +0000
+++ b/sql/sql_delete.cc 2009-08-27 13:47:48 +0000
@@ -1206,7 +1206,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST
*/
mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name,
MDL_EXCLUSIVE);
- if (thd->mdl_context.acquire_exclusive_lock(&mdl_request))
+ if (thd->mdl_context.acquire_exclusive_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
has_mdl_lock= TRUE;
pthread_mutex_lock(&LOCK_open);
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2009-08-25 07:22:47 +0000
+++ b/sql/sql_table.cc 2009-08-27 13:47:48 +0000
@@ -4288,7 +4288,8 @@ static int prepare_for_repair(THD *thd,
key_length= create_table_def_key(thd, key, table_list, 0);
table_list->mdl_request.init(MDL_TABLE, table_list->db, table_list->table_name,
MDL_EXCLUSIVE);
- if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request))
+ if (thd->mdl_context.acquire_exclusive_lock(&table_list->mdl_request,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(0);
has_mdl_lock= TRUE;
Attachment: [text/bzr-bundle] bzr/jon.hauglid@sun.com-20090827134748-18g8h467eafsoam6.bundle
| Thread |
|---|
| • bzr commit into mysql-5.4 branch (jon.hauglid:2803) Bug#45225 | Jon Olav Hauglid | 27 Aug |