Commit 37bedc11 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

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.
parent 36bb57fe
...@@ -102,6 +102,19 @@ struct timespec { ...@@ -102,6 +102,19 @@ struct timespec {
(ABSTIME).max_timeout_msec= (long)((NSEC)/1000000); \ (ABSTIME).max_timeout_msec= (long)((NSEC)/1000000); \
} }
/**
Compare two timespec structs.
@retval 1 If TS1 ends after TS2.
@retval 0 If TS1 is equal to TS2.
@retval -1 If TS1 ends before TS2.
*/
#define cmp_timespec(TS1, TS2) \
((TS1.tv.i64 > TS2.tv.i64) ? 1 : \
((TS1.tv.i64 < TS2.tv.i64) ? -1 : 0))
int win_pthread_mutex_trylock(pthread_mutex_t *mutex); int win_pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_create(pthread_t *, const pthread_attr_t *, pthread_handler, void *); int pthread_create(pthread_t *, const pthread_attr_t *, pthread_handler, void *);
...@@ -412,6 +425,33 @@ int my_pthread_mutex_trylock(pthread_mutex_t *mutex); ...@@ -412,6 +425,33 @@ int my_pthread_mutex_trylock(pthread_mutex_t *mutex);
(ABSTIME).tv_nsec= (long) (now % ULL(10000000) * 100 + ((NSEC) % 100)); \ (ABSTIME).tv_nsec= (long) (now % ULL(10000000) * 100 + ((NSEC) % 100)); \
} }
#endif /* !set_timespec_nsec */ #endif /* !set_timespec_nsec */
#endif /* HAVE_TIMESPEC_TS_SEC */
/**
Compare two timespec structs.
@retval 1 If TS1 ends after TS2.
@retval 0 If TS1 is equal to TS2.
@retval -1 If TS1 ends before TS2.
*/
#ifdef HAVE_TIMESPEC_TS_SEC
#ifndef cmp_timespec
#define cmp_timespec(TS1, TS2) \
((TS1.ts_sec > TS2.ts_sec || \
(TS1.ts_sec == TS2.ts_sec && TS1.ts_nsec > TS2.ts_nsec)) ? 1 : \
((TS1.ts_sec < TS2.ts_sec || \
(TS1.ts_sec == TS2.ts_sec && TS1.ts_nsec < TS2.ts_nsec)) ? -1 : 0))
#endif /* !cmp_timespec */
#else
#ifndef cmp_timespec
#define cmp_timespec(TS1, TS2) \
((TS1.tv_sec > TS2.tv_sec || \
(TS1.tv_sec == TS2.tv_sec && TS1.tv_nsec > TS2.tv_nsec)) ? 1 : \
((TS1.tv_sec < TS2.tv_sec || \
(TS1.tv_sec == TS2.tv_sec && TS1.tv_nsec < TS2.tv_nsec)) ? -1 : 0))
#endif /* !cmp_timespec */
#endif /* HAVE_TIMESPEC_TS_SEC */ #endif /* HAVE_TIMESPEC_TS_SEC */
/* safe_mutex adds checking to mutex for easier debugging */ /* safe_mutex adds checking to mutex for easier debugging */
......
...@@ -156,10 +156,12 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, ...@@ -156,10 +156,12 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data,
void *status_param); void *status_param);
enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data,
THR_LOCK_OWNER *owner, THR_LOCK_OWNER *owner,
enum thr_lock_type lock_type); enum thr_lock_type lock_type,
ulong lock_wait_timeout);
void thr_unlock(THR_LOCK_DATA *data); void thr_unlock(THR_LOCK_DATA *data);
enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data,
uint count, THR_LOCK_OWNER *owner); uint count, THR_LOCK_OWNER *owner,
ulong lock_wait_timeout);
void thr_multi_unlock(THR_LOCK_DATA **data,uint count); void thr_multi_unlock(THR_LOCK_DATA **data,uint count);
void void
thr_lock_merge_status(THR_LOCK_DATA **data, uint count); thr_lock_merge_status(THR_LOCK_DATA **data, uint count);
...@@ -167,10 +169,12 @@ void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock); ...@@ -167,10 +169,12 @@ void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock);
my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread); my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread);
void thr_print_locks(void); /* For debugging */ void thr_print_locks(void); /* For debugging */
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
enum thr_lock_type new_lock_type); enum thr_lock_type new_lock_type,
ulong lock_wait_timeout);
void thr_downgrade_write_lock(THR_LOCK_DATA *data, void thr_downgrade_write_lock(THR_LOCK_DATA *data,
enum thr_lock_type new_lock_type); enum thr_lock_type new_lock_type);
my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data); my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data,
ulong lock_wait_timeout);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
......
...@@ -348,3 +348,91 @@ commit; ...@@ -348,3 +348,91 @@ commit;
# Switching to connection 'default'. # Switching to connection 'default'.
drop view v1; drop view v1;
drop table t1; drop table t1;
#
# Bug#45225 Locking: hang if drop table with no timeout
#
# These tests also provide function coverage for the
# lock_wait_timeout server variable.
#
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (id int);
SET SESSION lock_wait_timeout= 1;
#
# Test 1: acquire exclusive lock
#
# Connection default
START TRANSACTION;
INSERT INTO t1 VALUES (1);
# Connection 2
DROP TABLE t1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Connection default
COMMIT;
#
# Test 2: upgrade shared lock
#
# Connection default
START TRANSACTION;
SELECT * FROM t1;
id
1
# Connection 2
ALTER TABLE t1 RENAME TO t2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Connection default
COMMIT;
#
# Test 3: acquire shared lock
#
# Connection default
LOCK TABLE t1 WRITE;
# Connection 2
INSERT INTO t1(id) VALUES (2);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Connection default
UNLOCK TABLES;
#
# Test 4: table level locks
#
# Connection default
LOCK TABLE t1 READ;
# Connection 2
INSERT INTO t1(id) VALUES(4);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Connection default
UNLOCK TABLES;
#
# Test 5: Waiting on Table Definition Cache (TDC)
#
# Connection default
LOCK TABLE t1 READ;
# Connection con3
# Sending:
FLUSH TABLES;
# Connection con2
SELECT * FROM t1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Connection default
UNLOCK TABLES;
# Connection con3
# Reaping: FLUSH TABLES
#
# Test 6: Timeouts in I_S queries
#
# Connection default
CREATE TABLE t2 (id INT);
LOCK TABLE t2 WRITE;
# Connection con3
# Sending:
DROP TABLE t1, t2;
# Connection con2
SELECT table_name, table_comment FROM information_schema.tables
WHERE table_schema= 'test' AND table_name= 't1';
table_name table_comment
t1 Lock wait timeout exceeded; try restarting transaction
# Connection default
UNLOCK TABLES;
# Connection con3
# Reaping: DROP TABLE t1, t2
# Connection default
# Cleanup
...@@ -225,6 +225,9 @@ The following options may be given as the first argument: ...@@ -225,6 +225,9 @@ The following options may be given as the first argument:
the week. the week.
--local-infile Enable LOAD DATA LOCAL INFILE --local-infile Enable LOAD DATA LOCAL INFILE
(Defaults to on; use --skip-local-infile to disable.) (Defaults to on; use --skip-local-infile to disable.)
--lock-wait-timeout=#
Timeout in seconds to wait for a lock before returning an
error.
-l, --log[=name] Log connections and queries to file (deprecated option, -l, --log[=name] Log connections and queries to file (deprecated option,
use --general-log/--general-log-file instead). use --general-log/--general-log-file instead).
--log-bin[=name] Log update queries in binary format. Optional (but --log-bin[=name] Log update queries in binary format. Optional (but
...@@ -839,6 +842,7 @@ lc-messages en_US ...@@ -839,6 +842,7 @@ lc-messages en_US
lc-messages-dir MYSQL_SHAREDIR/ lc-messages-dir MYSQL_SHAREDIR/
lc-time-names en_US lc-time-names en_US
local-infile TRUE local-infile TRUE
lock-wait-timeout 31536000
log-bin (No default value) log-bin (No default value)
log-bin-index (No default value) log-bin-index (No default value)
log-bin-trust-function-creators FALSE log-bin-trust-function-creators FALSE
......
...@@ -224,6 +224,9 @@ The following options may be given as the first argument: ...@@ -224,6 +224,9 @@ The following options may be given as the first argument:
the week. the week.
--local-infile Enable LOAD DATA LOCAL INFILE --local-infile Enable LOAD DATA LOCAL INFILE
(Defaults to on; use --skip-local-infile to disable.) (Defaults to on; use --skip-local-infile to disable.)
--lock-wait-timeout=#
Timeout in seconds to wait for a lock before returning an
error.
-l, --log[=name] Log connections and queries to file (deprecated option, -l, --log[=name] Log connections and queries to file (deprecated option,
use --general-log/--general-log-file instead). use --general-log/--general-log-file instead).
--log-bin[=name] Log update queries in binary format. Optional (but --log-bin[=name] Log update queries in binary format. Optional (but
...@@ -842,6 +845,7 @@ lc-messages en_US ...@@ -842,6 +845,7 @@ lc-messages en_US
lc-messages-dir MYSQL_SHAREDIR/ lc-messages-dir MYSQL_SHAREDIR/
lc-time-names en_US lc-time-names en_US
local-infile TRUE local-infile TRUE
lock-wait-timeout 31536000
log-bin (No default value) log-bin (No default value)
log-bin-index (No default value) log-bin-index (No default value)
log-bin-trust-function-creators FALSE log-bin-trust-function-creators FALSE
......
SET @start_global_value = @@global.lock_wait_timeout;
SELECT @start_global_value;
@start_global_value
31536000
SET @start_session_value = @@session.lock_wait_timeout;
SELECT @start_session_value;
@start_session_value
31536000
'#--------------------FN_DYNVARS_002_01-------------------------#'
SET @@global.lock_wait_timeout = 100;
SET @@global.lock_wait_timeout = DEFAULT;
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
31536000
SET @@session.lock_wait_timeout = 200;
SET @@session.lock_wait_timeout = DEFAULT;
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
31536000
'#--------------------FN_DYNVARS_002_02-------------------------#'
SET @@global.lock_wait_timeout = @start_global_value;
SELECT @@global.lock_wait_timeout = 31536000;
@@global.lock_wait_timeout = 31536000
1
SET @@session.lock_wait_timeout = @start_session_value;
SELECT @@session.lock_wait_timeout = 31536000;
@@session.lock_wait_timeout = 31536000
1
'#--------------------FN_DYNVARS_002_03-------------------------#'
SET @@global.lock_wait_timeout = 1;
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
1
SET @@global.lock_wait_timeout = 60020;
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
60020
SET @@global.lock_wait_timeout = 65535;
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
65535
'#--------------------FN_DYNVARS_002_04-------------------------#'
SET @@session.lock_wait_timeout = 1;
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
1
SET @@session.lock_wait_timeout = 50050;
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
50050
SET @@session.lock_wait_timeout = 65535;
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
65535
'#------------------FN_DYNVARS_002_05-----------------------#'
SET @@global.lock_wait_timeout = 0;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '0'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
1
SET @@global.lock_wait_timeout = -1024;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '-1024'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
1
SET @@global.lock_wait_timeout = 31536001;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '31536001'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
31536000
SET @@global.lock_wait_timeout = ON;
ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
31536000
SET @@global.lock_wait_timeout = OFF;
ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
31536000
SET @@global.lock_wait_timeout = test;
ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
31536000
SET @@session.lock_wait_timeout = 0;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '0'
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
1
SET @@session.lock_wait_timeout = -2;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '-2'
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
1
SET @@session.lock_wait_timeout = 31537000;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '31537000'
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
31536000
SET @@session.lock_wait_timeout = ON;
ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout'
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
31536000
SET @@session.lock_wait_timeout = OFF;
ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout'
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
31536000
SET @@session.lock_wait_timeout = test;
ERROR 42000: Incorrect argument type to variable 'lock_wait_timeout'
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
31536000
'#------------------FN_DYNVARS_002_06-----------------------#'
SELECT @@global.lock_wait_timeout = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
WHERE VARIABLE_NAME='lock_wait_timeout';
@@global.lock_wait_timeout = VARIABLE_VALUE
1
'#------------------FN_DYNVARS_002_07-----------------------#'
SELECT @@session.lock_wait_timeout = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='lock_wait_timeout';
@@session.lock_wait_timeout = VARIABLE_VALUE
1
'#------------------FN_DYNVARS_002_08-----------------------#'
SET @@global.lock_wait_timeout = TRUE;
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
1
SET @@global.lock_wait_timeout = FALSE;
Warnings:
Warning 1292 Truncated incorrect lock_wait_timeout value: '0'
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
1
'#---------------------FN_DYNVARS_001_09----------------------#'
SET @@global.lock_wait_timeout = 10;
SET @@session.lock_wait_timeout = 11;
SELECT @@lock_wait_timeout = @@global.lock_wait_timeout;
@@lock_wait_timeout = @@global.lock_wait_timeout
0
'#---------------------FN_DYNVARS_001_10----------------------#'
SET @@lock_wait_timeout = 100;
SELECT @@lock_wait_timeout = @@local.lock_wait_timeout;
@@lock_wait_timeout = @@local.lock_wait_timeout
1
SELECT @@local.lock_wait_timeout = @@session.lock_wait_timeout;
@@local.lock_wait_timeout = @@session.lock_wait_timeout
1
'#---------------------FN_DYNVARS_001_11----------------------#'
SET lock_wait_timeout = 1;
SELECT @@lock_wait_timeout;
@@lock_wait_timeout
1
SELECT local.lock_wait_timeout;
ERROR 42S02: Unknown table 'local' in field list
SELECT session.lock_wait_timeout;
ERROR 42S02: Unknown table 'session' in field list
SELECT lock_wait_timeout = @@session.lock_wait_timeout;
ERROR 42S22: Unknown column 'lock_wait_timeout' in 'field list'
SET @@global.lock_wait_timeout = @start_global_value;
SELECT @@global.lock_wait_timeout;
@@global.lock_wait_timeout
31536000
SET @@session.lock_wait_timeout = @start_session_value;
SELECT @@session.lock_wait_timeout;
@@session.lock_wait_timeout
31536000
############## mysql-test\t\lock_wait_timeout_basic.test #######################
# #
# Variable Name: lock_wait_timeout #
# Scope: GLOBAL & SESSION #
# Access Type: Dynamic #
# Data Type: Numeric #
# Default Value: 1 #
# Range: 1 - 31536000 #
# #
# #
# Creation Date: 2010-02-08 #
# Author: Jon Olav Hauglid #
# #
# Description: Test Cases of Dynamic System Variable "lock_wait_timeout" #
# that checks behavior of this variable in the following ways #
# * Default Value #
# * Valid & Invalid values #
# * Scope & Access method #
# * Data Integrity #
# #
# Reference: http://dev.mysql.com/doc/refman/5.5/en/ #
# server-system-variables.html#option_mysqld_lock-wait-timeout #
# #
################################################################################
--source include/load_sysvars.inc
#####################################################################
# START OF lock_wait_timeout TESTS #
#####################################################################
#############################################################
# Save initial value #
#############################################################
SET @start_global_value = @@global.lock_wait_timeout;
SELECT @start_global_value;
SET @start_session_value = @@session.lock_wait_timeout;
SELECT @start_session_value;
--echo '#--------------------FN_DYNVARS_002_01-------------------------#'
#####################################################################
# Display the DEFAULT value of lock_wait_timeout #
#####################################################################
SET @@global.lock_wait_timeout = 100;
SET @@global.lock_wait_timeout = DEFAULT;
SELECT @@global.lock_wait_timeout;
SET @@session.lock_wait_timeout = 200;
SET @@session.lock_wait_timeout = DEFAULT;
SELECT @@session.lock_wait_timeout;
--echo '#--------------------FN_DYNVARS_002_02-------------------------#'
#####################################################################
# Check the DEFAULT value of lock_wait_timeout #
#####################################################################
SET @@global.lock_wait_timeout = @start_global_value;
SELECT @@global.lock_wait_timeout = 31536000;
SET @@session.lock_wait_timeout = @start_session_value;
SELECT @@session.lock_wait_timeout = 31536000;
--echo '#--------------------FN_DYNVARS_002_03-------------------------#'
###############################################################################
# Change the value of lock_wait_timeout to a valid value for GLOBAL Scope #
###############################################################################
SET @@global.lock_wait_timeout = 1;
SELECT @@global.lock_wait_timeout;
SET @@global.lock_wait_timeout = 60020;
SELECT @@global.lock_wait_timeout;
SET @@global.lock_wait_timeout = 65535;
SELECT @@global.lock_wait_timeout;
--echo '#--------------------FN_DYNVARS_002_04-------------------------#'
###############################################################################
# Change the value of lock_wait_timeout to a valid value for SESSION Scope #
###############################################################################
SET @@session.lock_wait_timeout = 1;
SELECT @@session.lock_wait_timeout;
SET @@session.lock_wait_timeout = 50050;
SELECT @@session.lock_wait_timeout;
SET @@session.lock_wait_timeout = 65535;
SELECT @@session.lock_wait_timeout;
--echo '#------------------FN_DYNVARS_002_05-----------------------#'
#################################################################
# Change the value of lock_wait_timeout to an invalid value #
#################################################################
# for global scope
SET @@global.lock_wait_timeout = 0;
SELECT @@global.lock_wait_timeout;
SET @@global.lock_wait_timeout = -1024;
SELECT @@global.lock_wait_timeout;
SET @@global.lock_wait_timeout = 31536001;
SELECT @@global.lock_wait_timeout;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@global.lock_wait_timeout = ON;
SELECT @@global.lock_wait_timeout;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@global.lock_wait_timeout = OFF;
SELECT @@global.lock_wait_timeout;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@global.lock_wait_timeout = test;
SELECT @@global.lock_wait_timeout;
# for session scope
SET @@session.lock_wait_timeout = 0;
SELECT @@session.lock_wait_timeout;
SET @@session.lock_wait_timeout = -2;
SELECT @@session.lock_wait_timeout;
SET @@session.lock_wait_timeout = 31537000;
SELECT @@session.lock_wait_timeout;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@session.lock_wait_timeout = ON;
SELECT @@session.lock_wait_timeout;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@session.lock_wait_timeout = OFF;
SELECT @@session.lock_wait_timeout;
--Error ER_WRONG_TYPE_FOR_VAR
SET @@session.lock_wait_timeout = test;
SELECT @@session.lock_wait_timeout;
--echo '#------------------FN_DYNVARS_002_06-----------------------#'
####################################################################
# Check if the value in GLOBAL Table matches value in variable #
####################################################################
SELECT @@global.lock_wait_timeout = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
WHERE VARIABLE_NAME='lock_wait_timeout';
--echo '#------------------FN_DYNVARS_002_07-----------------------#'
####################################################################
# Check if the value in SESSION Table matches value in variable #
####################################################################
SELECT @@session.lock_wait_timeout = VARIABLE_VALUE
FROM INFORMATION_SCHEMA.SESSION_VARIABLES
WHERE VARIABLE_NAME='lock_wait_timeout';
--echo '#------------------FN_DYNVARS_002_08-----------------------#'
####################################################################
# Check if TRUE and FALSE values can be used on variable #
####################################################################
SET @@global.lock_wait_timeout = TRUE;
SELECT @@global.lock_wait_timeout;
SET @@global.lock_wait_timeout = FALSE;
SELECT @@global.lock_wait_timeout;
--echo '#---------------------FN_DYNVARS_001_09----------------------#'
###############################################################################
# Check if global and session variables are independant of each other #
###############################################################################
SET @@global.lock_wait_timeout = 10;
SET @@session.lock_wait_timeout = 11;
SELECT @@lock_wait_timeout = @@global.lock_wait_timeout;
--echo '#---------------------FN_DYNVARS_001_10----------------------#'
##############################################################################
# Check if accessing variable with SESSION,LOCAL and without SCOPE points #
# to same session variable #
##############################################################################
SET @@lock_wait_timeout = 100;
SELECT @@lock_wait_timeout = @@local.lock_wait_timeout;
SELECT @@local.lock_wait_timeout = @@session.lock_wait_timeout;
--echo '#---------------------FN_DYNVARS_001_11----------------------#'
###############################################################################
# Check if lock_wait_timeout can be accessed with and without @@ sign #
###############################################################################
SET lock_wait_timeout = 1;
SELECT @@lock_wait_timeout;
--Error ER_UNKNOWN_TABLE
SELECT local.lock_wait_timeout;
--Error ER_UNKNOWN_TABLE
SELECT session.lock_wait_timeout;
--Error ER_BAD_FIELD_ERROR
SELECT lock_wait_timeout = @@session.lock_wait_timeout;
####################################
# Restore initial value #
####################################
SET @@global.lock_wait_timeout = @start_global_value;
SELECT @@global.lock_wait_timeout;
SET @@session.lock_wait_timeout = @start_session_value;
SELECT @@session.lock_wait_timeout;
###################################################
# END OF lock_wait_timeout TESTS #
###################################################
...@@ -877,5 +877,166 @@ drop view v1; ...@@ -877,5 +877,166 @@ drop view v1;
drop table t1; drop table t1;
--echo #
--echo # Bug#45225 Locking: hang if drop table with no timeout
--echo #
--echo # These tests also provide function coverage for the
--echo # lock_wait_timeout server variable.
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
CREATE TABLE t1 (id int);
connect(con2, localhost, root,,);
SET SESSION lock_wait_timeout= 1;
--echo #
--echo # Test 1: acquire exclusive lock
--echo #
--echo # Connection default
connection default;
START TRANSACTION;
INSERT INTO t1 VALUES (1);
--echo # Connection 2
connection con2;
--error ER_LOCK_WAIT_TIMEOUT
DROP TABLE t1;
--echo # Connection default
connection default;
COMMIT;
--echo #
--echo # Test 2: upgrade shared lock
--echo #
--echo # Connection default
connection default;
START TRANSACTION;
SELECT * FROM t1;
--echo # Connection 2
connection con2;
--error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 RENAME TO t2;
--echo # Connection default
connection default;
COMMIT;
--echo #
--echo # Test 3: acquire shared lock
--echo #
--echo # Connection default
connection default;
LOCK TABLE t1 WRITE;
--echo # Connection 2
connection con2;
--error ER_LOCK_WAIT_TIMEOUT
INSERT INTO t1(id) VALUES (2);
--echo # Connection default
connection default;
UNLOCK TABLES;
--echo #
--echo # Test 4: table level locks
--echo #
--echo # Connection default
connection default;
LOCK TABLE t1 READ;
--echo # Connection 2
connection con2;
--error ER_LOCK_WAIT_TIMEOUT
INSERT INTO t1(id) VALUES(4);
--echo # Connection default
connection default;
UNLOCK TABLES;
--echo #
--echo # Test 5: Waiting on Table Definition Cache (TDC)
--echo #
connect(con3, localhost, root);
--echo # Connection default
connection default;
LOCK TABLE t1 READ;
--echo # Connection con3
connection con3;
--echo # Sending:
--send FLUSH TABLES
--echo # Connection con2
connection con2;
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = "Flushing tables" AND info = "FLUSH TABLES";
--source include/wait_condition.inc
--error ER_LOCK_WAIT_TIMEOUT
SELECT * FROM t1;
--echo # Connection default
connection default;
UNLOCK TABLES;
--echo # Connection con3
connection con3;
--echo # Reaping: FLUSH TABLES
--reap
--echo #
--echo # Test 6: Timeouts in I_S queries
--echo #
--echo # Connection default
connection default;
CREATE TABLE t2 (id INT);
LOCK TABLE t2 WRITE;
--echo # Connection con3
connection con3;
--echo # Sending:
--send DROP TABLE t1, t2
--echo # Connection con2
connection con2;
let $wait_condition=
SELECT COUNT(*) = 1 FROM information_schema.processlist
WHERE state = "Waiting for table" AND info = "DROP TABLE t1, t2";
--source include/wait_condition.inc
# Note: This query causes two timeouts.
# 1: try_acquire_high_prio_shared_mdl_lock on t1
# 2: recover_from_failed_open on t1
SELECT table_name, table_comment FROM information_schema.tables
WHERE table_schema= 'test' AND table_name= 't1';
--echo # Connection default
connection default;
UNLOCK TABLES;
--echo # Connection con3
connection con3;
--echo # Reaping: DROP TABLE t1, t2
--reap
--echo # Connection default
connection default;
--echo # Cleanup
disconnect con2;
disconnect con3;
# Wait till all disconnects are completed # Wait till all disconnects are completed
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
...@@ -389,13 +389,12 @@ static void wake_up_waiters(THR_LOCK *lock); ...@@ -389,13 +389,12 @@ static void wake_up_waiters(THR_LOCK *lock);
static enum enum_thr_lock_result static enum enum_thr_lock_result
wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
my_bool in_wait_list) my_bool in_wait_list, ulong lock_wait_timeout)
{ {
struct st_my_thread_var *thread_var= my_thread_var; struct st_my_thread_var *thread_var= my_thread_var;
mysql_cond_t *cond= &thread_var->suspend; mysql_cond_t *cond= &thread_var->suspend;
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);
const char *old_proc_info; const char *old_proc_info;
DBUG_ENTER("wait_for_lock"); DBUG_ENTER("wait_for_lock");
...@@ -438,14 +437,10 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, ...@@ -438,14 +437,10 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
old_proc_info= proc_info_hook(NULL, "Table lock", old_proc_info= proc_info_hook(NULL, "Table lock",
__func__, __FILE__, __LINE__); __func__, __FILE__, __LINE__);
if (can_deadlock) set_timespec(wait_timeout, 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)
{ {
int rc= (can_deadlock ? int rc= mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout);
mysql_cond_timedwait(cond, &data->lock->mutex,
&wait_timeout) :
mysql_cond_wait(cond, &data->lock->mutex));
/* /*
We must break the wait if one of the following occurs: We must break the wait if one of the following occurs:
- the connection has been aborted (!thread_var->abort), but - the connection has been aborted (!thread_var->abort), but
...@@ -517,7 +512,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, ...@@ -517,7 +512,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
enum enum_thr_lock_result enum enum_thr_lock_result
thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
enum thr_lock_type lock_type) enum thr_lock_type lock_type, ulong lock_wait_timeout)
{ {
THR_LOCK *lock=data->lock; THR_LOCK *lock=data->lock;
enum enum_thr_lock_result result= THR_LOCK_SUCCESS; enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
...@@ -782,7 +777,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, ...@@ -782,7 +777,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
goto end; goto end;
} }
/* Can't get lock yet; Wait for it */ /* Can't get lock yet; Wait for it */
DBUG_RETURN(wait_for_lock(wait_queue, data, 0)); DBUG_RETURN(wait_for_lock(wait_queue, data, 0, lock_wait_timeout));
end: end:
mysql_mutex_unlock(&lock->mutex); mysql_mutex_unlock(&lock->mutex);
DBUG_RETURN(result); DBUG_RETURN(result);
...@@ -1041,7 +1036,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) ...@@ -1041,7 +1036,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count)
enum enum_thr_lock_result enum enum_thr_lock_result
thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner,
ulong lock_wait_timeout)
{ {
THR_LOCK_DATA **pos,**end; THR_LOCK_DATA **pos,**end;
DBUG_ENTER("thr_multi_lock"); DBUG_ENTER("thr_multi_lock");
...@@ -1051,7 +1047,8 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) ...@@ -1051,7 +1047,8 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner)
/* lock everything */ /* lock everything */
for (pos=data,end=data+count; pos < end ; pos++) for (pos=data,end=data+count; pos < end ; pos++)
{ {
enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type); enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type,
lock_wait_timeout);
if (result != THR_LOCK_SUCCESS) if (result != THR_LOCK_SUCCESS)
{ /* Aborted */ { /* Aborted */
thr_multi_unlock(data,(uint) (pos-data)); thr_multi_unlock(data,(uint) (pos-data));
...@@ -1472,7 +1469,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data, ...@@ -1472,7 +1469,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */ /* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
enum thr_lock_type new_lock_type) enum thr_lock_type new_lock_type,
ulong lock_wait_timeout)
{ {
THR_LOCK *lock=data->lock; THR_LOCK *lock=data->lock;
DBUG_ENTER("thr_upgrade_write_delay_lock"); DBUG_ENTER("thr_upgrade_write_delay_lock");
...@@ -1515,13 +1513,14 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, ...@@ -1515,13 +1513,14 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
{ {
check_locks(lock,"waiting for lock",0); check_locks(lock,"waiting for lock",0);
} }
DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1)); DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1, lock_wait_timeout));
} }
/* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */ /* downgrade a WRITE lock to a WRITE_DELAY lock if there is pending locks */
my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data) my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data,
ulong lock_wait_timeout)
{ {
THR_LOCK *lock=data->lock; THR_LOCK *lock=data->lock;
enum thr_lock_type write_lock_type; enum thr_lock_type write_lock_type;
...@@ -1553,7 +1552,8 @@ my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data) ...@@ -1553,7 +1552,8 @@ my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data)
free_all_read_locks(lock,0); free_all_read_locks(lock,0);
mysql_mutex_unlock(&lock->mutex); mysql_mutex_unlock(&lock->mutex);
DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type)); DBUG_RETURN(thr_upgrade_write_delay_lock(data, write_lock_type,
lock_wait_timeout));
} }
......
...@@ -336,7 +336,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, ...@@ -336,7 +336,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks + rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
sql_lock->lock_count, sql_lock->lock_count,
sql_lock->lock_count, sql_lock->lock_count,
thd->lock_id)]; thd->lock_id,
thd->variables.lock_wait_timeout)];
if (rc > 1) /* a timeout or a deadlock */ if (rc > 1) /* a timeout or a deadlock */
{ {
if (sql_lock->table_count) if (sql_lock->table_count)
...@@ -983,7 +984,8 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) ...@@ -983,7 +984,8 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list)
mdl_requests.push_front(&global_request); mdl_requests.push_front(&global_request);
if (thd->mdl_context.acquire_locks(&mdl_requests)) if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
return 1; return 1;
return 0; return 0;
...@@ -1055,7 +1057,8 @@ bool lock_routine_name(THD *thd, bool is_function, ...@@ -1055,7 +1057,8 @@ bool lock_routine_name(THD *thd, bool is_function,
mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&mdl_request);
mdl_requests.push_front(&global_request); mdl_requests.push_front(&global_request);
if (thd->mdl_context.acquire_locks(&mdl_requests)) if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
return TRUE; return TRUE;
DEBUG_SYNC(thd, "after_wait_locked_pname"); DEBUG_SYNC(thd, "after_wait_locked_pname");
...@@ -1258,7 +1261,8 @@ bool Global_read_lock::lock_global_read_lock(THD *thd) ...@@ -1258,7 +1261,8 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
MDL_SHARED)); MDL_SHARED));
mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED); mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED);
if (thd->mdl_context.acquire_lock(&mdl_request)) if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{ {
/* Our thread was killed -- return back to initial state. */ /* Our thread was killed -- return back to initial state. */
mysql_mutex_lock(&LOCK_global_read_lock); mysql_mutex_lock(&LOCK_global_read_lock);
......
...@@ -772,45 +772,25 @@ static inline void mdl_exit_cond(THD *thd, ...@@ -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; const char *old_msg;
mdl_signal_type result; mdl_signal_type result;
st_my_thread_var *mysys_var= my_thread_var; st_my_thread_var *mysys_var= my_thread_var;
int wait_result= 0;
mysql_mutex_lock(&m_signal_lock); mysql_mutex_lock(&m_signal_lock);
old_msg= MDL_ENTER_COND(m_thd, mysys_var, &m_signal_cond, &m_signal_lock); old_msg= MDL_ENTER_COND(m_thd, mysys_var, &m_signal_cond, &m_signal_lock);
if (! m_signal) while (!m_signal && !mysys_var->abort &&
{ wait_result != ETIMEDOUT && wait_result != ETIME)
set_timespec(abstime, timeout); wait_result= mysql_cond_timedwait(&m_signal_cond, &m_signal_lock,
mysql_cond_timedwait(&m_signal_cond, &m_signal_lock, &abstime); 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); MDL_EXIT_COND(m_thd, mysys_var, &m_signal_lock, old_msg);
...@@ -1197,15 +1177,17 @@ MDL_context::find_ticket(MDL_request *mdl_request, ...@@ -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 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 @retval FALSE Success. MDL_request::ticket points to the ticket
for the lock. for the lock.
@retval TRUE Failure (Out of resources or waiting is aborted), @retval TRUE Failure (Out of resources or waiting is aborted),
*/ */
bool 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) ...@@ -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 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 @note Should not be used outside of MDL subsystem. Instead one
should call acquire_lock() or acquire_locks() should call acquire_lock() or acquire_locks()
methods which ensure that conditions for deadlock-free methods which ensure that conditions for deadlock-free
...@@ -1406,13 +1390,17 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket) ...@@ -1406,13 +1390,17 @@ void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket)
@retval TRUE Failure @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_lock *lock;
MDL_ticket *ticket; MDL_ticket *ticket;
bool not_used; bool not_used;
st_my_thread_var *mysys_var= my_thread_var; st_my_thread_var *mysys_var= my_thread_var;
MDL_key *key= &mdl_request->key; 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); mysql_mutex_assert_not_owner(&LOCK_open);
...@@ -1464,16 +1452,31 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request) ...@@ -1464,16 +1452,31 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request)
/* There is a shared or exclusive lock on the object. */ /* There is a shared or exclusive lock on the object. */
DEBUG_SYNC(m_thd, "mdl_acquire_lock_wait"); 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(); stop_waiting();
if (is_deadlock || mysys_var->abort) if (mysys_var->abort || is_deadlock || is_timeout)
{ {
lock->remove_ticket(&MDL_lock::m_waiting, ticket); lock->remove_ticket(&MDL_lock::m_waiting, ticket);
MDL_ticket::destroy(ticket); MDL_ticket::destroy(ticket);
if (is_deadlock) if (is_deadlock)
my_error(ER_LOCK_DEADLOCK, MYF(0)); my_error(ER_LOCK_DEADLOCK, MYF(0));
else if (is_timeout)
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
return TRUE; return TRUE;
} }
rw_wrlock(&lock->m_rwlock); rw_wrlock(&lock->m_rwlock);
...@@ -1513,6 +1516,8 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2) ...@@ -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 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. @note The list of requests should not contain non-exclusive lock requests.
There should not be any acquired locks in the context. 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) ...@@ -1522,7 +1527,8 @@ extern "C" int mdl_request_ptr_cmp(const void* ptr1, const void* ptr2)
@retval TRUE Failure @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_list::Iterator it(*mdl_requests);
MDL_request **sort_buf, **p_req; MDL_request **sort_buf, **p_req;
...@@ -1552,7 +1558,7 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) ...@@ -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++) 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; goto err;
} }
my_free(sort_buf, MYF(0)); my_free(sort_buf, MYF(0));
...@@ -1578,6 +1584,8 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) ...@@ -1578,6 +1584,8 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests)
Used in ALTER TABLE, when a copy of the table with the Used in ALTER TABLE, when a copy of the table with the
new definition has been constructed. 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 @note In case of failure to upgrade lock (e.g. because upgrader
was killed) leaves lock in its original state (locked in was killed) leaves lock in its original state (locked in
shared mode). shared mode).
...@@ -1592,7 +1600,8 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests) ...@@ -1592,7 +1600,8 @@ bool MDL_context::acquire_locks(MDL_request_list *mdl_requests)
*/ */
bool 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_request mdl_xlock_request;
MDL_ticket *mdl_svp= mdl_savepoint(); MDL_ticket *mdl_svp= mdl_savepoint();
...@@ -1614,7 +1623,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket) ...@@ -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); 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); DBUG_RETURN(TRUE);
is_new_ticket= ! has_lock(mdl_svp, mdl_xlock_request.ticket); is_new_ticket= ! has_lock(mdl_svp, mdl_xlock_request.ticket);
...@@ -1803,21 +1812,25 @@ bool MDL_context::find_deadlock() ...@@ -1803,21 +1812,25 @@ bool MDL_context::find_deadlock()
Does not acquire the locks! 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 FALSE Success. One can try to obtain metadata locks.
@retval TRUE Failure (thread was killed or deadlock is possible). @retval TRUE Failure (thread was killed or deadlock is possible).
*/ */
bool 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; MDL_lock *lock;
st_my_thread_var *mysys_var= my_thread_var; 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); mysql_mutex_assert_not_owner(&LOCK_open);
DBUG_ASSERT(mdl_request->ticket == NULL); 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 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) ...@@ -1860,19 +1873,32 @@ MDL_context::wait_for_lock(MDL_request *mdl_request)
set_deadlock_weight(MDL_DEADLOCK_WEIGHT_DML); set_deadlock_weight(MDL_DEADLOCK_WEIGHT_DML);
will_wait_for(pending_ticket); 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(); stop_waiting();
lock->remove_ticket(&MDL_lock::m_waiting, pending_ticket); lock->remove_ticket(&MDL_lock::m_waiting, pending_ticket);
MDL_ticket::destroy(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 TRUE;
} }
} }
return mysys_var->abort; return TRUE;
} }
......
...@@ -466,13 +466,14 @@ class MDL_context ...@@ -466,13 +466,14 @@ class MDL_context
void destroy(); void destroy();
bool try_acquire_lock(MDL_request *mdl_request); bool try_acquire_lock(MDL_request *mdl_request);
bool acquire_lock(MDL_request *mdl_request); bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout);
bool acquire_locks(MDL_request_list *requests); bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout);
bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket); bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
ulong lock_wait_timeout);
bool clone_ticket(MDL_request *mdl_request); bool clone_ticket(MDL_request *mdl_request);
bool wait_for_lock(MDL_request *mdl_request); bool wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout);
void release_all_locks_for_name(MDL_ticket *ticket); void release_all_locks_for_name(MDL_ticket *ticket);
void release_lock(MDL_ticket *ticket); void release_lock(MDL_ticket *ticket);
...@@ -644,7 +645,7 @@ class MDL_context ...@@ -644,7 +645,7 @@ class MDL_context
MDL_ticket *find_ticket(MDL_request *mdl_req, MDL_ticket *find_ticket(MDL_request *mdl_req,
bool *is_transactional); bool *is_transactional);
void release_locks_stored_before(MDL_ticket *sentinel); void release_locks_stored_before(MDL_ticket *sentinel);
bool acquire_lock_impl(MDL_request *mdl_request); bool acquire_lock_impl(MDL_request *mdl_request, ulong lock_wait_timeout);
bool find_deadlock(); bool find_deadlock();
...@@ -680,8 +681,7 @@ class MDL_context ...@@ -680,8 +681,7 @@ class MDL_context
mysql_mutex_unlock(&m_signal_lock); mysql_mutex_unlock(&m_signal_lock);
} }
mdl_signal_type wait(); mdl_signal_type timed_wait(struct timespec *abs_timeout);
mdl_signal_type timed_wait(ulong timeout);
mdl_signal_type peek_signal() mdl_signal_type peek_signal()
{ {
......
...@@ -2148,7 +2148,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, ...@@ -2148,7 +2148,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
table->s->table_name.str, (ulong) table->s, table->s->table_name.str, (ulong) table->s,
table->db_stat, table->s->version)); table->db_stat, table->s->version));
if (thd->mdl_context.upgrade_shared_lock_to_exclusive(table->mdl_ticket)) if (thd->mdl_context.upgrade_shared_lock_to_exclusive(
table->mdl_ticket, thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
mysql_mutex_lock(&LOCK_open); mysql_mutex_lock(&LOCK_open);
...@@ -2362,7 +2363,8 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, ...@@ -2362,7 +2363,8 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
mdl_requests.push_front(mdl_request); mdl_requests.push_front(mdl_request);
mdl_requests.push_front(global_request); mdl_requests.push_front(global_request);
if (thd->mdl_context.acquire_locks(&mdl_requests)) if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
return 1; return 1;
} }
else else
...@@ -3843,7 +3845,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, ...@@ -3843,7 +3845,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
switch (m_action) switch (m_action)
{ {
case OT_WAIT_MDL_LOCK: case OT_WAIT_MDL_LOCK:
result= thd->mdl_context.wait_for_lock(mdl_request); result= thd->mdl_context.wait_for_lock(mdl_request,
thd->variables.lock_wait_timeout);
break; break;
case OT_WAIT_TDC: case OT_WAIT_TDC:
result= tdc_wait_for_old_versions(thd, &m_mdl_requests); result= tdc_wait_for_old_versions(thd, &m_mdl_requests);
...@@ -3862,7 +3865,9 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, ...@@ -3862,7 +3865,9 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
mdl_requests.push_front(&mdl_xlock_request); mdl_requests.push_front(&mdl_xlock_request);
mdl_requests.push_front(&mdl_global_request); mdl_requests.push_front(&mdl_global_request);
if ((result= thd->mdl_context.acquire_locks(&mdl_requests))) if ((result=
thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout)))
break; break;
DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
...@@ -3893,7 +3898,9 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request, ...@@ -3893,7 +3898,9 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
mdl_requests.push_front(&mdl_xlock_request); mdl_requests.push_front(&mdl_xlock_request);
mdl_requests.push_front(&mdl_global_request); mdl_requests.push_front(&mdl_global_request);
if ((result= thd->mdl_context.acquire_locks(&mdl_requests))) if ((result=
thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout)))
break; break;
DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE); DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
...@@ -4381,7 +4388,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, ...@@ -4381,7 +4388,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
mdl_requests.push_front(global_request); mdl_requests.push_front(global_request);
} }
if (thd->mdl_context.acquire_locks(&mdl_requests)) if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
return TRUE; return TRUE;
for (table= tables_start; table && table != tables_end; for (table= tables_start; table && table != tables_end;
...@@ -8530,6 +8538,9 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) ...@@ -8530,6 +8538,9 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests)
TABLE_SHARE *share; TABLE_SHARE *share;
const char *old_msg; const char *old_msg;
MDL_request *mdl_request; MDL_request *mdl_request;
struct timespec abstime;
set_timespec(abstime, thd->variables.lock_wait_timeout);
int wait_result= 0;
while (!thd->killed) while (!thd->killed)
{ {
...@@ -8557,15 +8568,31 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests) ...@@ -8557,15 +8568,31 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests)
} }
if (!mdl_request) if (!mdl_request)
{ {
/*
Reset wait_result here in case this was the final check
after getting a timeout from mysql_cond_timedwait().
*/
wait_result= 0;
mysql_mutex_unlock(&LOCK_open);
break;
}
if (wait_result == ETIMEDOUT || wait_result == ETIME)
{
/*
Test for timeout here instead of right after mysql_cond_timedwait().
This allows for a final iteration and a final check before reporting
ER_LOCK_WAIT_TIMEOUT.
*/
mysql_mutex_unlock(&LOCK_open); mysql_mutex_unlock(&LOCK_open);
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
break; break;
} }
old_msg= thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting for table"); old_msg= thd->enter_cond(&COND_refresh, &LOCK_open, "Waiting for table");
mysql_cond_wait(&COND_refresh, &LOCK_open); wait_result= mysql_cond_timedwait(&COND_refresh, &LOCK_open, &abstime);
/* LOCK_open mutex is unlocked by THD::exit_cond() as side-effect. */ /* LOCK_open mutex is unlocked by THD::exit_cond() as side-effect. */
thd->exit_cond(old_msg); thd->exit_cond(old_msg);
} }
return thd->killed; return thd->killed || wait_result == ETIMEDOUT || wait_result == ETIME;
} }
......
...@@ -342,6 +342,7 @@ typedef struct system_variables ...@@ -342,6 +342,7 @@ typedef struct system_variables
ulong auto_increment_increment, auto_increment_offset; ulong auto_increment_increment, auto_increment_offset;
ulong bulk_insert_buff_size; ulong bulk_insert_buff_size;
ulong join_buff_size; ulong join_buff_size;
ulong lock_wait_timeout;
ulong max_allowed_packet; ulong max_allowed_packet;
ulong max_error_count; ulong max_error_count;
ulong max_length_for_sort_data; ulong max_length_for_sort_data;
......
...@@ -1210,7 +1210,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) ...@@ -1210,7 +1210,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&mdl_request);
mdl_requests.push_front(&mdl_global_request); mdl_requests.push_front(&mdl_global_request);
if (thd->mdl_context.acquire_locks(&mdl_requests)) if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
has_mdl_lock= TRUE; has_mdl_lock= TRUE;
......
...@@ -1823,6 +1823,12 @@ class Delayed_insert :public ilink { ...@@ -1823,6 +1823,12 @@ class Delayed_insert :public ilink {
*/ */
thd.lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED); thd.lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED);
thd.set_current_stmt_binlog_format_row_if_mixed(); thd.set_current_stmt_binlog_format_row_if_mixed();
/*
Prevent changes to global.lock_wait_timeout from affecting
delayed insert threads as any timeouts in delayed inserts
are not communicated to the client.
*/
thd.variables.lock_wait_timeout= LONG_TIMEOUT;
bzero((char*) &thd.net, sizeof(thd.net)); // Safety bzero((char*) &thd.net, sizeof(thd.net)); // Safety
bzero((char*) &table_list, sizeof(table_list)); // Safety bzero((char*) &table_list, sizeof(table_list)); // Safety
...@@ -2708,7 +2714,8 @@ bool Delayed_insert::handle_inserts(void) ...@@ -2708,7 +2714,8 @@ bool Delayed_insert::handle_inserts(void)
table->use_all_columns(); table->use_all_columns();
thd_proc_info(&thd, "upgrading lock"); thd_proc_info(&thd, "upgrading lock");
if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock)) if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock,
thd.variables.lock_wait_timeout))
{ {
/* /*
This can happen if thread is killed either by a shutdown This can happen if thread is killed either by a shutdown
...@@ -2893,7 +2900,8 @@ bool Delayed_insert::handle_inserts(void) ...@@ -2893,7 +2900,8 @@ bool Delayed_insert::handle_inserts(void)
goto err; goto err;
} }
query_cache_invalidate3(&thd, table, 1); query_cache_invalidate3(&thd, table, 1);
if (thr_reschedule_write_lock(*thd.lock->locks)) if (thr_reschedule_write_lock(*thd.lock->locks,
thd.variables.lock_wait_timeout))
{ {
/* This is not known to happen. */ /* This is not known to happen. */
my_error(ER_DELAYED_CANT_CHANGE_LOCK,MYF(ME_FATALERROR), my_error(ER_DELAYED_CANT_CHANGE_LOCK,MYF(ME_FATALERROR),
......
...@@ -3103,7 +3103,9 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, ...@@ -3103,7 +3103,9 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
thd->mdl_context.try_acquire_lock(&table->mdl_request)) && thd->mdl_context.try_acquire_lock(&table->mdl_request)) &&
!table->mdl_request.ticket && !can_deadlock) !table->mdl_request.ticket && !can_deadlock)
{ {
if ((error= thd->mdl_context.wait_for_lock(&table->mdl_request))) if ((error=
thd->mdl_context.wait_for_lock(&table->mdl_request,
thd->variables.lock_wait_timeout)))
break; break;
} }
return error; return error;
......
...@@ -4403,7 +4403,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, ...@@ -4403,7 +4403,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
mdl_requests.push_front(&table_list->mdl_request); mdl_requests.push_front(&table_list->mdl_request);
mdl_requests.push_front(&mdl_global_request); mdl_requests.push_front(&mdl_global_request);
if (thd->mdl_context.acquire_locks(&mdl_requests)) if (thd->mdl_context.acquire_locks(&mdl_requests,
thd->variables.lock_wait_timeout))
DBUG_RETURN(0); DBUG_RETURN(0);
has_mdl_lock= TRUE; has_mdl_lock= TRUE;
......
...@@ -853,6 +853,12 @@ static Sys_var_mybool Sys_local_infile( ...@@ -853,6 +853,12 @@ static Sys_var_mybool Sys_local_infile(
"local_infile", "Enable LOAD DATA LOCAL INFILE", "local_infile", "Enable LOAD DATA LOCAL INFILE",
GLOBAL_VAR(opt_local_infile), CMD_LINE(OPT_ARG), DEFAULT(TRUE)); GLOBAL_VAR(opt_local_infile), CMD_LINE(OPT_ARG), DEFAULT(TRUE));
static Sys_var_ulong Sys_lock_wait_timeout(
"lock_wait_timeout",
"Timeout in seconds to wait for a lock before returning an error.",
SESSION_VAR(lock_wait_timeout), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(LONG_TIMEOUT), BLOCK_SIZE(1));
#ifdef HAVE_MLOCKALL #ifdef HAVE_MLOCKALL
static Sys_var_mybool Sys_locked_in_memory( static Sys_var_mybool Sys_locked_in_memory(
"locked_in_memory", "locked_in_memory",
......
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