Loading include/my_pthread.h +7 −1 Original line number Diff line number Diff line Loading @@ -536,9 +536,15 @@ void safe_mutex_end(FILE *file); #define pthread_cond_timedwait(A,B,C) safe_cond_timedwait((A),(B),(C),__FILE__,__LINE__) #define pthread_mutex_trylock(A) pthread_mutex_lock(A) #define pthread_mutex_t safe_mutex_t #define safe_mutex_assert_owner(mp) DBUG_ASSERT((mp)->count > 0 && pthread_equal(pthread_self(),(mp)->thread)) #define safe_mutex_assert_owner(mp) \ DBUG_ASSERT((mp)->count > 0 && \ pthread_equal(pthread_self(), (mp)->thread)) #define safe_mutex_assert_not_owner(mp) \ DBUG_ASSERT(! (mp)->count || \ ! pthread_equal(pthread_self(), (mp)->thread)) #else #define safe_mutex_assert_owner(mp) #define safe_mutex_assert_not_owner(mp) #endif /* SAFE_MUTEX */ /* READ-WRITE thread locking */ Loading mysql-test/r/handler.result +37 −0 Original line number Diff line number Diff line Loading @@ -445,3 +445,40 @@ drop table t2; drop table t3; drop table t4; drop table t5; create table t1 (c1 int); insert into t1 values (1); handler t1 open; handler t1 read first; c1 1 send the below to another connection, do not wait for the result optimize table t1; proceed with the normal connection handler t1 read next; c1 1 handler t1 close; read the result from the other connection Table Op Msg_type Msg_text test.t1 optimize status OK proceed with the normal connection drop table t1; create table t1 (c1 int); insert into t1 values (14397); flush tables with read lock; drop table t1; ERROR HY000: Can't execute the query because you have a conflicting read lock send the below to another connection, do not wait for the result drop table t1; proceed with the normal connection select * from t1; c1 14397 unlock tables; read the result from the other connection proceed with the normal connection select * from t1; ERROR 42S02: Table 'test.t1' doesn't exist drop table if exists t1; Warnings: Note 1051 Unknown table 't1' mysql-test/t/handler.test +75 −0 Original line number Diff line number Diff line Loading @@ -347,4 +347,79 @@ drop table t3; drop table t4; drop table t5; # # Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash # create table t1 (c1 int); insert into t1 values (1); # client 1 handler t1 open; handler t1 read first; # client 2 connect (con2,localhost,root,,); connection con2; --exec echo send the below to another connection, do not wait for the result send optimize table t1; --sleep 1 # client 1 --exec echo proceed with the normal connection connection default; handler t1 read next; handler t1 close; # client 2 --exec echo read the result from the other connection connection con2; reap; # client 1 --exec echo proceed with the normal connection connection default; drop table t1; # End of 4.1 tests # # Addendum to Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash # Show that DROP TABLE can no longer deadlock against # FLUSH TABLES WITH READ LOCK. This is a 5.0 issue. # create table t1 (c1 int); insert into t1 values (14397); flush tables with read lock; # The thread with the global read lock cannot drop the table itself: --error 1223 drop table t1; # # client 2 # We need a second connection to try the drop. # The drop waits for the global read lock to go away. # Without the addendum fix it locked LOCK_open before entering the wait loop. connection con2; --exec echo send the below to another connection, do not wait for the result send drop table t1; --sleep 1 # # client 1 # Now we need something that wants LOCK_open. A simple table access which # opens the table does the trick. --exec echo proceed with the normal connection connection default; # This would hang on LOCK_open without the 5.0 addendum fix. select * from t1; # Release the read lock. This should make the DROP go through. unlock tables; # # client 2 # Read the result of the drop command. connection con2; --exec echo read the result from the other connection reap; # # client 1 # Now back to normal operation. The table should not exist any more. --exec echo proceed with the normal connection connection default; --error 1146 select * from t1; # Just to be sure and not confuse the next test case writer. drop table if exists t1; sql/lock.cc +14 −4 Original line number Diff line number Diff line Loading @@ -815,10 +815,13 @@ static void print_lock_error(int error, const char *table) access to them is protected with a mutex LOCK_global_read_lock (XXX: one should never take LOCK_open if LOCK_global_read_lock is taken, otherwise a deadlock may occur - see mysql_rm_table. Other mutexes could be a problem too - grep the code for global_read_lock if you want to use any other mutex here) (XXX: one should never take LOCK_open if LOCK_global_read_lock is taken, otherwise a deadlock may occur. Other mutexes could be a problem too - grep the code for global_read_lock if you want to use any other mutex here) Also one must not hold LOCK_open when calling wait_if_global_read_lock(). When the thread with the global read lock tries to close its tables, it needs to take LOCK_open in close_thread_table(). How blocking of threads by global read lock is achieved: that's advisory. Any piece of code which should be blocked by global read lock must Loading Loading @@ -937,6 +940,13 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, DBUG_ENTER("wait_if_global_read_lock"); LINT_INIT(old_message); /* Assert that we do not own LOCK_open. If we would own it, other threads could not close their tables. This would make a pretty deadlock. */ safe_mutex_assert_not_owner(&LOCK_open); (void) pthread_mutex_lock(&LOCK_global_read_lock); if ((need_exit_cond= must_wait)) { Loading sql/mysql_priv.h +2 −1 Original line number Diff line number Diff line Loading @@ -890,7 +890,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen); bool mysql_ha_close(THD *thd, TABLE_LIST *tables); bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows); int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags); int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags, bool is_locked); /* mysql_ha_flush mode_flags bits */ #define MYSQL_HA_CLOSE_FINAL 0x00 #define MYSQL_HA_REOPEN_ON_USAGE 0x01 Loading Loading
include/my_pthread.h +7 −1 Original line number Diff line number Diff line Loading @@ -536,9 +536,15 @@ void safe_mutex_end(FILE *file); #define pthread_cond_timedwait(A,B,C) safe_cond_timedwait((A),(B),(C),__FILE__,__LINE__) #define pthread_mutex_trylock(A) pthread_mutex_lock(A) #define pthread_mutex_t safe_mutex_t #define safe_mutex_assert_owner(mp) DBUG_ASSERT((mp)->count > 0 && pthread_equal(pthread_self(),(mp)->thread)) #define safe_mutex_assert_owner(mp) \ DBUG_ASSERT((mp)->count > 0 && \ pthread_equal(pthread_self(), (mp)->thread)) #define safe_mutex_assert_not_owner(mp) \ DBUG_ASSERT(! (mp)->count || \ ! pthread_equal(pthread_self(), (mp)->thread)) #else #define safe_mutex_assert_owner(mp) #define safe_mutex_assert_not_owner(mp) #endif /* SAFE_MUTEX */ /* READ-WRITE thread locking */ Loading
mysql-test/r/handler.result +37 −0 Original line number Diff line number Diff line Loading @@ -445,3 +445,40 @@ drop table t2; drop table t3; drop table t4; drop table t5; create table t1 (c1 int); insert into t1 values (1); handler t1 open; handler t1 read first; c1 1 send the below to another connection, do not wait for the result optimize table t1; proceed with the normal connection handler t1 read next; c1 1 handler t1 close; read the result from the other connection Table Op Msg_type Msg_text test.t1 optimize status OK proceed with the normal connection drop table t1; create table t1 (c1 int); insert into t1 values (14397); flush tables with read lock; drop table t1; ERROR HY000: Can't execute the query because you have a conflicting read lock send the below to another connection, do not wait for the result drop table t1; proceed with the normal connection select * from t1; c1 14397 unlock tables; read the result from the other connection proceed with the normal connection select * from t1; ERROR 42S02: Table 'test.t1' doesn't exist drop table if exists t1; Warnings: Note 1051 Unknown table 't1'
mysql-test/t/handler.test +75 −0 Original line number Diff line number Diff line Loading @@ -347,4 +347,79 @@ drop table t3; drop table t4; drop table t5; # # Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash # create table t1 (c1 int); insert into t1 values (1); # client 1 handler t1 open; handler t1 read first; # client 2 connect (con2,localhost,root,,); connection con2; --exec echo send the below to another connection, do not wait for the result send optimize table t1; --sleep 1 # client 1 --exec echo proceed with the normal connection connection default; handler t1 read next; handler t1 close; # client 2 --exec echo read the result from the other connection connection con2; reap; # client 1 --exec echo proceed with the normal connection connection default; drop table t1; # End of 4.1 tests # # Addendum to Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash # Show that DROP TABLE can no longer deadlock against # FLUSH TABLES WITH READ LOCK. This is a 5.0 issue. # create table t1 (c1 int); insert into t1 values (14397); flush tables with read lock; # The thread with the global read lock cannot drop the table itself: --error 1223 drop table t1; # # client 2 # We need a second connection to try the drop. # The drop waits for the global read lock to go away. # Without the addendum fix it locked LOCK_open before entering the wait loop. connection con2; --exec echo send the below to another connection, do not wait for the result send drop table t1; --sleep 1 # # client 1 # Now we need something that wants LOCK_open. A simple table access which # opens the table does the trick. --exec echo proceed with the normal connection connection default; # This would hang on LOCK_open without the 5.0 addendum fix. select * from t1; # Release the read lock. This should make the DROP go through. unlock tables; # # client 2 # Read the result of the drop command. connection con2; --exec echo read the result from the other connection reap; # # client 1 # Now back to normal operation. The table should not exist any more. --exec echo proceed with the normal connection connection default; --error 1146 select * from t1; # Just to be sure and not confuse the next test case writer. drop table if exists t1;
sql/lock.cc +14 −4 Original line number Diff line number Diff line Loading @@ -815,10 +815,13 @@ static void print_lock_error(int error, const char *table) access to them is protected with a mutex LOCK_global_read_lock (XXX: one should never take LOCK_open if LOCK_global_read_lock is taken, otherwise a deadlock may occur - see mysql_rm_table. Other mutexes could be a problem too - grep the code for global_read_lock if you want to use any other mutex here) (XXX: one should never take LOCK_open if LOCK_global_read_lock is taken, otherwise a deadlock may occur. Other mutexes could be a problem too - grep the code for global_read_lock if you want to use any other mutex here) Also one must not hold LOCK_open when calling wait_if_global_read_lock(). When the thread with the global read lock tries to close its tables, it needs to take LOCK_open in close_thread_table(). How blocking of threads by global read lock is achieved: that's advisory. Any piece of code which should be blocked by global read lock must Loading Loading @@ -937,6 +940,13 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, DBUG_ENTER("wait_if_global_read_lock"); LINT_INIT(old_message); /* Assert that we do not own LOCK_open. If we would own it, other threads could not close their tables. This would make a pretty deadlock. */ safe_mutex_assert_not_owner(&LOCK_open); (void) pthread_mutex_lock(&LOCK_global_read_lock); if ((need_exit_cond= must_wait)) { Loading
sql/mysql_priv.h +2 −1 Original line number Diff line number Diff line Loading @@ -890,7 +890,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen); bool mysql_ha_close(THD *thd, TABLE_LIST *tables); bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows); int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags); int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags, bool is_locked); /* mysql_ha_flush mode_flags bits */ #define MYSQL_HA_CLOSE_FINAL 0x00 #define MYSQL_HA_REOPEN_ON_USAGE 0x01 Loading