Commit 8227a34e authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

------------------------------------------------------------
revno: 2630.4.22
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w2
timestamp: Thu 2008-06-05 22:06:48 +0400
message:
  WL#3726 "DDL locking for all metadata objects"

  After review fixes in progress.

  Moved code checking that current lock request can be satisfied
  given the current state of individual or global metadata lock
  to separate well-documented functions.
parent 386b95df
...@@ -408,6 +408,188 @@ static void release_lock_object(MDL_LOCK *lock) ...@@ -408,6 +408,188 @@ static void release_lock_object(MDL_LOCK *lock)
} }
/**
Check if request for the lock on particular object can be satisfied given
current state of the global metadata lock.
@note In other words, we're trying to check that the individual lock
request, implying a form of lock on the global metadata, is
compatible with the current state of the global metadata lock.
@param lock_data Request for lock on an individual object, implying a
certain kind of global metadata lock.
@retval TRUE - Lock request can be satisfied
@retval FALSE - There is some conflicting lock
Here is a compatibility matrix defined by this function:
| | Satisfied or pending requests
| | for global metadata lock
----------------+-------------+--------------------------------------------
Type of request | Correspond. |
for indiv. lock | global lock | Active-S Pending-S Active-IS(**) Active-IX
----------------+-------------+--------------------------------------------
S | IS | + + + +
upgradable S | IX | - - + +
X | IX | - - + +
S upgraded to X | IX (*) | 0 + + +
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
"0" -- means impossible situation which will trigger assert
(*) Since for upgradable shared locks we always take intention exclusive
global lock at the same time when obtaining the shared lock, there
is no need to obtain such lock during the upgrade itself.
(**) Since intention shared global locks are compatible with all other
type of locks we don't even have any accounting for them.
*/
static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data)
{
switch (lock_data->type)
{
case MDL_SHARED:
if (lock_data->is_upgradable &&
(global_lock.active_shared || global_lock.waiting_shared))
{
/*
We are going to obtain intention exclusive global lock and
there is active or pending shared global lock. Have to wait.
*/
return FALSE;
}
else
return TRUE;
break;
case MDL_EXCLUSIVE:
if (lock_data->state == MDL_PENDING_UPGRADE)
{
/*
We are upgrading MDL_SHARED to MDL_EXCLUSIVE.
There should be no conflicting global locks since for each upgradable
shared lock we obtain intention exclusive global lock first.
*/
DBUG_ASSERT(global_lock.active_shared == 0 &&
global_lock.active_intention_exclusive);
return TRUE;
}
else
{
if (global_lock.active_shared || global_lock.waiting_shared)
{
/*
We are going to obtain intention exclusive global lock and
there is active or pending shared global lock.
*/
return FALSE;
}
else
return TRUE;
}
break;
default:
DBUG_ASSERT(0);
}
return FALSE;
}
/**
Check if request for the lock can be satisfied given current state of lock.
@param lock Lock.
@param lock_data Request for lock.
@retval TRUE Lock request can be satisfied
@retval FALSE There is some conflicting lock.
This function defines the following compatibility matrix for metadata locks:
| Satisfied or pending requests which we have in MDL_LOCK
----------------+---------------------------------------------------------
Current request | Active-S Pending-X Active-X Act-S-pend-upgrade-to-X
----------------+---------------------------------------------------------
S | + - - (*) -
High-prio S | + + - +
X | - + - -
S upgraded to X | - (**) + 0 0
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
"0" -- means impossible situation which will trigger assert
(*) Unless active exclusive lock belongs to the same context as shared
lock being requested.
(**) Unless all active shared locks belong to the same context as one
being upgraded.
*/
static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data)
{
switch (lock_data->type)
{
case MDL_SHARED:
if ((lock->active_exclusive.is_empty() &&
(lock_data->prio == MDL_HIGH_PRIO ||
lock->waiting_exclusive.is_empty() &&
lock->active_shared_waiting_upgrade.is_empty())) ||
(!lock->active_exclusive.is_empty() &&
lock->active_exclusive.head()->ctx == lock_data->ctx))
{
/*
When exclusive lock comes from the same context we can satisfy our
shared lock. This is required for CREATE TABLE ... SELECT ... and
ALTER VIEW ... AS ....
*/
return TRUE;
}
else
return FALSE;
break;
case MDL_EXCLUSIVE:
if (lock_data->state == MDL_PENDING_UPGRADE)
{
/* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. */
MDL_LOCK_DATA *conf_lock_data;
I_P_List_iterator<MDL_LOCK_DATA,
MDL_LOCK_DATA_lock> it(lock->active_shared);
/*
There should be no active exclusive locks since we own shared lock
on the object.
*/
DBUG_ASSERT(lock->active_exclusive.is_empty() &&
lock->active_shared_waiting_upgrade.head() == lock_data);
while ((conf_lock_data= it++))
{
/*
When upgrading shared lock to exclusive one we can have other shared
locks for the same object in the same context, e.g. in case when several
instances of TABLE are open.
*/
if (conf_lock_data->ctx != lock_data->ctx)
return FALSE;
}
return TRUE;
}
else
{
return (lock->active_exclusive.is_empty() &&
lock->active_shared_waiting_upgrade.is_empty() &&
lock->active_shared.is_empty());
}
break;
default:
DBUG_ASSERT(0);
}
return FALSE;
}
/** /**
Try to acquire one shared lock. Try to acquire one shared lock.
...@@ -449,8 +631,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) ...@@ -449,8 +631,7 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry)
pthread_mutex_lock(&LOCK_mdl); pthread_mutex_lock(&LOCK_mdl);
if (lock_data->is_upgradable && if (!can_grant_global_lock(lock_data))
(global_lock.active_shared || global_lock.waiting_shared))
{ {
pthread_mutex_unlock(&LOCK_mdl); pthread_mutex_unlock(&LOCK_mdl);
*retry= TRUE; *retry= TRUE;
...@@ -461,6 +642,11 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) ...@@ -461,6 +642,11 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry)
lock_data->key_length))) lock_data->key_length)))
{ {
lock= get_lock_object(); lock= get_lock_object();
/*
Before inserting MDL_LOCK object into hash we should add at least one
MDL_LOCK_DATA to its lists in order to provide key for this element.
Thus we can't merge two branches of the above if-statement.
*/
lock->active_shared.push_front(lock_data); lock->active_shared.push_front(lock_data);
lock->lock_data_count= 1; lock->lock_data_count= 1;
my_hash_insert(&mdl_locks, (uchar*)lock); my_hash_insert(&mdl_locks, (uchar*)lock);
...@@ -471,18 +657,8 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry) ...@@ -471,18 +657,8 @@ bool mdl_acquire_shared_lock(MDL_LOCK_DATA *lock_data, bool *retry)
} }
else else
{ {
if ((lock->active_exclusive.is_empty() && if (can_grant_lock(lock, lock_data))
(lock_data->prio == MDL_HIGH_PRIO ||
lock->waiting_exclusive.is_empty() &&
lock->active_shared_waiting_upgrade.is_empty())) ||
(!lock->active_exclusive.is_empty() &&
lock->active_exclusive.head()->ctx == lock_data->ctx))
{ {
/*
When exclusive lock comes from the same context we can satisfy our
shared lock. This is required for CREATE TABLE ... SELECT ... and
ALTER VIEW ... AS ....
*/
lock->active_shared.push_front(lock_data); lock->active_shared.push_front(lock_data);
lock->lock_data_count++; lock->lock_data_count++;
lock_data->state= MDL_ACQUIRED; lock_data->state= MDL_ACQUIRED;
...@@ -523,7 +699,7 @@ static void release_lock(MDL_LOCK_DATA *lock_data); ...@@ -523,7 +699,7 @@ static void release_lock(MDL_LOCK_DATA *lock_data);
bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context)
{ {
MDL_LOCK_DATA *lock_data, *conf_lock_data; MDL_LOCK_DATA *lock_data;
MDL_LOCK *lock; MDL_LOCK *lock;
bool signalled= FALSE; bool signalled= FALSE;
const char *old_msg; const char *old_msg;
...@@ -552,6 +728,10 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) ...@@ -552,6 +728,10 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context)
lock_data->key_length))) lock_data->key_length)))
{ {
lock= get_lock_object(); lock= get_lock_object();
/*
Again before inserting MDL_LOCK into hash provide key for
it by adding MDL_LOCK_DATA to one of its lists.
*/
lock->waiting_exclusive.push_front(lock_data); lock->waiting_exclusive.push_front(lock_data);
lock->lock_data_count= 1; lock->lock_data_count= 1;
my_hash_insert(&mdl_locks, (uchar*)lock); my_hash_insert(&mdl_locks, (uchar*)lock);
...@@ -572,30 +752,28 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) ...@@ -572,30 +752,28 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context)
{ {
lock= lock_data->lock; lock= lock_data->lock;
if (global_lock.active_shared || global_lock.waiting_shared) if (!can_grant_global_lock(lock_data))
{ {
/* /*
There is active or pending global shared lock we have There is an active or pending global shared lock so we have
to wait until it goes away. to wait until it goes away.
*/ */
signalled= TRUE; signalled= TRUE;
break; break;
} }
else if (!lock->active_exclusive.is_empty() || else if (!can_grant_lock(lock, lock_data))
!lock->active_shared_waiting_upgrade.is_empty())
{
/*
Exclusive MDL owner won't wait on table-level lock the same
applies to shared lock waiting upgrade (in this cases we already
have some table-level lock).
*/
signalled= TRUE;
break;
}
else if ((conf_lock_data= lock->active_shared.head()))
{ {
signalled= notify_thread_having_shared_lock(thd, MDL_LOCK_DATA *conf_lock_data;
conf_lock_data->ctx->thd); I_P_List_iterator<MDL_LOCK_DATA,
MDL_LOCK_DATA_lock> it(lock->active_shared);
signalled= !lock->active_exclusive.is_empty() ||
!lock->active_shared_waiting_upgrade.is_empty();
while ((conf_lock_data= it++))
signalled|=
notify_thread_having_shared_lock(thd, conf_lock_data->ctx->thd);
break; break;
} }
} }
...@@ -674,7 +852,6 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) ...@@ -674,7 +852,6 @@ bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context)
bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context,
MDL_LOCK_DATA *lock_data) MDL_LOCK_DATA *lock_data)
{ {
MDL_LOCK_DATA *conf_lock_data;
MDL_LOCK *lock; MDL_LOCK *lock;
const char *old_msg; const char *old_msg;
THD *thd= context->thd; THD *thd= context->thd;
...@@ -700,6 +877,8 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, ...@@ -700,6 +877,8 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context,
old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table"); old_msg= thd->enter_cond(&COND_mdl, &LOCK_mdl, "Waiting for table");
lock_data->state= MDL_PENDING_UPGRADE; lock_data->state= MDL_PENDING_UPGRADE;
/* Set type of lock request to the type at which we are aiming. */
lock_data->type= MDL_EXCLUSIVE;
lock->active_shared.remove(lock_data); lock->active_shared.remove(lock_data);
/* /*
There can be only one upgrader for this lock or we will have deadlock. There can be only one upgrader for this lock or we will have deadlock.
...@@ -711,43 +890,26 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, ...@@ -711,43 +890,26 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context,
lock->active_shared_waiting_upgrade.push_front(lock_data); lock->active_shared_waiting_upgrade.push_front(lock_data);
/* /*
There should be no conflicting global locks since for each upgradable Since we should have been already acquired intention exclusive global lock
shared lock we obtain intention exclusive global lock first. this call is only enforcing asserts.
*/ */
DBUG_ASSERT(global_lock.active_shared == 0 && DBUG_ASSERT(can_grant_global_lock(lock_data));
global_lock.active_intention_exclusive);
while (1) while (1)
{ {
if (can_grant_lock(lock, lock_data))
break;
bool signalled= FALSE; bool signalled= FALSE;
bool found_conflict= FALSE; MDL_LOCK_DATA *conf_lock_data;
I_P_List_iterator<MDL_LOCK_DATA, MDL_LOCK_DATA_lock> it(lock->active_shared); I_P_List_iterator<MDL_LOCK_DATA, MDL_LOCK_DATA_lock> it(lock->active_shared);
DBUG_PRINT("info", ("looking at conflicting locks"));
while ((conf_lock_data= it++)) while ((conf_lock_data= it++))
{ {
/*
We can have other shared locks for the same object in the same context,
e.g. in case when several instances of TABLE are open.
*/
if (conf_lock_data->ctx != context) if (conf_lock_data->ctx != context)
{
DBUG_PRINT("info", ("found active shared locks"));
found_conflict= TRUE;
signalled|= notify_thread_having_shared_lock(thd, signalled|= notify_thread_having_shared_lock(thd,
conf_lock_data->ctx->thd); conf_lock_data->ctx->thd);
} }
}
/*
There should be no active exclusive locks since we own shared lock
on the object.
*/
DBUG_ASSERT(lock->active_exclusive.is_empty());
if (!found_conflict)
break;
if (signalled) if (signalled)
pthread_cond_wait(&COND_mdl, &LOCK_mdl); pthread_cond_wait(&COND_mdl, &LOCK_mdl);
...@@ -767,6 +929,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, ...@@ -767,6 +929,7 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context,
if (thd->killed) if (thd->killed)
{ {
lock_data->state= MDL_ACQUIRED; lock_data->state= MDL_ACQUIRED;
lock_data->type= MDL_SHARED;
lock->active_shared_waiting_upgrade.remove(lock_data); lock->active_shared_waiting_upgrade.remove(lock_data);
lock->active_shared.push_front(lock_data); lock->active_shared.push_front(lock_data);
/* Pending requests for shared locks can be satisfied now. */ /* Pending requests for shared locks can be satisfied now. */
...@@ -778,7 +941,6 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, ...@@ -778,7 +941,6 @@ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context,
lock->active_shared_waiting_upgrade.remove(lock_data); lock->active_shared_waiting_upgrade.remove(lock_data);
lock->active_exclusive.push_front(lock_data); lock->active_exclusive.push_front(lock_data);
lock_data->type= MDL_EXCLUSIVE;
lock_data->state= MDL_ACQUIRED; lock_data->state= MDL_ACQUIRED;
if (lock->cached_object) if (lock->cached_object)
(*lock->cached_object_release_hook)(lock->cached_object); (*lock->cached_object_release_hook)(lock->cached_object);
...@@ -941,8 +1103,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) ...@@ -941,8 +1103,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context)
while ((lock_data= it++)) while ((lock_data= it++))
{ {
DBUG_ASSERT(lock_data->state == MDL_PENDING); DBUG_ASSERT(lock_data->state == MDL_PENDING);
if ((lock_data->is_upgradable || lock_data->type == MDL_EXCLUSIVE) && if (!can_grant_global_lock(lock_data))
(global_lock.active_shared || global_lock.waiting_shared))
break; break;
/* /*
To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock. To avoid starvation we don't wait if we have pending MDL_EXCLUSIVE lock.
...@@ -950,9 +1111,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context) ...@@ -950,9 +1111,7 @@ bool mdl_wait_for_locks(MDL_CONTEXT *context)
if (lock_data->type == MDL_SHARED && if (lock_data->type == MDL_SHARED &&
(lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key, (lock= (MDL_LOCK *)my_hash_search(&mdl_locks, (uchar*)lock_data->key,
lock_data->key_length)) && lock_data->key_length)) &&
!(lock->active_exclusive.is_empty() && !can_grant_lock(lock, lock_data))
lock->active_shared_waiting_upgrade.is_empty() &&
lock->waiting_exclusive.is_empty()))
break; break;
} }
if (!lock_data) if (!lock_data)
......
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