Commit 3d6a89e7 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 d7f203c7
......@@ -102,6 +102,19 @@ struct timespec {
(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 pthread_create(pthread_t *, const pthread_attr_t *, pthread_handler, void *);
......@@ -412,6 +425,33 @@ int my_pthread_mutex_trylock(pthread_mutex_t *mutex);
(ABSTIME).tv_nsec= (long) (now % ULL(10000000) * 100 + ((NSEC) % 100)); \
}
#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 */
/* 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,
void *status_param);
enum enum_thr_lock_result 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);
void thr_unlock(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_lock_merge_status(THR_LOCK_DATA **data, uint count);
......@@ -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);
void thr_print_locks(void); /* For debugging */
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,
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
}
#endif
......
......@@ -348,3 +348,91 @@ commit;
# Switching to connection 'default'.
drop view v1;
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:
the week.
--local-infile Enable LOAD DATA LOCAL INFILE
(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,
use --general-log/--general-log-file instead).
--log-bin[=name] Log update queries in binary format. Optional (but
......@@ -839,6 +842,7 @@ lc-messages en_US
lc-messages-dir MYSQL_SHAREDIR/
lc-time-names en_US
local-infile TRUE
lock-wait-timeout 31536000
log-bin (No default value)
log-bin-index (No default value)
log-bin-trust-function-creators FALSE
......
......@@ -224,6 +224,9 @@ The following options may be given as the first argument:
the week.
--local-infile Enable LOAD DATA LOCAL INFILE
(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,
use --general-log/--general-log-file instead).
--log-bin[=name] Log update queries in binary format. Optional (but
......@@ -842,6 +845,7 @@ lc-messages en_US
lc-messages-dir MYSQL_SHAREDIR/
lc-time-names en_US
local-infile TRUE
lock-wait-timeout 31536000
log-bin (No default value)
log-bin-index (No default value)
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;
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
--source include/wait_until_count_sessions.inc
......@@ -389,13 +389,12 @@ static void wake_up_waiters(THR_LOCK *lock);
static enum enum_thr_lock_result
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;
mysql_cond_t *cond= &thread_var->suspend;
struct timespec wait_timeout;
enum enum_thr_lock_result result= THR_LOCK_ABORTED;
my_bool can_deadlock= test(data->owner->info->n_cursors);
const char *old_proc_info;
DBUG_ENTER("wait_for_lock");
......@@ -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",
__func__, __FILE__, __LINE__);
if (can_deadlock)
set_timespec(wait_timeout, table_lock_wait_timeout);
set_timespec(wait_timeout, lock_wait_timeout);
while (!thread_var->abort || in_wait_list)
{
int rc= (can_deadlock ?
mysql_cond_timedwait(cond, &data->lock->mutex,
&wait_timeout) :
mysql_cond_wait(cond, &data->lock->mutex));
int rc= mysql_cond_timedwait(cond, &data->lock->mutex, &wait_timeout);
/*
We must break the wait if one of the following occurs:
- 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,
enum enum_thr_lock_result
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;
enum enum_thr_lock_result result= THR_LOCK_SUCCESS;
......@@ -782,7 +777,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
goto end;
}
/* 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:
mysql_mutex_unlock(&lock->mutex);
DBUG_RETURN(result);
......@@ -1041,7 +1036,8 @@ static void sort_locks(THR_LOCK_DATA **data,uint count)
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;
DBUG_ENTER("thr_multi_lock");
......@@ -1051,7 +1047,8 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner)
/* lock everything */
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)
{ /* Aborted */
thr_multi_unlock(data,(uint) (pos-data));
......@@ -1472,7 +1469,8 @@ void thr_downgrade_write_lock(THR_LOCK_DATA *in_data,
/* Upgrade a WRITE_DELAY lock to a WRITE_LOCK */
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;
DBUG_ENTER("thr_upgrade_write_delay_lock");
......@@ -1515,13 +1513,14 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data,
{
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 */
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;
enum thr_lock_type write_lock_type;
......@@ -1553,7 +1552,8 @@ my_bool thr_reschedule_write_lock(THR_LOCK_DATA *data)
free_all_read_locks(lock,0);
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,
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
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 (sql_lock->table_count)
......@@ -983,7 +984,8 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list)
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 0;
......@@ -1055,7 +1057,8 @@ bool lock_routine_name(THD *thd, bool is_function,
mdl_requests.push_front(&mdl_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;
DEBUG_SYNC(thd, "after_wait_locked_pname");
......@@ -1258,7 +1261,8 @@ bool Global_read_lock::lock_global_read_lock(THD *thd)
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. */
mysql_mutex_lock(&LOCK_global_read_lock);
......
......@@ -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;
}
......
......@@ -466,13 +466,14 @@ public:
void destroy();
bool try_acquire_lock(MDL_request *mdl_request);
bool acquire_lock(MDL_request *mdl_request);
bool acquire_locks(MDL_request_list *requests);
bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket);
bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout);
bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout);
bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
ulong lock_wait_timeout);
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_lock(MDL_ticket *ticket);
......@@ -644,7 +645,7 @@ private:
MDL_ticket *find_ticket(MDL_request *mdl_req,
bool *is_transactional);
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();
......@@ -680,8 +681,7 @@ private:
mysql_mutex_unlock(&m_signal_lock);
}
mdl_signal_type wait();
mdl_signal_type timed_wait(ulong timeout);
mdl_signal_type timed_wait(struct timespec *abs_timeout);
mdl_signal_type peek_signal()
{
......
......@@ -2148,7 +2148,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
table->s->table_name.str, (ulong) table->s,
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);
mysql_mutex_lock(&LOCK_open);
......@@ -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(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;
}
else
......@@ -3843,7 +3845,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
switch (m_action)
{
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;
case OT_WAIT_TDC:
result= tdc_wait_for_old_versions(thd, &m_mdl_requests);
......@@ -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_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;
DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
......@@ -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_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;
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,
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;
for (table= tables_start; table && table != tables_end;
......@@ -8530,6 +8538,9 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests)
TABLE_SHARE *share;
const char *old_msg;
MDL_request *mdl_request;
struct timespec abstime;
set_timespec(abstime, thd->variables.lock_wait_timeout);
int wait_result= 0;
while (!thd->killed)
{
......@@ -8557,15 +8568,31 @@ tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests)
}
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);
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
break;
}
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. */
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
ulong auto_increment_increment, auto_increment_offset;
ulong bulk_insert_buff_size;
ulong join_buff_size;
ulong lock_wait_timeout;
ulong max_allowed_packet;
ulong max_error_count;
ulong max_length_for_sort_data;
......
......@@ -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_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);
has_mdl_lock= TRUE;
......
......@@ -1823,6 +1823,12 @@ public:
*/
thd.lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED);
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*) &table_list, sizeof(table_list)); // Safety
......@@ -2708,7 +2714,8 @@ bool Delayed_insert::handle_inserts(void)
table->use_all_columns();
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
......@@ -2893,7 +2900,8 @@ bool Delayed_insert::handle_inserts(void)
goto err;
}
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. */
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,
thd->mdl_context.try_acquire_lock(&table->mdl_request)) &&
!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;
}
return error;
......
......@@ -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(&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);
has_mdl_lock= TRUE;
......
......@@ -853,6 +853,12 @@ static Sys_var_mybool Sys_local_infile(
"local_infile", "Enable LOAD DATA LOCAL INFILE",
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
static Sys_var_mybool Sys_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