@ -3750,120 +3750,6 @@ udf_handler::~udf_handler()
bool udf_handler : : get_arguments ( ) { return 0 ; }
# endif /* HAVE_DLOPEN */
/*
* * User level locks
*/
mysql_mutex_t LOCK_user_locks ;
static HASH hash_user_locks ;
class User_level_lock
{
uchar * key ;
size_t key_length ;
public :
int count ;
bool locked ;
mysql_cond_t cond ;
my_thread_id thread_id ;
void set_thread ( THD * thd ) { thread_id = thd - > thread_id ; }
User_level_lock ( const uchar * key_arg , uint length , ulong id )
: key_length ( length ) , count ( 1 ) , locked ( 1 ) , thread_id ( id )
{
key = ( uchar * ) my_memdup ( key_arg , length , MYF ( 0 ) ) ;
mysql_cond_init ( key_user_level_lock_cond , & cond , NULL ) ;
if ( key )
{
if ( my_hash_insert ( & hash_user_locks , ( uchar * ) this ) )
{
my_free ( key ) ;
key = 0 ;
}
}
}
~ User_level_lock ( )
{
if ( key )
{
my_hash_delete ( & hash_user_locks , ( uchar * ) this ) ;
my_free ( key ) ;
}
mysql_cond_destroy ( & cond ) ;
}
inline bool initialized ( ) { return key ! = 0 ; }
friend void item_user_lock_release ( User_level_lock * ull ) ;
friend uchar * ull_get_key ( const User_level_lock * ull , size_t * length ,
my_bool not_used ) ;
} ;
uchar * ull_get_key ( const User_level_lock * ull , size_t * length ,
my_bool not_used __attribute__ ( ( unused ) ) )
{
* length = ull - > key_length ;
return ull - > key ;
}
# ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_LOCK_user_locks ;
static PSI_mutex_info all_user_mutexes [ ] =
{
{ & key_LOCK_user_locks , " LOCK_user_locks " , PSI_FLAG_GLOBAL }
} ;
static void init_user_lock_psi_keys ( void )
{
const char * category = " sql " ;
int count ;
if ( PSI_server = = NULL )
return ;
count = array_elements ( all_user_mutexes ) ;
PSI_server - > register_mutex ( category , all_user_mutexes , count ) ;
}
# endif
static bool item_user_lock_inited = 0 ;
void item_user_lock_init ( void )
{
# ifdef HAVE_PSI_INTERFACE
init_user_lock_psi_keys ( ) ;
# endif
mysql_mutex_init ( key_LOCK_user_locks , & LOCK_user_locks , MY_MUTEX_INIT_SLOW ) ;
my_hash_init ( & hash_user_locks , system_charset_info ,
16 , 0 , 0 , ( my_hash_get_key ) ull_get_key , NULL , 0 ) ;
item_user_lock_inited = 1 ;
}
void item_user_lock_free ( void )
{
if ( item_user_lock_inited )
{
item_user_lock_inited = 0 ;
my_hash_free ( & hash_user_locks ) ;
mysql_mutex_destroy ( & LOCK_user_locks ) ;
}
}
void item_user_lock_release ( User_level_lock * ull )
{
ull - > locked = 0 ;
ull - > thread_id = 0 ;
if ( - - ull - > count )
mysql_cond_signal ( & ull - > cond ) ;
else
delete ull ;
}
/**
Wait until we are at or past the given position in the master binlog
on the slave .
*/
longlong Item_master_pos_wait : : val_int ( )
{
@ -4010,7 +3896,136 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
/**
Get a user level lock . If the thread has an old lock this is first released .
For locks with EXPLICIT duration , MDL returns a new ticket
every time a lock is granted . This allows to implement recursive
locks without extra allocation or additional data structures , such
as below . However , if there are too many tickets in the same
MDL_context , MDL_context : : find_ticket ( ) is getting too slow ,
since it ' s using a linear search .
This is why a separate structure is allocated for a user
level lock , and before requesting a new lock from MDL ,
GET_LOCK ( ) checks thd - > ull_hash if such lock is already granted ,
and if so , simply increments a reference counter .
*/
class User_level_lock
{
public :
MDL_ticket * lock ;
int refs ;
} ;
/** Extract a hash key from User_level_lock. */
uchar * ull_get_key ( const uchar * ptr , size_t * length ,
my_bool not_used __attribute__ ( ( unused ) ) )
{
User_level_lock * ull = ( User_level_lock * ) ptr ;
MDL_key * key = ull - > lock - > get_key ( ) ;
* length = key - > length ( ) ;
return ( uchar * ) key - > ptr ( ) ;
}
/**
Release all user level locks for this THD .
*/
void mysql_ull_cleanup ( THD * thd )
{
User_level_lock * ull ;
DBUG_ENTER ( " mysql_ull_cleanup " ) ;
for ( uint i = 0 ; i < thd - > ull_hash . records ; i + + )
{
ull = ( User_level_lock * ) my_hash_element ( & thd - > ull_hash , i ) ;
thd - > mdl_context . release_lock ( ull - > lock ) ;
my_free ( ull ) ;
}
my_hash_free ( & thd - > ull_hash ) ;
DBUG_VOID_RETURN ;
}
/**
Set explicit duration for metadata locks corresponding to
user level locks to protect them from being released at the end
of transaction .
*/
void mysql_ull_set_explicit_lock_duration ( THD * thd )
{
User_level_lock * ull ;
DBUG_ENTER ( " mysql_ull_set_explicit_lock_duration " ) ;
for ( uint i = 0 ; i < thd - > ull_hash . records ; i + + )
{
ull = ( User_level_lock * ) my_hash_element ( & thd - > ull_hash , i ) ;
thd - > mdl_context . set_lock_duration ( ull - > lock , MDL_EXPLICIT ) ;
}
DBUG_VOID_RETURN ;
}
/**
When MDL detects a lock wait timeout , it pushes
an error into the statement diagnostics area .
For GET_LOCK ( ) , lock wait timeout is not an error ,
but a special return value ( 0 ) . NULL is returned in
case of error .
Capture and suppress lock wait timeout .
*/
class Lock_wait_timeout_handler : public Internal_error_handler
{
public :
Lock_wait_timeout_handler ( ) : m_lock_wait_timeout ( false ) { }
bool m_lock_wait_timeout ;
bool handle_condition ( THD * /* thd */ , uint sql_errno ,
const char * /* sqlstate */ ,
MYSQL_ERROR : : enum_warning_level /* level */ ,
const char * message ,
MYSQL_ERROR * * /* cond_hdl */ ) ;
} ;
bool
Lock_wait_timeout_handler : :
handle_condition ( THD * /* thd */ , uint sql_errno ,
const char * /* sqlstate */ ,
MYSQL_ERROR : : enum_warning_level /* level */ ,
const char * message ,
MYSQL_ERROR * * /* cond_hdl */ )
{
if ( sql_errno = = ER_LOCK_WAIT_TIMEOUT )
{
m_lock_wait_timeout = true ;
return true ; /* condition handled */
}
return false ;
}
static int ull_name_ok ( String * name )
{
if ( ! name | | ! name - > length ( ) )
return 0 ;
if ( name - > length ( ) > NAME_LEN )
{
my_error ( ER_TOO_LONG_IDENT , MYF ( 0 ) , name - > c_ptr_safe ( ) ) ;
return 0 ;
}
return 1 ;
}
/**
Get a user level lock .
@ retval
1 : Got lock
@ -4023,14 +4038,13 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
longlong Item_func_get_lock : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
ulonglong timeout = args [ 1 ] - > val_int ( ) ;
THD * thd = current_thd ;
THD * thd = current_thd ;
User_level_lock * ull ;
int error ;
Interruptible_wait timed_cond ( thd ) ;
DBUG_ENTER ( " Item_func_get_lock::val_int " ) ;
null_value = 1 ;
/*
In slave thread no need to get locks , everything is serialized . Anyway
there is no way to make GET_LOCK ( ) work on slave like it did on master
@ -4039,104 +4053,70 @@ longlong Item_func_get_lock::val_int()
it ' s not guaranteed to be same as on master .
*/
if ( thd - > slave_thread )
{
null_value = 0 ;
DBUG_RETURN ( 1 ) ;
}
mysql_mutex_lock ( & LOCK_user_locks ) ;
if ( ! res | | ! res - > length ( ) )
{
mysql_mutex_unlock ( & LOCK_user_locks ) ;
null_value = 1 ;
if ( ! ull_name_ok ( res ) )
DBUG_RETURN ( 0 ) ;
}
DBUG_PRINT ( " info " , ( " lock %.*s, thd=%ld " , res - > length ( ) , res - > ptr ( ) ,
( long ) thd - > real_id ) ) ;
null_value = 0 ;
if ( thd - > ull )
/* HASH entries are of type User_level_lock. */
if ( ! my_hash_inited ( & thd - > ull_hash ) & &
my_hash_init ( & thd - > ull_hash , & my_charset_bin ,
16 /* small hash */ , 0 , 0 , ull_get_key , NULL , 0 ) )
{
item_user_lock_release ( thd - > ull ) ;
thd - > ull = 0 ;
}
if ( ! ( ull = ( ( User_level_lock * ) my_hash_search ( & hash_user_locks ,
( uchar * ) res - > ptr ( ) ,
( size_t ) res - > length ( ) ) ) ) )
{
ull = new User_level_lock ( ( uchar * ) res - > ptr ( ) , ( size_t ) res - > length ( ) ,
thd - > thread_id ) ;
if ( ! ull | | ! ull - > initialized ( ) )
{
delete ull ;
mysql_mutex_unlock ( & LOCK_user_locks ) ;
null_value = 1 ; // Probably out of memory
DBUG_RETURN ( 0 ) ;
}
ull - > set_thread ( thd ) ;
thd - > ull = ull ;
mysql_mutex_unlock ( & LOCK_user_locks ) ;
DBUG_PRINT ( " info " , ( " made new lock " ) ) ;
DBUG_RETURN ( 1 ) ; // Got new lock
DBUG_RETURN ( 0 ) ;
}
ull - > count + + ;
DBUG_PRINT ( " info " , ( " ull->count=%d " , ull - > count ) ) ;
/*
Structure is now initialized . Try to get the lock .
Set up control struct to allow others to abort locks .
*/
thd_proc_info ( thd , " User lock " ) ;
thd - > mysys_var - > current_mutex = & LOCK_user_locks ;
thd - > mysys_var - > current_cond = & ull - > cond ;
MDL_request ull_request ;
ull_request . init ( MDL_key : : USER_LOCK , res - > c_ptr_safe ( ) , " " ,
MDL_SHARED_NO_WRITE , MDL_EXPLICIT ) ;
MDL_key * ull_key = & ull_request . key ;
timed_cond . set_timeout ( timeout * 1000000000ULL ) ;
error = 0 ;
thd_wait_begin ( thd , THD_WAIT_USER_LOCK ) ;
while ( ull - > locked & & ! thd - > killed )
if ( ( ull = ( User_level_lock * )
my_hash_search ( & thd - > ull_hash , ull_key - > ptr ( ) , ull_key - > length ( ) ) ) )
{
DBUG_PRINT ( " info " , ( " waiting on lock " ) ) ;
error = timed_cond . wait ( & ull - > cond , & LOCK_user_locks ) ;
if ( error = = ETIMEDOUT | | error = = ETIME )
{
DBUG_PRINT ( " info " , ( " lock wait timeout " ) ) ;
break ;
}
error = 0 ;
/* Recursive lock */
ull - > refs + + ;
null_value = 0 ;
DBUG_RETURN ( 1 ) ;
}
thd_wait_end ( thd ) ;
if ( ull - > locked )
Lock_wait_timeout_handler lock_wait_timeout_handler ;
thd - > push_internal_handler ( & lock_wait_timeout_handler ) ;
bool error = thd - > mdl_context . acquire_lock ( & ull_request , timeout ) ;
( void ) thd - > pop_internal_handler ( ) ;
if ( error )
{
if ( ! - - ull - > count )
{
DBUG_ASSERT ( 0 ) ;
delete ull ; // Should never happen
}
if ( ! error ) // Killed (thd->killed != 0)
{
error = 1 ;
null_value = 1 ; // Return NULL
}
if ( lock_wait_timeout_handler . m_lock_wait_timeout )
null_value = 0 ;
DBUG_RETURN ( 0 ) ;
}
else // We got the lock
ull = ( User_level_lock * ) my_malloc ( sizeof ( User_level_lock ) ,
MYF ( MY_WME | MY_THREAD_SPECIFIC ) ) ;
if ( ull = = NULL )
{
ull - > locked = 1 ;
ull - > set_thread ( thd ) ;
ull - > thread_id = thd - > thread_id ;
thd - > ull = ull ;
error = 0 ;
DBUG_PRINT ( " info " , ( " got the lock " ) ) ;
thd - > mdl_context . release_lock ( ull_request . ticket ) ;
DBUG_RETURN ( 0 ) ;
}
mysql_mutex_unlock ( & LOCK_user_locks ) ;
mysql_mutex_lock ( & thd - > mysys_var - > mutex ) ;
thd_proc_info ( thd , 0 ) ;
thd - > mysys_var - > current_mutex = 0 ;
thd - > mysys_var - > current_cond = 0 ;
mysql_mutex_unlock ( & thd - > mysys_var - > mutex ) ;
ull - > lock = ull_request . ticket ;
ull - > refs = 1 ;
DBUG_RETURN ( ! error ? 1 : 0 ) ;
if ( my_hash_insert ( & thd - > ull_hash , ( uchar * ) ull ) )
{
thd - > mdl_context . release_lock ( ull - > lock ) ;
my_free ( ull ) ;
DBUG_RETURN ( 0 ) ;
}
null_value = 0 ;
DBUG_RETURN ( 1 ) ;
}
@ -4151,43 +4131,86 @@ longlong Item_func_get_lock::val_int()
longlong Item_func_release_lock : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
User_level_lock * ull ;
longlong result ;
THD * thd = current_thd ;
String * res = args [ 0 ] - > val_str ( & value ) ;
THD * thd = current_thd ;
DBUG_ENTER ( " Item_func_release_lock::val_int " ) ;
if ( ! res | | ! res - > length ( ) )
{
null_value = 1 ;
null_value = 1 ;
if ( ! ull_name_ok ( res ) )
DBUG_RETURN ( 0 ) ;
}
DBUG_PRINT ( " info " , ( " lock %.*s " , res - > length ( ) , res - > ptr ( ) ) ) ;
null_value = 0 ;
result = 0 ;
mysql_mutex_lock ( & LOCK_user_locks ) ;
if ( ! ( ull = ( ( User_level_lock * ) my_hash_search ( & hash_user_locks ,
( const uchar * ) res - > ptr ( ) ,
( size_t ) res - > length ( ) ) ) ) )
MDL_key ull_key ;
ull_key . mdl_key_init ( MDL_key : : USER_LOCK , res - > c_ptr_safe ( ) , " " ) ;
User_level_lock * ull ;
if ( ! ( ull =
( User_level_lock * ) my_hash_search ( & thd - > ull_hash ,
ull_key . ptr ( ) , ull_key . length ( ) ) ) )
{
null_value = 1 ;
null_value = thd - > mdl_context . get_lock_owner ( & ull_key ) = = 0 ;
DBUG_RETURN ( 0 ) ;
}
else
null_value = 0 ;
if ( - - ull - > refs = = 0 )
{
DBUG_PRINT ( " info " , ( " ull->locked=%d ull->thread=%lu thd=%lu " ,
( int ) ull - > locked ,
( long ) ull - > thread_id ,
( long ) thd - > thread_id ) ) ;
if ( ull - > locked & & current_thd - > thread_id = = ull - > thread_id )
{
DBUG_PRINT ( " info " , ( " release lock " ) ) ;
result = 1 ; // Release is ok
item_user_lock_release ( ull ) ;
thd - > ull = 0 ;
}
my_hash_delete ( & thd - > ull_hash , ( uchar * ) ull ) ;
thd - > mdl_context . release_lock ( ull - > lock ) ;
my_free ( ull ) ;
}
mysql_mutex_unlock ( & LOCK_user_locks ) ;
DBUG_RETURN ( result ) ;
DBUG_RETURN ( 1 ) ;
}
/**
Check a user level lock .
Sets null_value = TRUE on error .
@ retval
1 Available
@ retval
0 Already taken , or error
*/
longlong Item_func_is_free_lock : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
THD * thd = current_thd ;
null_value = 1 ;
if ( ! ull_name_ok ( res ) )
return 0 ;
MDL_key ull_key ;
ull_key . mdl_key_init ( MDL_key : : USER_LOCK , res - > c_ptr_safe ( ) , " " ) ;
null_value = 0 ;
return thd - > mdl_context . get_lock_owner ( & ull_key ) = = 0 ;
}
longlong Item_func_is_used_lock : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
THD * thd = current_thd ;
null_value = 1 ;
if ( ! ull_name_ok ( res ) )
return 0 ;
MDL_key ull_key ;
ull_key . mdl_key_init ( MDL_key : : USER_LOCK , res - > c_ptr_safe ( ) , " " ) ;
ulong thread_id = thd - > mdl_context . get_lock_owner ( & ull_key ) ;
if ( thread_id = = 0 )
return 0 ;
null_value = 0 ;
return thread_id ;
}
@ -4288,6 +4311,54 @@ void Item_func_benchmark::print(String *str, enum_query_type query_type)
}
mysql_mutex_t LOCK_item_func_sleep ;
# ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_LOCK_item_func_sleep ;
static PSI_mutex_info item_func_sleep_mutexes [ ] =
{
{ & key_LOCK_item_func_sleep , " LOCK_user_locks " , PSI_FLAG_GLOBAL }
} ;
static void init_item_func_sleep_psi_keys ( void )
{
const char * category = " sql " ;
int count ;
if ( PSI_server = = NULL )
return ;
count = array_elements ( item_func_sleep_mutexes ) ;
PSI_server - > register_mutex ( category , item_func_sleep_mutexes , count ) ;
}
# endif
static bool item_func_sleep_inited = 0 ;
void item_func_sleep_init ( void )
{
# ifdef HAVE_PSI_INTERFACE
init_item_func_sleep_psi_keys ( ) ;
# endif
mysql_mutex_init ( key_LOCK_item_func_sleep , & LOCK_item_func_sleep , MY_MUTEX_INIT_SLOW ) ;
item_func_sleep_inited = 1 ;
}
void item_func_sleep_free ( void )
{
if ( item_func_sleep_inited )
{
item_func_sleep_inited = 0 ;
mysql_mutex_destroy ( & LOCK_item_func_sleep ) ;
}
}
/** This function is just used to create tests with time gaps. */
longlong Item_func_sleep : : val_int ( )
@ -4316,24 +4387,24 @@ longlong Item_func_sleep::val_int()
timed_cond . set_timeout ( ( ulonglong ) ( timeout * 1000000000.0 ) ) ;
mysql_cond_init ( key_item_func_sleep_cond , & cond , NULL ) ;
mysql_mutex_lock ( & LOCK_user_locks ) ;
mysql_mutex_lock ( & LOCK_item_func_sleep ) ;
thd_proc_info ( thd , " User sleep " ) ;
thd - > mysys_var - > current_mutex = & LOCK_user_locks ;
thd - > mysys_var - > current_mutex = & LOCK_item_func_sleep ;
thd - > mysys_var - > current_cond = & cond ;
error = 0 ;
thd_wait_begin ( thd , THD_WAIT_SLEEP ) ;
while ( ! thd - > killed )
{
error = timed_cond . wait ( & cond , & LOCK_user_locks ) ;
error = timed_cond . wait ( & cond , & LOCK_item_func_sleep ) ;
if ( error = = ETIMEDOUT | | error = = ETIME )
break ;
error = 0 ;
}
thd_wait_end ( thd ) ;
thd_proc_info ( thd , 0 ) ;
mysql_mutex_unlock ( & LOCK_user_locks ) ;
mysql_mutex_unlock ( & LOCK_item_func_sleep ) ;
mysql_mutex_lock ( & thd - > mysys_var - > mutex ) ;
thd - > mysys_var - > current_mutex = 0 ;
thd - > mysys_var - > current_cond = 0 ;
@ -6208,61 +6279,6 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
}
/**
Check a user level lock .
Sets null_value = TRUE on error .
@ retval
1 Available
@ retval
0 Already taken , or error
*/
longlong Item_func_is_free_lock : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
User_level_lock * ull ;
null_value = 0 ;
if ( ! res | | ! res - > length ( ) )
{
null_value = 1 ;
return 0 ;
}
mysql_mutex_lock ( & LOCK_user_locks ) ;
ull = ( User_level_lock * ) my_hash_search ( & hash_user_locks , ( uchar * ) res - > ptr ( ) ,
( size_t ) res - > length ( ) ) ;
mysql_mutex_unlock ( & LOCK_user_locks ) ;
if ( ! ull | | ! ull - > locked )
return 1 ;
return 0 ;
}
longlong Item_func_is_used_lock : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;
String * res = args [ 0 ] - > val_str ( & value ) ;
User_level_lock * ull ;
null_value = 1 ;
if ( ! res | | ! res - > length ( ) )
return 0 ;
mysql_mutex_lock ( & LOCK_user_locks ) ;
ull = ( User_level_lock * ) my_hash_search ( & hash_user_locks , ( uchar * ) res - > ptr ( ) ,
( size_t ) res - > length ( ) ) ;
mysql_mutex_unlock ( & LOCK_user_locks ) ;
if ( ! ull | | ! ull - > locked )
return 0 ;
null_value = 0 ;
return ull - > thread_id ;
}
longlong Item_func_row_count : : val_int ( )
{
DBUG_ASSERT ( fixed = = 1 ) ;