diff options
author | Jon Olav Hauglid <jon.hauglid@sun.com> | 2010-02-11 11:23:39 +0100 |
---|---|---|
committer | Jon Olav Hauglid <jon.hauglid@sun.com> | 2010-02-11 11:23:39 +0100 |
commit | 3d6a89e7929292a8ca4c369d03e7d723045626b6 (patch) | |
tree | 61c2376a790a37e251fcfa30ba118b660e25e60d /sql/mdl.cc | |
parent | d7f203c79fdb25bef8756016efc63628187520cd (diff) | |
download | mariadb-git-3d6a89e7929292a8ca4c369d03e7d723045626b6.tar.gz |
Bug #45225 Locking: hang if drop table with no timeout
This patch introduces timeouts for metadata locks.
The timeout is specified in seconds using the new dynamic system
variable "lock_wait_timeout" which has both GLOBAL and SESSION
scopes. Allowed values range from 1 to 31536000 seconds (= 1 year).
The default value is 1 year.
The new server parameter "lock-wait-timeout" can be used to set
the default value parameter upon server startup.
"lock_wait_timeout" applies to all statements that use metadata locks.
These include DML and DDL operations on tables, views, stored procedures
and stored functions. They also include LOCK TABLES, FLUSH TABLES WITH
READ LOCK and HANDLER statements.
The patch also changes thr_lock.c code (table data locks used by MyISAM
and other simplistic engines) to use the same system variable.
InnoDB row locks are unaffected.
One exception to the handling of the "lock_wait_timeout" variable
is delayed inserts. All delayed inserts are executed with a timeout
of 1 year regardless of the setting for the global variable. As the
connection issuing the delayed insert gets no notification of
delayed insert timeouts, we want to avoid unnecessary timeouts.
It's important to note that the timeout value is used for each lock
acquired and that one statement can take more than one lock.
A statement can therefore block for longer than the lock_wait_timeout
value before reporting a timeout error. When lock timeout occurs,
ER_LOCK_WAIT_TIMEOUT is reported.
Test case added to lock_multi.test.
include/my_pthread.h:
Added macros for comparing two timespec structs.
include/thr_lock.h:
Introduced timeouts for thr_lock.c locks.
mysql-test/r/mysqld--help-notwin.result:
Updated result file with the new server variable.
mysql-test/r/mysqld--help-win.result:
Updated result file with the new server variable.
mysql-test/suite/sys_vars/r/lock_wait_timeout_basic.result:
Added basic test for the new server variable.
mysql-test/suite/sys_vars/t/lock_wait_timeout_basic.test:
Added basic test for the new server variable.
mysys/thr_lock.c:
Introduced timeouts for thr_lock.c locks.
sql/mdl.cc:
Introduced timeouts for metadata locks.
sql/mdl.h:
Introduced timeouts for metadata locks.
sql/sql_base.cc:
Introduced timeouts in tdc_wait_for_old_versions().
sql/sql_class.h:
Added new server variable lock_wait_timeout.
sql/sys_vars.cc:
Added new server variable lock_wait_timeout.
Diffstat (limited to 'sql/mdl.cc')
-rw-r--r-- | sql/mdl.cc | 114 |
1 files changed, 70 insertions, 44 deletions
diff --git a/sql/mdl.cc b/sql/mdl.cc index e48ae8a3f59..245d6d8a018 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -772,45 +772,25 @@ static inline void mdl_exit_cond(THD *thd, } -MDL_context::mdl_signal_type MDL_context::wait() +MDL_context::mdl_signal_type MDL_context::timed_wait(struct timespec + *abs_timeout) { const char *old_msg; - st_my_thread_var *mysys_var= my_thread_var; - mdl_signal_type result; - - mysql_mutex_lock(&m_signal_lock); - - old_msg= MDL_ENTER_COND(m_thd, mysys_var, &m_signal_cond, &m_signal_lock); - - while (! m_signal && !mysys_var->abort) - mysql_cond_wait(&m_signal_cond, &m_signal_lock); - - result= m_signal; - - MDL_EXIT_COND(m_thd, mysys_var, &m_signal_lock, old_msg); - - return result; -} - - -MDL_context::mdl_signal_type MDL_context::timed_wait(ulong timeout) -{ - struct timespec abstime; - const char *old_msg; mdl_signal_type result; st_my_thread_var *mysys_var= my_thread_var; + int wait_result= 0; mysql_mutex_lock(&m_signal_lock); old_msg= MDL_ENTER_COND(m_thd, mysys_var, &m_signal_cond, &m_signal_lock); - if (! m_signal) - { - set_timespec(abstime, timeout); - mysql_cond_timedwait(&m_signal_cond, &m_signal_lock, &abstime); - } + while (!m_signal && !mysys_var->abort && + wait_result != ETIMEDOUT && wait_result != ETIME) + wait_result= mysql_cond_timedwait(&m_signal_cond, &m_signal_lock, + abs_timeout); - result= (m_signal != NO_WAKE_UP) ? m_signal : TIMEOUT_WAKE_UP; + result= (m_signal != NO_WAKE_UP || mysys_var->abort) ? + m_signal : TIMEOUT_WAKE_UP; MDL_EXIT_COND(m_thd, mysys_var, &m_signal_lock, old_msg); @@ -1197,15 +1177,17 @@ MDL_context::find_ticket(MDL_request *mdl_request, @param mdl_request [in/out] Lock request object for lock to be acquired + @param lock_wait_timeout [in] Seconds to wait before timeout. + @retval FALSE Success. MDL_request::ticket points to the ticket for the lock. @retval TRUE Failure (Out of resources or waiting is aborted), */ bool -MDL_context::acquire_lock(MDL_request *mdl_request) +MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout) { - return acquire_lock_impl(mdl_request); + return acquire_lock_impl(mdl_request, lock_wait_timeout); } @@ -1397,6 +1379,8 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) @param mdl_request Request for the lock to be acqured. + @param lock_wait_timeout Seconds to wait before timeout. + @note Should not be used outside of MDL subsystem. Instead one should call acquire_lock() or acquire_locks() methods which ensure that conditions for deadlock-free @@ -1406,13 +1390,17 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) @retval TRUE Failure */ -bool MDL_context::acquire_lock_impl(MDL_request *mdl_request) +bool MDL_context::acquire_lock_impl(MDL_request *mdl_request, + ulong lock_wait_timeout) { MDL_lock *lock; MDL_ticket *ticket; bool not_used; st_my_thread_var *mysys_var= my_thread_var; MDL_key *key= &mdl_request->key; + struct timespec abs_timeout; + struct timespec abs_shortwait; + set_timespec(abs_timeout, lock_wait_timeout); mysql_mutex_assert_not_owner(&LOCK_open); @@ -1464,16 +1452,31 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request) /* There is a shared or exclusive lock on the object. */ DEBUG_SYNC(m_thd, "mdl_acquire_lock_wait"); - bool is_deadlock= (find_deadlock() || timed_wait(1) == VICTIM_WAKE_UP); + bool is_deadlock= find_deadlock(); + bool is_timeout= FALSE; + if (!is_deadlock) + { + set_timespec(abs_shortwait, 1); + bool timeout_is_near= cmp_timespec(abs_shortwait, abs_timeout) > 0; + mdl_signal_type wait_result= + timed_wait(timeout_is_near ? &abs_timeout : &abs_shortwait); + + if (timeout_is_near && wait_result == TIMEOUT_WAKE_UP) + is_timeout= TRUE; + else if (wait_result == VICTIM_WAKE_UP) + is_deadlock= TRUE; + } stop_waiting(); - if (is_deadlock || mysys_var->abort) + if (mysys_var->abort || is_deadlock || is_timeout) { lock->remove_ticket(&MDL_lock::m_waiting, ticket); MDL_ticket::destroy(ticket); if (is_deadlock) my_error(ER_LOCK_DEADLOCK, MYF(0)); + else if (is_timeout) + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); return TRUE; } rw_wrlock(&lock->m_rwlock); @@ -1513,6 +1516,8 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2) @param mdl_requests List of requests for locks to be acquired. + @param lock_wait_timeout Seconds to wait before timeout. + @note The list of requests should not contain non-exclusive lock requests. There should not be any acquired locks in the context. @@ -1522,7 +1527,8 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2) @retval TRUE Failure */ -bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) +bool MDL_context::acquire_locks(MDL_request_list *mdl_requests, + ulong lock_wait_timeout) { MDL_request_list::Iterator it(*mdl_requests); MDL_request **sort_buf, **p_req; @@ -1552,7 +1558,7 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) for (p_req= sort_buf; p_req < sort_buf + req_count; p_req++) { - if (acquire_lock_impl(*p_req)) + if (acquire_lock_impl(*p_req, lock_wait_timeout)) goto err; } my_free(sort_buf, MYF(0)); @@ -1578,6 +1584,8 @@ err: Used in ALTER TABLE, when a copy of the table with the new definition has been constructed. + @param lock_wait_timeout Seconds to wait before timeout. + @note In case of failure to upgrade lock (e.g. because upgrader was killed) leaves lock in its original state (locked in shared mode). @@ -1592,7 +1600,8 @@ err: */ bool -MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket) +MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, + ulong lock_wait_timeout) { MDL_request mdl_xlock_request; MDL_ticket *mdl_svp= mdl_savepoint(); @@ -1614,7 +1623,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket) mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE); - if (acquire_lock_impl(&mdl_xlock_request)) + if (acquire_lock_impl(&mdl_xlock_request, lock_wait_timeout)) DBUG_RETURN(TRUE); is_new_ticket= ! has_lock(mdl_svp, mdl_xlock_request.ticket); @@ -1803,21 +1812,25 @@ bool MDL_context::find_deadlock() Does not acquire the locks! + @param lock_wait_timeout Seconds to wait before timeout. + @retval FALSE Success. One can try to obtain metadata locks. @retval TRUE Failure (thread was killed or deadlock is possible). */ bool -MDL_context::wait_for_lock(MDL_request *mdl_request) +MDL_context::wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout) { MDL_lock *lock; st_my_thread_var *mysys_var= my_thread_var; + struct timespec abs_timeout; + set_timespec(abs_timeout, lock_wait_timeout); mysql_mutex_assert_not_owner(&LOCK_open); DBUG_ASSERT(mdl_request->ticket == NULL); - while (!mysys_var->abort) + while (TRUE) { /* We have to check if there are some HANDLERs open by this thread @@ -1860,19 +1873,32 @@ MDL_context::wait_for_lock(MDL_request *mdl_request) set_deadlock_weight(MDL_DEADLOCK_WEIGHT_DML); will_wait_for(pending_ticket); - bool is_deadlock= (find_deadlock() || wait() == VICTIM_WAKE_UP); + bool is_deadlock= find_deadlock(); + bool is_timeout= FALSE; + if (!is_deadlock) + { + mdl_signal_type wait_result= timed_wait(&abs_timeout); + if (wait_result == TIMEOUT_WAKE_UP) + is_timeout= TRUE; + else if (wait_result == VICTIM_WAKE_UP) + is_deadlock= TRUE; + } stop_waiting(); lock->remove_ticket(&MDL_lock::m_waiting, pending_ticket); MDL_ticket::destroy(pending_ticket); - if (is_deadlock) + + if (mysys_var->abort || is_deadlock || is_timeout) { - my_error(ER_LOCK_DEADLOCK, MYF(0)); + if (is_deadlock) + my_error(ER_LOCK_DEADLOCK, MYF(0)); + else if (is_timeout) + my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); return TRUE; } } - return mysys_var->abort; + return TRUE; } |